Prolog: handling cycles in graph traversal

2019-08-11 05:20发布

问题:

road(london, paris, 135).
road(paris, london, 135).
road(paris, madrid, 250).
road(madrid, paris, 250).
road(madrid, barcelona, 70).
road(barcelona, madrid, 70).

route(X, Y, [X|Y], N) :-
   road(X, Y, N).
route(X, Y, [X|T], N) :- 
   road(X, Z, A),
   route(Z, Y, [_|T], B),
   N is A + B.

This is a sample code of the problem I am facing. My test input is

?- route(london, barcelona, R, 455).

This input will keep re-iterating through london-paris and paris-london, however I have noticed that it will find the route from london-barcelona if I remove, the cycle paris-london.

My question is if there is any way I can implement a predicate which will allow me to ignore a cycle.

回答1:

You can do these modifications:

road(london, paris, 135).
road(paris, madrid, 250).
road(madrid, paris, 250). 
road(madrid, barcelona, 70).
road(barcelona, madrid, 70).

path(From, To, List, N) :-
   route(From, To, [From], N).

route(X, Y, Passed_city, N) :-
   road(X, Y, N).
route(X, Y, Passed_city, N) :- 
   road(X, Z, A),
   \+ member(Z, Passed_city),
   route(Z, Y, [Z|Passed_city], B),
   N is A + B.

and call the query

?- path(london, barcelona, R, 455).

What I did is to create a new rule for path/4 to insert the first city From in the list which contains all the cities you passed, like so: route(From, To, [From], N).

Then I've inserted the goal \+ member(Z, Passed_city) in the body of the second rule.

\+ means "not provable", so \+ member(Z, Passed_city) is true if member(Z, Passed_city) fails, that is, if Z is not in Passed_city.



回答2:

Now work..

road(london, paris, 135).
road(paris, madrid, 250).
road(madrid, paris, 250). 
road(madrid, barcelona, 70).
road(barcelona, madrid, 70).

path(From, To, List, N) :-
   route(From, To, [From], N).

route(X, Y, Passed_city, N) :-
   road(X, Y, N), Passed_city = [Passed_city|Y].
route(X, Y, Passed_city, N) :- 
   road(X, Z, A),
   \+ member(Z, Passed_city),
   route(Z, Y, [Z|Passed_city], B),
   N is A + B.