Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog - Check if two lists have the same elements except one

I'm working with two lists of characters and I want to check if they have the same elements except one in the same position, like this:

compare([L1,L2,L3,L4],[W1,W2,W3,W4]) :-
((W1 \= L1, W2 = L2, W3 = L3, W4 = L4);
(W1 = L1, W2 \= L2, W3 = L3, W4 = L4);
(W1 = L1, W2 = L2, W3 \= L3, W4 = L4); 
(W1 = L1, W2 = L2, W3 = L3, W4 \= L4)).

This is working but there is a simplified way?

Thanks.

like image 548
KonaKona Avatar asked Nov 24 '25 10:11

KonaKona


2 Answers

Using if_/3 and (=)/3 we can make @Steven's code monotonic:

cmp_([X|Xs], [Y|Ys]) :-
   if_(X = Y, cmp_(Xs, Ys), Xs = Ys).
like image 94
repeat Avatar answered Nov 26 '25 00:11

repeat


You're right that even though your solution works, it isn't very clean nor re-usable as it only works for lists of length 4. Let's try to define a recursive predicate that works for lists of any size.

When looking at both lists one element at a time, there are actually only 2 cases to consider: either the elements are the same or they aren't.

If they are the same, that means that the rest of both lists must have exactly one different element in order to succeed. And that's exactly the predicate we're writing in the first place!

compare([H|T1], [H|T2]) :- compare(T1, T2).

Now for the second case. If the first elements of the list are different, then the rest of both lists must be exactly the same (as we have already encountered a different element

compare([H1|T1], [H2|T1]) :- H1 \= H2.

There, that's all! Now, you may notice output such as this:

?- compare([a,b], [a,c]).
true;
false.

This is because there is still a choice point open: for the first elements the first clause matches, but the second clause hasn't been considered yet. In this case though, we know that both clauses are mutually exclusive. So we can add a cut (!) to ensure that no choice point is left.

This also allows us to simplify the second close: if we reach this we know the first elements are not the same, so there is no need to check this again.

Putting it all together and the code becomes:

compare([H|T1], [H|T2]) :- !, compare(T1, T2).
compare([_|T], [_|T]).
like image 33
Steven Avatar answered Nov 25 '25 23:11

Steven