I have que next Prolog function that only multiply any number (F) you enter with every element in the list[], for example input: multiplyf(2, [5,4,8], N). output: [10,8,16]. This function works properly (you can try it) but now i need to filter only the even numbers to multyply F keeping the odd numbers the same, example before: input: multiplyf(2, [5,4,8], N). output: [5,8,16], Any idea how i could filter this list? I try with many ways but i didn't get the answer.
multiplyf(_,[],[]):-!.
multiplyf(F,[X|Xs], [Y|Ys]):-
Y is F*X,
multiplyf(F, Xs, Ys), !.
I try with:
even(X) :-
Y is mod(X,2),
Y =:= 0.
But when i inserted even(X) in the first function it doesn't work too.
Thank you.
CodePudding user response:
The other answer is OK in spirit but in the details it is a mess. Maybe @Nicholas Carey was just trying to provoke me ;-)
You can test for odd and even using a bitmask on the least significant bit of an integer.
odd(X) :- X /\ 0x1 =:= 1.
even(X) :- X /\ 0x1 =:= 0.
There are other ways to achieve the same but this is the one with least typing. Read the docs on is/2 to understand the pitfalls of writing 0 is something something
.
If you want to avoid cuts altogether you can use the following well-understood and documented approach that takes advantage of clause indexing on the first argument.
mf(Factor, Xs, Ys) :-
mf_1(Xs, Factor, Ys).
mf_1([], _, []).
mf_1([X|Xs], F, Ys) :-
LSB is X /\ 0x1,
mf_2(LSB, X, Xs, F, Ys).
mf_2(0 /* even */, X, Xs, F, [Y|Ys]) :-
Y is 2 * X,
mf_1(Xs, F, Ys).
mf_2(1 /* odd */, X, Xs, F, [X|Ys]) :-
mf_1(Xs, F, Ys).
This doesn't have cuts or ->
, doesn't leave choice points, and doesn't work with floats.
CodePudding user response:
To modify your multiply/3
:
multiplyf( _ , [] , [] ) :- ! .
multiplyf( F ,[X|Xs] , [Y|Ys] ) :-
Y is F * X,
multiplyf(F,Xs,Ys), !.
is straightforward enough:
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :- even(Y), Z is X * Y , multiply(X,Ys,Zs) .
multiply_evens( X , [Y|Ys] , [Y|Zs] ) :- odd(Y), multiply_evens(X,Ys,Zs) .
even(X) :- 0 is X mod 2 .
odd(X) :- 1 is X mod 2 .
This is also arguably the most declarative solution. Very little is left to doubt about intent.
You can condense it by using a cut (!
) to eliminate a choice point (after all, the number in question isn't going to cease to be positive/negative):
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :- even(Y), !, Z is X * Y , multiply(X,Ys,Zs) .
multiply_evens( X , [Y|Ys] , [Y|Zs] ) :- multiply_evens(X,Ys,Zs) .
even(X) :- 0 is X mod 2 .
You can condense it even further by using a soft cut:
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :-
( even(Y) -> Z is X * Y ; Z is X ),
multiply(X,Ys,Zs) .
even(X) :- 0 is X mod 2 .
And there's something to be said for extracting the multiplication bit from the list traversal itself:
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :- multiply_even(X,Y,Z), multiply_evens(X,Ys,Zs) .
multiple_even(X,Y,Z) :- even(Y), !, Z is X * Y .
multiply_even(_,Y,Y) .
even(X) :- 0 is X mod 2 .