I'm new to prolog and i can't find a solution to filter list by 2 conditions, save result into 2 variables and then evaluate answer base on them.
In my case, if list contains more numbers that greater then 0, then numbers lesser then 0.
For example i get a list with elements:
checklist([-2, 3.3, 12, -10.1, 14, -11, 123]) # true
checklist([-2.1, -3, -4, 14, 16.7]) # false
checklist([11.5, 2.5, -34.1, -1]) # false
I'd write something like this in python:
bigger_count = 0
lesser_count = 0
for num in list:
if num > 0:
bigger_count = 1
elif num < 0:
lesser_count = 1
print(bigger_count > lesser_count)
Especially i doesn't understand how to work with 2 variables at one time.
CodePudding user response:
The easiest way is to use a worker predicate that carries the required extra state.
checklist( Ns, P, N ) :- checklist(Ns, 0, 0, P, N )
checklist( [] , P , N , P , N ) .
checklist( [X|Xs] , P0 , N0 , P , N ) :- X > 0, P1 is P0 1, checklist(Xs,P1,N0,P,N).
checklist( [X|Xs] , P0 , N0 , P , N ) :- X < 0, B1 is N0 1, checklist(Xs,P0,N1,P,N).
CodePudding user response:
Another possible solution is:
check(List) :-
check(List, Balance),
Balance > 0.
check([], 0).
check([X|Xs], B) :-
check(Xs, B0),
( X > 0 -> B is B0 1
; X < 0 -> B is B0 - 1
; B is B0 ).
Examples:
?- check([-2, 3.3, 12, -10.1, 14, -11, 123]).
true.
?- check([-2.1, -3, -4, 14, 16.7]).
false.
?- check([11.5, 2.5, -34.1, -1]).
false.
?- check([1,-1]).
false.
CodePudding user response:
If you add up all the values in the list, you'll get a positive or negative result which varies with how big the numbers are, instead of how many there are. There is a mathematical function sign
or signum
which turns all negative values into -1, and all positive values into 1, then you can add them up and if there were more positive inputs, the answer is positive, if there are more negative inputs, the answer is negative. Or they will cancel out and the sum will be 0.
If you maplist
sign
that over the inputs:
?- maplist(sign, [-2, 3.3, 12, -10.1, 14, -11, 123], X).
X = [-1, 1.0, 1, -1.0, 1, -1, 1]
Then:
?- sum_list(X, S).
S = 1.0
a positive result, so more positives than negatives in this case.
CodePudding user response:
you could partition/4
the input list into the smaller vs larger values:
?- partition(>(0), [-2, 3.3, 12, -10.1, 14, -11, 123], Lesser, Greater).
Lesser = [-2, -10.1, -11],
Greater = [3.3, 12, 14, 123]
Then use length/2
and compare the result to see which one is longer.
(This might not be useful if any of the inputs are 0
, they are not in your examples. I think this will use more memory to make a copy of the list.).
CodePudding user response:
https://www.swi-prolog.org/pldoc/doc_for?object=include/3
https://www.swi-prolog.org/pldoc/doc_for?object=convlist/3
If you have 2 conditions, you need to traverse the list twice, but is the complexity of code that can handle two different predicates worth the trouble?
Anyway, if you're writing from scratch, you can simplify your life by using DCGs:
filter(_Pred, []) --> [].
filter(Pred, [X|Xs]) -->
( { call(Pred, X) }
-> [X]
; []
),
filter(Pred, Xs).
filter(Pred, List, Result) :-
phrase(filter(Pred, List), Result).
even(X) :- 0 is X mod 2.
?- filter(even, [1,2,3,4,5], X).
X = [2, 4]