2

I am writing the following code and is giving perfect results.

edge(s,a,300).
edge(s,d,20).
edge(a,d,400).
edge(a,b,1500).
edge(b,c,9).
edge(b,e,200).
edge(c,d,2000).
edge(c,g,12).
edge(d,e,3).
edge(e,f,400).
edge(f,g,800).

connected(X,Y,D) :- edge(X,Y,D) ; edge(Y,X,D).

path(A,B,D,Path) :- 
    travel(A,B,D,[A],Q),
    reverse(Q,Path).

travel(A,B,D,P,[B|P]) :-
    connected(A,B,D).

travel(A,B,D,Visited,Path) :-
    connected(A,X,D1),
    X \== B,
    \+member(X,Visited),
    D2 is D - D1,
    travel(X,B,D2,[X|Visited],Path).

Here if I query like

| ?- path(s,e,23,P).
P = [s,d,e] ? ;
no
| ?-

I do get the correct response. But no I wish to get the results for a D<50, say. How to do?

false
  • 10,264
  • 13
  • 101
  • 209
Vipul
  • 195
  • 1
  • 4
  • 15
  • instead of using something complex like CLP you should simply correct your code to change D from input to output parameter. – CapelliC Nov 06 '12 at 16:11
  • See also [a similar problem](http://stackoverflow.com/a/13126730/772868). – false Nov 07 '12 at 10:09

3 Answers3

4

Arithmetic expressions are kind of a special case in Prolog, and not expected to work with unbound arguments, so you cannot simply ask for path(s,e,D,P), D < 50, because the is-clause needs all its right-hand side arguments instantiated.

You can use the finite domain (FD) constraint extensions for Prolog (e.g. in GNU Prolog): just change your is to the FD-equivalent #=, and ask:

/Users/stolz/test.pl compiled, 27 lines read - 3180 bytes written, 8 ms
| ?- fd_domain(D,0,50), path(s,e,D,P).

D = 23
P = [s,d,e] ? ;

no
Volker Stolz
  • 7,274
  • 1
  • 32
  • 50
  • at least, given the range exposed in edge/3, use a reasonable value as upper bound, **not** 50. And really you don't see a more simple way to solve the problem without CLP? – CapelliC Nov 06 '12 at 17:37
  • 2
    @chac of course there are other solutions, but I wouldn't pass the opportunity to advocate the use of constraints. Tongue in cheek: actually I expect his teacher to tell him off for using constraints ;) – Volker Stolz Nov 06 '12 at 17:50
2

You should let Prolog compute the distance D as well as the Path:

travel(A,B,D,Visited,Path) :-
    connected(A,X,D1),
    X \== B,
    \+member(X,Visited),
    travel(X,B,D2,[X|Visited],Path),
    D is D2 + D1.

Then you can query it

?- path(Start, Stop, D, P), D < 50.

and will get (on backtracking) all paths with D < 50.

?- path(s,e,X,P),X<50.
X = 23,
P = [s, d, e] ;
false.
CapelliC
  • 59,646
  • 5
  • 47
  • 90
  • I think @gusbro's solution seems slightly more elegant since it cuts off earlier and avoid finding too long paths. On the other hand, it goes into the same direction as the CLP-solution. – Volker Stolz Nov 06 '12 at 18:11
  • yes, it give me `ERROR: is/2: Arguments are not sufficiently instantiated` – CapelliC Nov 06 '12 at 18:35
  • I have another question. What if I want all the possible paths (P) from s,e and passing through say a,b. – Vipul Nov 09 '12 at 02:50
  • you get all possible paths using backtracking or findall, to see if a path contains a node use memberchk/2. Try `?- findall(P-D,(path(s,e,D,P),memberchk(b,P)),L), writeln(L).` – CapelliC Nov 09 '12 at 08:13
1

Although I think finite domain constraints are the best to use here, you can achieve what you want with a minor modification to your code.

In the first clause of travel/5 instead of unifying input argument D with the third argument of connected/3, you can use a fresh variable and then check to see whether your current distance (D) is larger or equal to this new variable.

travel(A,B,D,P,[B|P]) :-
    connected(A,B,TD),
    D >= TD.
gusbro
  • 22,357
  • 35
  • 46
  • What do you mean that it does not seem to work that way ?. For your example ( `path(s,e,5000,P).` ) it will, upon backtracking, return all the results from `s` to `e` with distance less than or equal to 5000. – gusbro Nov 06 '12 at 18:49