1

I want to write a function that returns true if two lists are exactly the same(order of elements matters).

I tried it this way:

same([ ], [ ]).   
same([H1|R1], [H2|R2]):-
    H1 == H2,
    same(R1, R2).

It returns true while two lists are the same, also I expect if I have

?- same(X, [1, 2, 3]).

I want it to return

X = [1, 2, 3].

But it doesn't work if input is like this. Here are some sample outputs I got:

?- same([1, 2], [1, 2]).

true.

?- same([2, 1], [1, 2]).

false.

?- same(X, [1, 2, 3]).

false.

?- same([1, 2, 3], [1, 2, X]).

false.

How to fix it?

false
  • 10,264
  • 13
  • 101
  • 209
MicM
  • 123
  • 1
  • 2
  • 13

1 Answers1

5

The problem is that you're using ==/2 (checking whether two items are instantiated the same) rather than =/2 (checks if two items are unified or unifiable). Just change to unification:

same([], []).

same([H1|R1], [H2|R2]):-
    H1 = H2,
    same(R1, R2).

Then this will have the behavior you're looking for:

| ?- same(X, [1, 2, 3]).

X = [1,2,3] ? a

no
| ?- same([1, 2], [1, 2]).

(1 ms) yes
| ?- same([2, 1], [1, 2]).

no
| ?- same([1, 2, 3], [1, 2, X]).

X = 3

(1 ms) yes
| ?- same([A,B,C], L).

L = [A,B,C]

yes
% In this last example, A, B, and C are variables. So it says L is [A,B,C],
% whatever A, B, and C are.

If you query X == 3 in Prolog, and X is not bound to the value 3, or it is just unbound, it will fail. If X is unbound and you query, X = 3, then Prolog will unify X (bind it) with 3 and it will succeed.

For more regarding the difference between =/2 and ==/2, see What is the difference between == and = in Prolog?

You can also use maplist for a nice, compact solution. maplist is very handy for iterating through a list:

same(L1, L2) :- maplist(=, L1, L2).

Here, unification (=/2) is still used for exactly the same reason as above.


Finally, as @Boris points out, in Prolog, the unification predicate will work on entire lists. In this case, this would suffice:
same(L1, L2) :- L1 = L2.

Or equivalently:

same(L, L).  % Would unify L1 and L2 queried as same(L1, L2)

This will succeed if the lists are the same, or will attempt to unify them by unifying each element in turn.

| ?- same([1,2,X], [1,2,3]).    % Or just [1,2,X] = [1,2,3]

X = 3

yes
| ?- same([1,2,X], [1,2,3,4]).  % Or just [1,2,X] = [1,2,3,4]

no

The prior more elaborate approaches are considered an exercise in list processing for illustration. But the simplest and most correct method for comparison and/or unification of lists would be L1 = L2.

Community
  • 1
  • 1
lurker
  • 56,987
  • 9
  • 69
  • 103
  • This answer is strange (as is the original question). Why are you not directly unifying the two lists? –  Mar 21 '14 at 10:54
  • @Boris the question isn't strange if you consider that it is an exercise in list processing. But you're right about the answer. I lost sight of the simplest approach as I provided an answer following that of illustrating list processing. Thanks for pointing it out. – lurker Mar 21 '14 at 11:10
  • 1
    And actually, same(X, X). is enough, but this is infact =(X, X). –  Mar 21 '14 at 11:22
  • how would you do this if you didn't care about order? – hiswendy May 22 '18 at 17:27
  • @hiswendy the simplest thing to do would be to use `msort` to put them in order and not remove duplicates, then just attempt to unify them afterwards. So, `same(L1, L2) :- msort(L1, S1), msort(L2, S2), S1 = S2.` A longer way, but less imperative, would be to go element by element and use `select/3`. – lurker May 22 '18 at 17:40