Home > Software engineering >  Prolog - Filter list with only even numbers
Prolog - Filter list with only even numbers

Time:10-25

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 .
  • Related