Merge two predicates in one - Prolog - prolog

I am a beginner at prolog and required to write a predicate path(Start, Dest, Path) for metro stations application using backtracking. Given some facts: connected(x,y) describing the connected stations, I managed to do the following
%% Some facts (for illustration):
connected(a,b).
connected(b,c).
connected(c,d).
connected(d,e).
connected(e,f).
%% Direction A (e.g from b to e)
path1(Start, End, [[Start,End]]):- (connected(Start,End) ; connected(End,Start)),!.
path1(Start, End, [[Start,X]|Path]):-
connected(Start, X),
path1(X, End, Path).
%% Direction B (e.g from f to A)
path2(Start, End, [[Start,End]]):- (connected(Start,End) ; connected(End,Start)),!.
path2(Start, End, [[Start,X]|Path]):-
connected(X, Start),
path2(X, End, Path).
%% And for the required predicate:
path(Start, End, Path):-
path1(Start, End, Path)
;
path2(Start, End, Path).
The above predicates works properly and as required, but what I want to do is to merge these two predicates into a single one in a better way and I don't know exactly how to do so. Anyone can help?
Thanks in advance.
EDIT:
I modified it to the following:
path(Start, End, [[Start,End]], _):- (connected(Start,End) ; connected(End,Start)),!.
path(Start, End, [[Start,X]|Path], Direction):-
(Direction == 0,
connected(Start, X),
path(X, End, Path,0))
;
(Direction == 1,
connected(X, Start),
path(X, End, Path,1))
%% And for the required predicate:
path(Start, End, Path):-
path(Start, End, Path, 0)
;
path(Start, End, Path, 1).
but still need to remove more of the repetitive code lines.

A possible solution is:
path(Start, End, Path) :-
path(_, Start, End, Path).
path(_, Start, Start, []).
path(Direction, Start, End, [[Start,X]|Path]) :-
link(Direction, Start, X),
path(Direction, X, End, Path).
link(forward, X, Y) :- connected(X, Y).
link(backward, X, Y) :- connected(Y, X).
connected(a,b).
connected(b,c).
connected(c,d).
connected(d,e).
connected(e,f).
Examples:
?- path(c, a, P).
P = [[c, b], [b, a]] ;
false.
?- path(a, c, P).
P = [[a, b], [b, c]] ;
false.
?- path(D, a,c,P).
D = forward,
P = [[a, b], [b, c]] ;
false.
?- path(D, c, a, P).
D = backward,
P = [[c, b], [b, a]] ;
false.

Related

Prolog: Order of clauses for finding path in graph

I have a cyclic graph with entry and exit nodes for which I want to find out all paths leading from any entry to any exit node.
entry(a).
exit(e).
exit(f).
next(a, b).
next(b, c).
next(b, d).
next(c, e).
next(d, f).
/* Cycle */
next(c, d).
next(d, b).
/* path(entrynode, exitnode, pathtrace) */
path(X, Y, P) :- entry(X), path2(X, Y, P).
path2(X, Y, [Y]) :- next(X, Y), exit(Y).
path2(X, Y, [P|PS]) :- next(X, P), path2(P, Y, PS).
My path2 predicate works great on a non cyclic graph. Now I want to extend it to cyclic ones. All I would have to do is check if a new possible node is already in my list of visited nodes. For this I would add not(member(X, PS)) to my last rule.
If I add it before the recursion, it always returns false. If I add it after the recursion Prolog tries to find the paths first and runs out of stack. It returns the correct answers but it tries to find more and gets stuck.
Therefore: Where should I add the check or what did I do wrong/what can I do better?
You need an additional argument for your path2/3 predicate as your third argument is the path being constructed, not a list of visited nodes. I.e. you cannot simply add the \+ member(X,Ps) goal to the last rule of the predicate as Ps is bound by the recursive call. Try instead:
path(X, Y, P) :-
entry(X),
path2(X, Y, [], P).
path2(X, Y, _Visited, [Y]) :-
next(X, Y),
exit(Y).
path2(X, Y, Visited, [P|PS]) :-
next(X, P),
\+ member(P, Visited),
path2(P, Y, [P| Visited], PS).
Sample calls:
| ?- path(X, Y, P).
P = [b,c,e]
X = a
Y = e ? ;
P = [b,c,d,f]
X = a
Y = f ? ;
P = [b,d,f]
X = a
Y = f ? ;
no

Finding a cycle in a simple Prolog graph

I am having trouble with this simple Prolog graph, i am trying to test for a cycle and have no idea why it isn't working. isPath seems to work, the problem is with cycle function which is supposed to check if there is a cycle on the given letter. Can anyone help?
path(a, b).
path(a, c).
path(a, f).
path(b, e).
path(c, d).
path(d, a).
path(d, h).
path(e, f).
path(e, g).
path(e, h).
path(f, g).
path(f, b).
path(h, g).
isPath(X, X) :-
path(X, Y).
isPath(X, Y) :-
path(X, Z),
isPath(Z, Y).
cycleIt(J) :-
isPath(J, K),
isPath(K, J).
You can use DCG !
How can you find cycles ? You need to find a node From, linked by an edge to another node Via, then from Via, you need to find a path, ending in From, and you must not have cycle during this search.
so :
cycle --> [X], {path(X, Y)},search(Y, X, []).
% the search is ended
search(From, To, _Visited)--> {path(From,To)}, [From,To].
% we try an another path using Via, which has not been already visited
search(From, To, Visited)--> {path(From,Via), \+member(Via, Visited)}, [From], search(Via, To, [Via|Visited]).
The result
?- phrase(cycle, A,[]).
A = [a, c, d, a] ;
A = [b, e, f, b] ;
A = [c, d, a, c] ;
A = [d, a, c, d] ;
A = [e, f, b, e] ;
A = [f, b, e, f] ;
false.

Prolog append/3 realization with more determinism?

It is folk knowledge that append(X,[Y],Z) finds the last element
Y of the list Z and the remaining list X.
But there is some advantage of having a customized predicate last/3,
namely it can react without leaving a choice point:
?- last([1,2,3],X,Y).
X = 3,
Y = [1,2]
?- append(Y,[X],[1,2,3]).
Y = [1,2],
X = 3 ;
No
Is there a way to realize a different implementation of
append/3 which would also not leave a choice point in the
above example?
P.S.: I am comparing:
/**
* append(L1, L2, L3):
* The predicate succeeds whenever L3 unifies with the concatenation of L1 and L2.
*/
% append(+List, +List, -List)
:- public append/3.
append([], X, X).
append([X|Y], Z, [X|T]) :- append(Y, Z, T).
And (à la Gertjan van Noord):
/**
* last(L, E, R):
* The predicate succeeds with E being the last element of the list L
* and R being the remainder of the list.
*/
% last(+List, -Elem, -List)
:- public last/3.
last([X|Y], Z, T) :- last2(Y, X, Z, T).
% last2(+List, +Elem, -Elem, -List)
:- private last2/4.
last2([], X, X, []).
last2([X|Y], U, Z, [U|T]) :- last2(Y, X, Z, T).
One way to do it is to use foldl/4 with the appropriate help predicate:
swap(A, B, B, A).
list_front_last([X|Xs], F, L) :-
is_list(Xs),
foldl(swap, Xs, F, X, L).
This should be it:
?- list_front_last([a,b,c,d], F, L).
F = [a, b, c],
L = d.
?- list_front_last([], F, L).
false.
?- list_front_last([c], F, L).
F = [],
L = c.
?- Ys = [y|Ys], list_front_last(Ys, F, L).
false.
Try to see if you can leave out the is_list/1 from the definition.
As I posted:
append2(Start, End, Both) :-
% Preventing unwanted choicepoint with append(X, [1], [1]).
is_list(Both),
is_list(End),
!,
append(Start, End, Both),
!.
append2(Start, End, Both) :-
append(Start, End, Both),
% Preventing unwanted choicepoint with append(X, Y, [1]).
(End == [] -> ! ; true).
Result in swi-prolog:
?- append2(Y, [X], [1,2,3]).
Y = [1, 2],
X = 3.

Prolog Zip Function

Im in rew to Prolog. I'm trying to write a zip function. The question goes like this.
zip(L1, L2, X): The list X is formed by “zipping” the first 2 arguments.
the result should be like this:
?- zip([a, b, c], [x, y, z], X).
L = [a, x, b, y, c, z]
?- zip([a, b], [x, y, z], X).
false
?- zip([a, b, c, d], X, [a, p, b, q, c, r, d, s]).
X = [p, q, r, s]
I have done this so far.
I can get the result for 1st 3rd but not the 2nd one. Can anybody can help me solving it for the 2nd one? thank you
zip([X],[Y],[X,Y]).
zip([], [], []).
zip([X|Xs], [Y|Ys], [X,Y|Zs]) :-
zip(Xs,Ys,Zs).
zip([X|Xs],[],[X|Xs]).
zip([Y|Ys],[],[Y|Ys]).
zip(Xs, [], Xs).
zip([], Ys, Ys).
How do I define this function where:
allsame(L): The list L contains identical elements.
I should get this.
?- allsame([b, b, b]).
true
?- allsame([c, c, c, Y, c, c, X, c]).
X = c, Y = c
You had it:
zip([], [], []).
zip([X|Xs], [Y|Ys], [X,Y|Zs]) :- zip(Xs,Ys,Zs).
This alone is enough to define the relation you're seeking. The extra clauses don't help.
Test:
?- zip([a, b, c], [x, y, z], X).
X = [a, x, b, y, c, z].
?- zip([a, b], [x, y, z], X).
false.
?- zip([a, b, c, d], X, [a, p, b, q, c, r, d, s]).
X = [p, q, r, s].
#m09 gave the correct answer. But I'd like to explain why what you have isn't correct:
(1) zip([X],[Y],[X,Y]).
This rule says that [X,Y] is what you get when you zip [X] with [Y]. That is correct, and will not lead to a problem. The rule is simply redundant with the rules below (which I'll explain...).
(2) zip([], [], []).
This rule says [] is what you get when you zip [] with [] which is correct and as simple a rule as you can have for zip.
(3) zip([X|Xs], [Y|Ys], [X,Y|Zs]) :-
zip(Xs,Ys,Zs).
This rule says that [X,Y|Zs] is what you get when you zip [X|Xs] with [Y|Ys] if Zs is what you get when you zip Xs with Ys. That is also logical and correct. Notice that zip([X], [Y], [X,Y]) is zip([X|[]], [Y|[]], [X,Y|[]]). so it can be derived from rules (2) and (3). It would match rule (3) first, zip([X|[]], [Y|[]], [X,Y|Zs]) :- zip([], [], Zs)., then Zs would become [] by rule (2)`.
(4) zip([X|Xs],[],[X|Xs]).
(5) zip([Y|Ys],[],[Y|Ys]).
Rule (4) says [X|Xs] is what you get when you zip [X|Xs] with []. Rule (5) says exactly the same thing, logically, only with a different variable name. These are incorrect, since that would mean, for example, zip([a,b,c], [], Z) would be true if Z = [a,b,c].
(6) zip(Xs, [], Xs).
This rule says that Xs is what you get when you zip Xs with []. Or stated another way, any input, zipped with [], would be that input value again. It wouldn't even have to be a list! This is clearly incorrect. Queries like zip(x, [], Z) would succeed with Z = x, and zip(friend(bill,mary), [], Z) would succeed with Z = friend(bill,mary).
(7) zip([], Ys, Ys).
This rule says that Ys is what you get when you zip [] with Ys. It is incorrect for the same reason (6) is incorrect. In fact, this rule, combined with (2) and (3) are why the query zip([a, b], [x, y, z], X). will yield a result rather than fail. Rules (2) and (3) will recurse to zip([b], [y,z], [b,y|T]) :- zip([], [z], T). and then zip([], [z], T) will finally succeed on rule (7) with T = [z], and ultimately yielding a final result to zip([a, b], [x, y, z], X) of X = [a, x, b, y, z].

substitute in a nested list (prolog)

/* substitute(X,Y,Xs,Ys) is true if the list Ys is the result of substituting Y for all occurrences of X in the list Xs.
This is what I have so far:
subs(_,_,[],[]).
subs(X,Y,[X|L1],[Y|L2]):- subs(X,Y,L1,L2).
subs(X,Y,[H|L1],[H|L2]):- X\=H, not(H=[_|_]), subs(X,Y,L1,L2).
subs(X,Y,[H|_],[L2]):- X\=H, H=[_|_], subs(X,Y,H,L2).
My code works except it omits the elements following the nested list. For example:
?- subs(a,b,[a,[a,c],a],Z).
Z = [b, [b, c]] .
What should I add to this program?
Here is how you could write it using (... -> ... ; ...):
subs(_, _, [], []).
subs(X, Y, [H1|T1], [H2|T2]) :-
(H1 == X ->
H2 = Y
; is_list(H1) ->
subs(X, Y, H1, H2),
subs(X, Y, T1, T2)
;
H1 = H2,
subs(X, Y, T1, T2)
).
The problem is that once you find a nested list, you forget about whatever is behind that nested list. Instead, after recursing with the nested nest, simply continue as before. Thus, you should change the last clause as follows:
subs(X,Y,[H|L1],[H2|L2]):- X\=H, H=[_|_], subs(X,Y,H,H2), subs(X, Y, L1, L2).
Aside from that, there are a couple of ways in which you can improve the code:
Use cuts (!/0) to stop backtracking. In this way you don't have to repeat yourself.
You can use is_list/1 to test whether an argument is a list.
It's okay to use more spaces. Really.
So, an alternative solution is (now using \+/1 instead of not/1):
subs(_, _, [], []).
subs(X, Y, [X|T1], [Y|T2]) :- subs(X, Y, T1, T2), !.
subs(X, Y, [H|T1], [H|T2]) :- \+ is_list(H), subs(X, Y, T1, T2), !.
subs(X, Y, [H1|T1], [H2|T2]) :- subs(X, Y, H1, H2), subs(X, Y, T1, T2).
Demonstration:
?- subs(a, b, [a, [a, [d, f, a]], a, b, a, [g]], Z).
Z = [b, [b, [d, f, b]], b, b, b, [g]].

Resources