问题
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.
回答1:
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
| ?-
回答2:
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.
回答3:
In this answer we try to keep it simple—by building on append/3
:
adjacent(E0, E1, Es) :- append(_, [E0,E1|_], Es).
Sample query:
?- adjacent(X, Y, [a,b,c,d,e]).
X = a, Y = b ;
X = b, Y = c ;
X = c, Y = d ;
X = d, Y = e ;
false.
回答4:
The auxiliary predicate adjacent_/5
always "lags behind" by exactly two (list items):
adjacent(X0, X1, [E0,E1|Es]) :- adjacent_(Es, E0, E1, X0, X1). adjacent_([], E0, E1, E0, E1). adjacent_([E2|Es], E0, E1, X0, X1) :- if_(E0+E1 = X0+X1, true, adjacent_(Es, E1, E2, X0, X1)).
Using SWI-Prolog we run:
?- set_prolog_flag(double_quotes, chars). true. ?- adjacent(a, b, "abab"). true. ?- adjacent(b, c, "abcd"). true. ?- adjacent(X, Y, "abcd"). X = a, Y = b ; X = b, Y = c ; X = c, Y = d.
Edit
The corrected definition of adjacent_/5
gives right answers for the following queries, too:
?- adjacent(X, X, [A,B,C]). X = A, A = B ; X = B, B = C, dif(f(C,C),f(A,A)). ?- adjacent(a, X, "aab"). X = a ; X = b. ?- adjacent(a, b, "aab"). true.
回答5:
Here is a definition that I believe is in the long term preferable to @repeat's solution:
adjacent(X0, X1, [E0,E1|Es]) :-
adjacent_(Es, E0, E1, X0, X1).
adjacent_([], E0, E1, E0, E1).
adjacent_([E2|Es], E0, E1, X0, X1) :-
if_(( E0 = X0, E1 = X1 ),
true,
adjacent_(Es, E1, E2, X0, X1)).
Using a reified and:
','(A_1, B_1, T) :-
if_(A_1, call(B_1, T), T = false).
;(A_1, B_1, T) :-
if_(A_1, T = true, call(B_1, T)).
来源:https://stackoverflow.com/questions/35667142/prolog-finding-adjacent-elements-in-a-list