Leonardo R.R.
Leonardo R.R.

Reputation: 45

PROLOG List filter predicate not aswering true or false

I'm trying to make a predicate that takes two vectors/lists and uses the first one as a filter. For example:

?- L1=[0,3,0,5,0,0,0,0],L2=[1,2,3,4,5,6,7,8],filter(L1,L2,1).
   L1 = [0, 3, 0, 5, 0, 0, 0, 0],
   L2 = [1, 2, 3, 4, 5, 6, 7, 8] .

That's what I'm getting but I would want true or false if L2 has 3 as the second element, 5 as the fourth element, etc. The 0s are ignored, that's the "filter" condition. What I know from the input is that L1 and L2 are always length=8 and only L1 has 0s.

My code is:

filter(_,_,9).
filter([Y|T],V2,Row):-
    Y=:=0,
    NewRow is Row + 1,
    filter([Y|T],V2,NewRow).

filter([Y|T],V2,Row):-
    Y=\=0,
    nth(Row,[Y|T],X1),
    nth(Row,V2,X2),
    X1=:=X2,
    NewRow is Row + 1,
    filter([Y|T],V2,NewRow).


nth(1,[X|_],X).  
nth(N,[_|T],R):- M is N-1, nth(M,T,R).

I know there are better ways of doing the function, for example comparing the first element of the first to the nth of the second and delete the head of the first with recursion but I just want to know why I'm not getting true or false, or any "return" value at all.

Can someone help me?, got it working

New code:

filter([],R,_,R).
filter([Y|T],V2,Row,R):-
Y=:=0,
NewRow is Row + 1,
filter(T,V2,NewRow,R).

filter([Y|T],V2,Row,R):-
Y=\=0,
nth(Row,V2,X2),
Y=:=X2,
NewRow is Row + 1,
filter(T,V2,NewRow,R).

Example of expected behaviour:

permutation([1,2,3,4,5,6,7,8],X),filter([1,2,3,4,0,0,0,0],X,1,R).
X = R, R = [1, 2, 3, 4, 5, 6, 7, 8] ;
X = R, R = [1, 2, 3, 4, 5, 6, 8, 7] ;
X = R, R = [1, 2, 3, 4, 5, 7, 6, 8] ;
X = R, R = [1, 2, 3, 4, 5, 7, 8, 6] .

Now i can get all the permutations that starts with 1,2,3,4. If someone knows a better way to achieve the same, plz share, but i already got what i needed =).

Upvotes: 2

Views: 307

Answers (2)

CapelliC
CapelliC

Reputation: 60034

seems like could be a perfect task for maplist/3

filter(L1, L2, _) :-
  maplist(skip_or_match, L1, L2).

skip_or_match(E1, E2) :- E1 == 0 ; E1 == E2.

yields

?- permutation([1,2,3,4,5,6,7,8],X),filter([1,2,3,4,0,0,0,0],X,_).
X = [1, 2, 3, 4, 5, 6, 7, 8] ;
X = [1, 2, 3, 4, 5, 6, 8, 7] ;
X = [1, 2, 3, 4, 5, 7, 6, 8] ;
X = [1, 2, 3, 4, 5, 7, 8, 6] ;
...

We could do that more useful, using Prolog facilities - namely, use an anonymus variable to express don't care.

Then filter/N is a simple application of maplist:

?- permutation([1,2,3,4,5,6,7,8],X),maplist(=,[1,2,3,4,_,_,_,_],X).
X = [1, 2, 3, 4, 5, 6, 7, 8] ;
X = [1, 2, 3, 4, 5, 6, 8, 7] ;
X = [1, 2, 3, 4, 5, 7, 6, 8] ;
X = [1, 2, 3, 4, 5, 7, 8, 6] ;
...

Upvotes: 3

liori
liori

Reputation: 42337

Your code always tests the first item of the filtering list for being zero. For example, look at the case when you're checking second value:

filter([0,3,0,5,0,0,0,0], [1,2,3,4,5,6,7,8], 2).

This call will perform the following unifications:

# first case: obvious fail…
filter([0,3,0,5,0,0,0,0], [1,2,3,4,5,6,7,8], 2) =\= filter(_, _, 9).

# second case:
filter([0,3,0,5,0,0,0,0], [1,2,3,4,5,6,7,8], 2) = filter([Y|T],V2,Row).
# unification succeeds with substitutions:
    Y = 0
    T = [3,0,5,0,0,0,0]
    V2 = [1,2,3,4,5,6,7,8]
    Row = 2
# and what happens next?
    Y =:= 0 # success!

You probably wanted here to check whether second element of [Y|T] is zero; instead, you're checking the first one. If you want to fix it without changing the rest of your code, you should instead perform comparisons to X1:

filter(V1,V2,Row):-
    nth(Row, V1, X1),
    X1 =:= 0,
    NewRow is Row + 1,
    filter(V1,V2,NewRow).

filter(V1,V2,Row):-
    nth(Row,V1,X1),
    X1=\=0,
    nth(Row,V2,X2),
    X1=:=X2,
    NewRow is Row + 1,
    filter(V1,V2,NewRow).

Also, there's one more thing that I think you might not be getting yet in Prolog. If a predicate fails, Prolog indeed prints false and stops computation. But if a predicate succeeds, there are two cases:

  • If there were no variables in your query, Prolog prints true.
  • If there were any variables in your query, Prolog does not print true. Instead, it prints values of variables instead. This also counts as true.

In your case Prolog actually “returns” true from your predicate—except that because you have used variables in your query, it printed their value instead of printing true.

Upvotes: 2

Related Questions