Question

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 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!

Was it helpful?

Solution

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.

OTHER TIPS

Move to a higher level and use 1 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 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top