How can I find all the connected cities on graph with Prolog? - prolog

Program should check if there is a direct route between two given cities. Alternatively, it can list all the connected cities for a given city.
My Solution is:
I keep a list of cities I visit. If the next city is not the city I came from and the name of the next city is not on the list, I let you press the screen.
My code is:
% knowledge base
path(newyork,losangeles).
path(losangeles,newyork).
path(losangeles,chicago).
path(chicago,losangeles).
path(chicago,houston).
path(houston,chicago).
path(houston,newyork).
path(newyork,houston).
% rules
route(X,Y):-myroute(X,X,Y,[]).
myroute(X,Y,Z,_L):- path(Y,Z), not(X = Z).
myroute(X,Y,A,L):- path(Y,A), not(X = A) , not(member(A,L)),
append(X,L,T) , myroute(Y,A,_Q,T).
Output:
?- route(newyork,Y).
Y = losangeles ;
Y = houston ;
false
Expected Output:
?- route(newyork,Y).
Y = losangeles ;
Y = chicago ;
Y = houston ;
false
My code is check if there is a direct route between two given cities. It can't list all the connected cities for a given city. Where am I making mistakes?

Let's follow the logic of your code:
?- route(newyork, Y).
Prolog tries to unify this with what it knows from the database. It finds this definition:
route(X,Y):-myroute(X,X,Y,[]).
Therefore it unifies X with newyork and proceeds to check if
myroute(newyork, newyork, Y, []) is true, as per the definition of route.
Prolog finds a match for that term in it database, with the first definition of the myroute predicate, and proceeds to unify X, Y and _L in that definition as newyork, newyork and [], i.e. it checks to see if myroute(newyork, newyork, Y, []) can be satisfied given the definition for myroute.
According to the first definition of myroute, prolog checks to see if it can find a fact in its database that will satisfy path(newyork, Z), such that Z is not newyork. There are two facts that can satisfy this, for Z=losangeles and for Z=houston. Note that there is no path(newyork, chicago) fact in the database, therefore this does not qualify as an answer given your current definitions.
Note also that if the first myroute definition fails, then so will the second one, since it checks for exactly the same things first! Therefore there will be no other solutions found via backtracking in this instance.
What you should be doing instead
I won't solve your exercise for you (especially since you didn't ask me to :p ) but in general, your myroute predicate is probably expected to work as follows:
myroute(StartPoint, EndPoint, RouteSoFar) :- % your definition here
and called from route like so:
route(X, Y) :- myroute(X, Y, []).
RouteSoFar is an accumulator; in the first call we call it with [] because we haven't visited any nodes, but in subsequent calls to myroute, it will be a list that you add to before calling the predicate again recursively. Somewhere in your predicate definition you'll have to check that the node you're about to visit isn't in the list of visited nodes already.
The logic of myroute is this: "There exists a myroute from X to Z if there is a simple path from X to Z (base case), OR if there is a path from X to an intermediate UNVISITED node Y, and then a myroute from that Y to Z (recursive case)".

Related

Prolog : return variable, check if variable is of certain type?

I am new to Prolog and can't understand predicates very well.
First question: How can I 'return' a certain variable?
We have alternate(?A, ?B). alternate(first, second) should give me back second, and alternate(second, first) should give back first.
Second question: How to check if variable is of certain type?
I have for example ispair(?Pair). I have to check if Pair is pos(X,Y).
Not sure if that's what you meant, but what about the following:
alternate(first, pair(X,_), X).
alternate(second, pair(_,X), X).
If you query without any restrictions, you get the following two answer substitutions:
?- alternate(X,Y,Z).
X = first,
Y = pair(Z, _5844) ; % hit ; to get the second answer
X = second,
Y = pair(_5842, Z). % variables _12345 are fresh ones created by prolog
You can also ask: on which side of the pair (a,b) is b?
?- alternate(Where, pair(a,b), b).
Where = second.
In the case that your pair is (b,b), you get two solutions:
?- alternate(Where, pair(b,b), b).
Where = first ;
Where = second.
Also, c is not part of the pair (a,b):
?- alternate(Where, pair(a,b), c).
false.
If you insist on picking an element from heaven, you will get no as answer:
?- alternate(heaven, X, Y).
false.
When you know that the first element of a pair is a, prolog will tell you how the pair must look like:
?- alternate(first, X, a).
X = pair(a, _5680).
Again we have a fresh variable (_5680) in there, because any second term is fine.

Prolog predicate that extracts all words immediately following a word in a given list of words

As it says in the title, i need to get all the words after a specifc word in prolog, for example:
?- find([in, house, car, in, shop, no, more, apples, in, table], in , X).
X = [house, shop, table] ;
No
This is the code i've written so far:
find([H,H_1|_],H,[H_1]).
find([Head,Head_1|Tail], Term, [Head|Result]) :-
find(Tail, Term, Result).
After i run it, i get:
X = [house] ;
X = [in, car, shop, more, table] ;
No
There is nothing better than writing simple programs to learn a language. After you grasp the basics, you could be interested into more idiomatic approach:
find(L,W,Fs) :- findall(F, append(_,[W,F|_],L), Fs).
The main problem is probably located here:
find([H,H_1|_],H,[H_1]).
This code unifies the list with the first element after the match. You then unify the third parameter (which is here used as a "result") with a list containing the single occurrence.
Note furthermore that it is also possible that we reached the end of the list. So in that case the predicate will fail as well.
Basically there are four cases here:
we reach the end of the list, the "result" parameter should unify with the empty list;
we found the element and there is a next element (that is also a match), we perform one step and continue our search;
we found the element and there is a next element (that is not a match), we add that element and continue our search;
the head does not match, we continue our search.
We can implement these possibilities as:
find([],_,[]). % we reach the end of the list
find([H,H|T],H,T2) :- % there is a match, the successor is also a match
find([H|T],H,T2). % perform one hop
find([H,N|T],H,[N|T2]) :- % there is a match, add the next element
N \= H,
find(T,H,T2). % and continue
find([N|T],H,T2) :- % there is no match
N \= H,
find(T,H,T2). % we continue
This produces:
?- find([in, house, car, in, shop, no, more, apples, in, table], in , X).
X = [house, shop, table] ;
false.
?- find([a,b,c],c,X).
false.
?- find([a,b,c,a,d],a,X).
X = [b, d] ;
false.
?- find([a,a,b],a,X).
X = [b] ;
false.
(Yes/No are in swi-prolog true/false).

Route goes infinite loop prolog

Just begin for prolog and have a practice for route question
train(a,b).
train(b,a).
train(b,c).
train(c,b).
route(X,Y,[]) :-
train(X,Y)
; train(Y,X).
route(X,Y,[H|T]) :-
route(X,H,[]),
route(H,Y,T).
by doing this route/3 The first rule give two direct connected places an empty set states that there is a route. Second rule states the case where there are intermediate places to reach from one to another. but when I query this and I got a loop route.
Someone said to have a helper predicate visited_route/4 to keep track of the places already visited, but don't know how this way works. Hints or example would be help.
The problem with your current solution is that the Prolog solver generates infinite tracks like [a,b,a,b,a,b,a...] never reaching the end.
You may want to do, is to exclude cases, where X, Y, or H is a member of T (this may be the visited_route/4 predicate). This way, you won't ever pass the same node twice.
Edit
I've sat down and freshened my Prolog knowledge a little bit, creating such code, which seems to work:
train(a,b).
%train(b,a). Your predicate is symmetric, you don't need to specify both directions
train(b,c).
%train(c,b).
train(c,d).
train(c,e).
train(d,f).
train(e,f).
visited_route(X, Y, [], V) :-
( train(X,Y) ; train(Y,X) ),
not(member(Y, V)).
visited_route(X, Y, [H | T], V) :-
visited_route(X, H, [], [X | V]),
visited_route(H, Y, T, [X | V]).
route(X,Y,R) :-
visited_route(X, Y, R, []).
Visited route has an additional list containing all nodes visited on a way from X to Y (not counting Y). When solver finds a way leading from X to Y in first visited_route predicate, it then checks if the route doesn't go through already visited node, and discards the candidate if so.

How to check if the paths are connected between rooms

I have the following facts that build ne of the wings of my dungeon.
path(room1,e,room2).
path(room2,w,room1).
path(room2,e,room5).
path(room5,w,room2).
path(room5,e,room6).
path(room6,w,room5).
path(room2,n,room3).
path(room3,s,room2).
path(room3,e,room4).
path(room4,w,room3).
path(room4,s,room5).
path(room5,n,room4).
path(room5,s,room7).
path(room7,n,room5).
path(room7,e,room8) :- at(key, in_hand).
path(room7,e,room8) :- write('The door appears to be locked.'), nl, fail.
path(room8,w,room7).
path(room2,s,room9).
path(room9,n,room2).
path(room9,s,room10).
path(room10,n,room9).
path(room10,w,room11).
path(room11,e,room10).
path(room11,s,room12).
path(room12,n,room11).
now i want to check if i one room is connected to another and then perhaps display one of the possible paths.
i tried to do something like this
connected(X,Y):-path(X,_,Z),path(Z,_,Y).
but it keeps giving me false, what am i doing wrong.
Try something like
connect(Bgn,End,Path) :- % to find a path between two nodes in the graph
connected(Bgn,End,[],P) , % - we invoke the helper, seeding the visited list as the empty list
reverse(P,Path) % - on success, since the path built has stack semantics, we need to reverse it, unifying it with the result
.
connected(X,Y,V,[X,Y|V]) :- % can we get directly from X to Y?
path(X,_,Y) % - if so, we succeed, marking X and Y as visited
. %
connected(X,Y,V,P) :- % otherwise...
path(X,_,T) , % - if a path exists from X to another room (T)
T \= Y , % - such that T is not Y
\+ member(X,V) , % - and we have not yet visited X
connected(T,Y,[X|V],P) % - we continue on, marking X as visited.
.
You'll note the test for having visited the room before. If your graph has cycles, if you don't have some sort of test for having previously visited the node, you'll wind up going around and around in circles...until you get a stack overflow.

Prolog - recursing down family tree

I am trying to write a Prolog program that will print out the male successors of British Royalty in order. My attempt so far:
son(elizabeth, charles).
son(charles, william).
son(charles, henry).
son(elizabeth, andrew).
son(elizabeth, edward).
son(edward, severn).
successor(X, Y) :- son(X, Y).
successor(X, Y) :- son(X, C), successor(C, Y).
The successor function doesn't quite do what I want: the current output is this:
successor(elizabeth, Y).
Y = charles ;
Y = andrew ;
Y = edward ;
Y = william ;
Y = henry ;
Y = severn ;
false.
The first rule makes all three immediate children print out, then the second rule prints out all the descendants. But the descendants of the first child should come before the second immediate child, like this:
successor(elizabeth, Y).
Y = charles ;
Y = william ; % william and henry should come before andrew
Y = henry ;
Y = andrew ;
Y = edward ;
Y = severn ;
false.
This is my first Prolog program, and I am at a loss for how to express the right relationship. Can anyone give me an idea or pointers to resources that would be helpful to me?
As rati noted above, Prolog queries are resolved by choosing a rule, recursively evaluating it using depth-first search, then choosing the next rule and repeating the process. However, the particular rules you're starting with actually result in a breadth-first search of the family tree, which, as you noted, does not give output that matches the actual line of succession. Instead, you want to do a depth-first traversal of the royal family tree. This version gives the result you're looking for:
successor(X, Y) :- son(X, Z), (Y = Z; successor(Z, Y)).
Using this rule, Prolog resolves the query successor(X, Y) roughly as follows:
For each Z who is a son of X:
Bind Y to Z, giving Z as a solution.
The ; operator functions as a logical OR, so now Y is unbound and successor/2 is called recursively to get the successors who are sons of Z.
And yes, please do try to get a copy of the Art of Prolog. It's not the easiest programming book to read, but I found it extremely helpful in my (ongoing) attempt to understand logic programming. There seem to have been some cheap hardcover copies of the 1994 edition floating around eBay lately.
You said:
The first rule makes all three immediate children print out, then the second rule prints out all the descendants.
For any given predicate (such as successor/2), PROLOG will generally evaluate all the possible solutions for the 1st clause, then the next, etc. up to the last clause, in that order. Therefore, PROLOG will behave exactly as you've suggested above - solutions to immediate children will be found first, as the first clause of successor/2 does just that, and the second clause finds the descendants. If you were after a different order, try re-ordering the clauses (i.e.);
successor(X, Y) :- son(X, C), successor(C, Y).
successor(X, Y) :- son(X, Y).
This will cause PROLOG to evaluate to:
?- successor(elizabeth, Y).
Y = william ;
Y = henry ;
Y = severn ;
Y = charles ;
Y = andrew ;
Y = edward.
i.e., all descentants before immediate children.
The ordering you've suggested as wanting, however, can't be achieved through a simple reordering of these subgoals. Instead, consider the various tree traversal methods; i.e., in-order, pre-order and post-order. You could write a (simple) program which is capable of walking the tree structure in various different ways, instead of the default evaluation order for PROLOG. For example, consider the following new definition of successor/2:
successor(Parent, [Son|SonDescendents]) :-
son(Parent, Son),
successor(Son, SonDescendents).
This clause seeks to depth-first populate a list of children under a son, and will backtrack to find all solutions.
successor(NonParent, []) :-
\+ son(NonParent, _).
This next clause takes care of the base-case whereby the given individual does not have any sons, therefore no descendants enter the result list (empty).
Evaluating this gives:
?- successor(elizabeth, S).
S = [charles, william] ;
S = [charles, henry] ;
S = [andrew] ;
S = [edward, severn] ;
false.
ps. I highly recommend the following texts for learning PROLOG:
The Art of Prolog, by Leon Sterling and Ehud Shapiro
The Craft of Prolog, by Richard O'Keefe
Programming in Prolog, by Clocksin and Mellish
Your rule set looks good to me, it's giving you the right results, it's just printing them as it deduces them, which makes the order seem incorrect. Work through the results on paper and you will likely get a similar result.

Resources