Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog - Finding adjacent elements in a list

Tags:

I'm trying to define a predicate adjacent(X, Y, Zs) that is true if X and Y are adjacent in a list. My code is currently this:

adjacent(_, _, []). adjacent(X, Y, [X, Y|Tail]) :-   adjacent(X,Y, Tail). 

It works for the basic case of adjacent(c, d, [a, b, c, d, e]), but due to the base case, every other case returns true as well, and I'm stuck on that.

The other problem is that if X is not equal to the first part of the list's head, then it skips past both X and Y and goes to the next 'X'; e.g., if c isn't equal to a, then it skips past both a and b and checks if c is equal to c. This is problematic when, for example, the list is

[a, c, d, e] 

because it ends up never checking c (I believe).

I'm pretty lost on how to reconcile the two issues and turn my logical understanding of what needs to occur into code.

EDIT: Thanks to Christian Hujer's answer, my base case mistake has been corrected, so now I'm just stuck on the second issue.

like image 951
quantumferret Avatar asked Feb 27 '16 07:02

quantumferret


People also ask

How do you get the second element in a list in Prolog?

You can move unification into the head of the clause and simply write: second([_, Second| _], Second). The notation for lists is to write the initial elements separated by commas and then a vertical bar to separate the list tail, i.e. the list containing the rest of the elements.

How do you check if a list contains a value in Prolog?

If you want to check if a list contains a member, use memberchk/2 . so memberchk(I/J, Item) succeeds if I/J is in the list Item . Your include/3 predicate has no base case, and attempt to ensure that every element of the given list is I/J , so it will always fail.

How do you get the head and tail of a list in Prolog?

In Prolog list elements are enclosed by brackets and separated by commas. Another way to represent a list is to use the head/tail notation [H|T]. Here the head of the list, H, is separated from the tail of the list, T, by a vertical bar. The tail of a list is the original list with its first element removed.

How do you select an element in a list in Prolog?

This predicate can be used to select an element from a list, delete an element or insert it. The definition of this Prolog library predicate is: select(A, [A|B], B). select(A, [B, C|D], [B|E]) :- select(A, [C|D], E).


2 Answers

In the original solution attempt:

adjacent(_, _, []). adjacent(X, Y, [X, Y|Tail]) :-     adjacent(X,Y, Tail). 

As @ChristianHujer points out, the first clause should not be there because it isn't true. The empty list should have no adjacent elements.

The second clause is also problematic. It shows that X and Y are adjacent in the list, but then recurses and doesn't just succeed. A proper clause should be:

adjacent(X, Y, [X,Y|_]). 

Which says that X and Y are adjacent in the list if they're the first two elements in the list, regardless of what the tail is. This also forms a proper base case. Then your general, recursive clause should take care of the rest of the cases:

adjacent(X, Y, [_|Tail]) :-     adjacent(X, Y, Tail). 

This says that X and Y are adjacent in [_|Tail] if they're adjacent in Tail. This takes care of the second problem you were encountering.

Thus, the whole solution would be:

adjacent(X, Y, [X,Y|_]). adjacent(X, Y, [_|Tail]) :-     adjacent(X, Y, Tail). 

This will succeed as many times as X and Y appear together, in that order, in the list.


This is also naturally solvable with a DCG (although @repeat's append/3 based solution is more concise):
adjacent(X, Y) --> ..., [X, Y], ... . ... --> [] | [_], ... .  adjacent(X, Y, L) :- phrase(adjacent(X, Y), L). 

| ?- adjacent(b, c, [a,b,c,d]).  true ? a  (1 ms) no | ?-  
like image 120
lurker Avatar answered Oct 09 '22 10:10

lurker


I think your base case is wrong. In your situation, you want recursion to terminate with a false predicate, not with a true predicate. And it's logical: In an empty list, there are no adjacent elements. Never.

like image 30
Christian Hujer Avatar answered Oct 09 '22 10:10

Christian Hujer