Traversing a graph (with possible loops) and returning the path in Prolog
I'm given a list of arcs:
arc(a,b).
arc(b,c).
arc(c,d).
arc(d,b).
arc(d,e).
arc(e,e).
arc(e,f).
I've written a set of clauses which will tell me if there's a path from node X
to node Y
. Loops may occur and I've accounted for that.
path(X,Y) :-
arc(X,Y).
path(X,Y) :-
arc(X,Z),
path(Z,Y,[X]).
path(X,Y,P) :-
arc(X,Y).
path(X,Y,P) :-
\+ member(X,P),
arc(X,Z),
append([X],P,L),
path(Z,Y,L).
I need to modify this to, on success, return a list of nodes that were traversed. I'm unclear as to how I would do this.
I assume my base case开发者_StackOverflow would be something like path2(X,Y,[X,Y]) :- arc(X,Y).
but that won't work with my program. Is there something wrong with my solution for the previous part, or am I just missing a small modification? Any help would be appreciated. Thanks!
Close... but consider:
path(Start, End, Path) :-
path(Start, End, [], Path).
Calling path/3
with a start and end node will construct the path between them, if it exists, and backtrack to give you alternate routes. No nodes are visited twice. The []
is a node accumulator list so we can check we're not going in circles as the path is built.
path(Now, End, Acc, Path) :-
arc(Now, Mid),
Mid == End, !,
append(Acc, [Now, End], Path).
path(Now, End, Acc, Path) :-
arc(Now, Mid),
\+ member(Mid, Acc),
path(Mid, End, [Now|Acc], Path).
These predicates do the actual work. The first one is the base-case, where the end node is reached via another call to arc/2
; in this case, a path has been built. The second one traverses the (directed) graph, but only to nodes that haven't been visited yet.
All paths can be located at once using findall/3
via:
findall(Path, path(Start,End,Path), Paths).
For bound values of Start
and End
to node atoms.
Move to a higher level and use transitive-closure1 as the basic idiom!
% your binary predicate arc/2 gets used here % | % v ?- path(arc, Path, From, To). From = To , Path = [To] ; From = a, To = b, Path = [a,b] ; From = a, To = c, Path = [a,b,c] ; From = a, To = d, Path = [a,b,c,d] ; From = a, To = e, Path = [a,b,c,d,e] ; From = a, To = f, Path = [a,b,c,d,e,f] ; From = b, To = c, Path = [b,c] ; From = b, To = d, Path = [b,c,d] ; From = b, To = e, Path = [b,c,d,e] ; From = b, To = f, Path = [b,c,d,e,f] ; From = c, To = d, Path = [c,d] ; From = c, To = b, Path = [c,d,b] ; From = c, To = e, Path = [c,d,e] ; From = c, To = f, Path = [c,d,e,f] ; From = d, To = b, Path = [d,b] ; From = d, To = c, Path = [d,b,c] ; From = d, To = e, Path = [d,e] ; From = d, To = f, Path = [d,e,f] ; From = e, To = f, Path = [e,f] ; false.
Footnote 1: As implemented by the versatile meta-predicate path/4
.
The answer given by sharky doesn't seem to work for me, but the following mod does and feels more straightforward to me. :
path(Now, End, Acc, [Now,End]) :-
arc(Now, End),
\+ member(Now, Acc),
\+ member(End, Acc).
path(Now, End, Acc, Path) :-
arc(Now, Mid),
\+ member(Mid, Acc),
path(Mid, End, [Now|Acc], Path).
Do tell me if I'm missing something.
精彩评论