I am quite new to Prolog and want to create a program, where I can ask for a route with the predicate "route(Startpoint,Endpoint,Route)".
My code so far is:
% facts
connection(s1,s2).
connection(s2,s3).
connection(s3,s4).
connection(s4,s5).
connection(s5,s6).
connection(s6,s7).
connection(s7,s1).
% predicates
route1(X,Y,[X,Y]) :- connection(X,Y).
route1(X,Y,R) :- connection(X,Z), route1(Z,Y,RZ), R=[X|RZ].
route2(X,Y,[X,Y]) :- connection(Y,X).
route2(X,Y,R) :- connection(Z,X), route2(Z,Y,RZ), R=[X|RZ].
route(X,Y,R) :- route1(X,Y,R); route2(X,Y,R).
My code works for some routes, but not when it comes to a cycle (like in the facts above). How can I prevent in Prolog, that a station gets visited in a route multiple times?
For example when I ask "?- route1(s1,s4,R).", Prolog gives me the correct route "[s1,s2,s3,s4]" first, but it also gives me other routes like "[s1, s2, s3, s4, s5, s6, s7, s1, s2, s3, s4]", "[s1, s2, s3, s4, s5, s6, s7, s1, s2, s3, s4, s5, s6, s7, s1, s2, s3, s4]" and so on.
Thanks in advance!
You could just write:
route1(X,Y,[X,Y]) :- connection(X,Y).
route1(X,Y,R) :- connection(X,Z), route1(Z,Y,RZ),R=[X|RZ],
sort(R,R1),length(R,N),length(R1,N1),
(N>N1->!,fail ;true).
sort/2 removes duplicates so if you want your solution not to have duplicates the sorted list and the output list must have same length.
?- route1(s1,s4,R).
R = [s1, s2, s3, s4] ;
false.
Another ways to do that would be :
route1(X,Y,R):-route1(X,Y,[],R).
route1(X,Y,_,[X,Y]) :- connection(X,Y).
route1(X,Y,L,R) :- connection(X,Z),\+member(Z,L),
route1(Z,Y,[Z|L],RZ),R=[X|RZ].
Example:
?- route1(s1,s4,R).
R = [s1, s2, s3, s4] ;
false.
Related
I want to make a program which should give out all possible routes between two stations. The problem I have is that it doesn't give me all routes. My code so far is:
connection(s1,s2).
connection(s2,s3).
connection(s3,s4).
connection(s4,s5).
connection(s5,s1).
connection(s1,s4).
connection(s2,s5).
direction1(X,Y) :- connection(X,Y).
direction2(X,Y) :- connection(Y,X).
route1(X,Y,R):- route1(X,Y,[X],R).
route1(X,Y,_,[X,Y]) :- direction1(X,Y).
route1(X, Y, Visited, Route) :- direction1(X, Z), Z \= Y, \+ member(Z, Visited), route1(Z, Y, [Z|Visited], Route1), Route = [X|Route1].
route2(X,Y,R):- route2(X,Y,[X],R).
route2(X,Y,_,[X,Y]) :- direction2(X,Y).
route2(X, Y, Visited, Route) :- direction2(X, Z), Z \= Y, \+ member(Z, Visited), route2(Z, Y, [Z|Visited], Route2), Route = [X|Route2].
route(X,Y,R) :- route1(X,Y,R); route2(X,Y,R).
For example when I ask for "?- route(s1,s4,R)" it only gives me R = [s1, s4], R = [s1, s2, s3, s4] and R = [s1, s5, s4].
But there are also the routes (s1,s2,s5,s4) and (s1,s5,s2,s3,s4) and I don't know why I don't get them. How to fix this?
Thanks in advance!
Is enough
direction(X,Y) :- connection(X,Y).
direction(X,Y) :- connection(Y,X).
route(X,Y,R) :-
route(X,Y,[X],R).
route(X,Y,_,[X,Y]) :-
direction(X,Y).
route(X, Y, Visited, [X | Hr]) :-
direction(X, Z),
Z \= Y,
\+ member(Z, Visited),
route(Z, Y, [Z | Visited], Hr).
I mean: use only one direction/2 instead the duplication of direction1/2 and direction2/2. The use of the Visited list permit you to avoid the potential loop.
So you can unify route1/3 and route2/3 in a single route/3.
Your code fail finding [s1, s2, s5, s4] because from s1 to s5 you need direction1/2 (so route1/3 and route1/4) but from s5 to s4 you need direction2/2 (but route1/4 doesn't call direction2/2).
In similar way your code fail finding [s1, s5, s2, s3, s4] because you need direction2/2 (so route2/3 and route2/4) from s1 to s2 but you need direction1/2 from s2 to s4.
I need some very basic help on how to approach this problem. I have a one room planner that, given a start state and end state, it solves this using recursion. However, I want to solve this for two states (aka rooms). I decided that setting flags would be my best bet since each state of the rooms is either in room1 or room2. However I do not know how to implement this. Any can push me in the right direction?
Just to clarify, the new states would be (ontable(X), room1) instead of ontable(X)
:- module( planner,
[
plan/4,change_state/3,conditions_met/2,member_state/2,
move/3,go/2,test/0,test2/0
]).
:- [utils].
plan(State, Goal, _, Moves) :- equal_set(State, Goal),
write('moves are'), nl,
reverse_print_stack(Moves).
plan(State, Goal, Been_list, Moves) :-
move(Name, Preconditions, Actions),
conditions_met(Preconditions, State),
change_state(State, Actions, Child_state),
not(member_state(Child_state, Been_list)),
stack(Child_state, Been_list, New_been_list),
stack(Name, Moves, New_moves),
plan(Child_state, Goal, New_been_list, New_moves),!.
change_state(S, [], S).
change_state(S, [add(P)|T], S_new) :- change_state(S, T, S2),
add_to_set(P, S2, S_new), !.
change_state(S, [del(P)|T], S_new) :- change_state(S, T, S2),
remove_from_set(P, S2, S_new), !.
conditions_met(P, S) :- subset(P, S).
member_state(S, [H|_]) :- equal_set(S, H).
member_state(S, [_|T]) :- member_state(S, T).
/* move types */
move(pickup(X), [handempty, clear(X), on(X, Y)],
[del(handempty), del(clear(X)), del(on(X, Y)),
add(clear(Y)), add(holding(X))]).
move(pickup(X), [handempty, clear(X), ontable(X)],
[del(handempty), del(clear(X)), del(ontable(X)),
add(holding(X))]).
move(putdown(X), [holding(X)],
[del(holding(X)), add(ontable(X)), add(clear(X)),
add(handempty)]).
move(stack(X, Y), [holding(X), clear(Y)],
[del(holding(X)), del(clear(Y)), add(handempty), add(on(X, Y)),
add(clear(X))]).
move(goroom1, [handempty], []).
move(goroom1, [holding(X)], []).
move(goroom2, [handempty], []).
move(goroom2, [holding(X)], []).
/* run commands */
go(S, G) :- plan(S, G, [S], []).
test :- go([handempty, ontable(b), ontable(c), on(a, b), clear(c), clear(a)],
[handempty, ontable(c), on(a,b), on(b, c), clear(a)]).
test2 :- go([handempty, ontable(b), ontable(c), on(a, b), clear(c), clear(a)],
[handempty, ontable(a), ontable(b), on(c, b), clear(a), clear(c)]).
So I solved it by using flags basically. So for each move predicate I basically add a room1 and room2 flag. If they are true then it does the action. So for example ontable(X) in room1 then del ontable and add holding(X) in room1. I also add two predicates to move between rooms. The biggest hurdle was moving from procedural logic to state logic. So if handempty is in room1 then it can only move to room2! #CapelliC thank you for the advice
I am using SICStus Prolog and have a set of facts:
student('John Henry', 'Maths').
student('Jim Henry', 'Maths').
student('John Alan', 'Maths').
student('Alan Smith', 'Computing').
student('Gary Henry', 'Maths').
I want to get the shared subject of two students where both students are different, so I got:
sharedSubject(S1, S2, Sub) :- S1 \== S2, student(S1, Sub), student(S2, Sub).
However, when I enter:
sharedSubject('John Henry', F, E).
I get F = 'John Henry'. Can someone point out where I am going wrong and what I need to do? Thanks.
Use dif/2 instead, or set the \== at the end of the rule - which is not as safe as dif/2. See also:
Difference between X\=Y and dif(X,Y)
What is the logical 'not' in Prolog?
Using \==/2 or dif/2
You must move the S1 \== S2 goal to the end as. If you call your sharedSubject/3 predicate with the second argument not instantiated, as in your sharedSubject('John Henry', F, E), the S1 \== S2 goal will always be true:
?- 'John Henry' \== S2.
true.
Also:
?- S1 \== S2.
true.
See the documentation of the standard (\==)/2 built-in predicate in your Prolog system documentation. In a nutshell, unless you want to test if two variables are the same, be sure that both arguments are instantiated when calling this term equality predicate.
I am trying to compare S1 with A1, S2 with A2, ..., S5 with A5 and get the total number of pairs that match each other. But the interpreter shows "syntax error, operator expected". Is there any simple approach to solve this problem and what's wrong with my code? Thanks!
grade(S1, S2, S3, S4, S5, A1, A2, A3, A4, A5, N):-
S1 = A1, grade2(S2, S3, S4, S5, A2, A3, A4, A5, N+1).
grade(S1, S2, S3, S4, S5, A1, A2, A3, A4, A5, N):-
\+ S1=A1, grade2(S2, S3, S4, S5, A2, A3, A4, A5, N).
grade2(S2, S3, S4, S5, A2, A3, A4, A5, N):-
S2=A2, grade3(S3, S4, S5, A3, A4, A5, N+1).
grade2(S2, S3, S4, S5, A2, A3, A4, A5, N):-
\+ S2=A2, grade3(S3, S4, S5, A3, A4, A5, N).
grade3(S3, S4, S5, A3, A4, A5, N):-
S3=A3, grade4(S4, S5, A4, A5, N+1).
grade3(S3, S4, S5, A3, A4, A5, N):-
\+ S3=A3, grade4(S4, S5, A4, A5, N).
grade4(S4, S5, A4, A5, N):-
S4=A4, grade5(S5, A5, N+1).
grade4(S4, S5, A4, A5, N):-
\+ S4=A4, grade5(S5, A5, N).
grade5(S5, A5, N):-
S5=A5, N is 1.
grade5(S5, A5, N):-
\+ S5=A5, N is 0.
With SWI-Prolog and module lambda you can write :
:- use_module(library(lambda)).
grade(S1, S2, S3, S4, S5, A1, A2, A3, A4, A5, N) :-
foldl(\X^Y^Z^T^(X = Y -> T is Z+1 ; T = Z),
[S1, S2, S3, S4, S5],
[A1, A2, A3, A4, A5],
0, N).
I don't get any syntax error from your code, but it can't execute: your first rule (for instance) should read
grade(S1, S2, S3, S4, S5, A1, A2, A3, A4, A5, N):-
S1 = A1, grade2(S2, S3, S4, S5, A2, A3, A4, A5, M),
N is M+1.
You used is/2 in the only place where it's useless. Last rule(s) could read
grade5(S5, S5, 1):-!.
grade5(_, _, 0).
Then for some easier to read code (understanding code with too much useless detail is harmful for my poor brain...), using library(aggregate)
grade(S1, S2, S3, S4, S5, A1, A2, A3, A4, A5, N):-
aggregate_all(count,
(nth1(I,[S1,S2,S3,S4,S5],X),
nth1(I,[A1,A2,A3,A4,A5],X)
), N).
I get
4 ?- grade(a,b,c,d,e, u,v,c,d,x, N).
N = 2.
I could not reproduce your error. However, as it is now it cannot work because of the way you calculate the value of N. If you do a trace you should be able to see where it goes wrong.
Anyway, although you seem to know in advance how many pairs you are comparing, a more generic approach is to put the pairs in two lists, or even better, in a list of pairs:
[S1-A1, S2-A2, ...]
The - here is just a way of writing -(S, A), and the usual Prolog way of representing "pairs". Once the list of pairs is in this form, you could then explicitly write:
grade([], 0).
grade([S-A|Rest], N) :-
( S == A
-> Add = 1
; Add = 0
),
grade(Rest, N0),
N is N0 + Add.
Note that you cannot use tail-recursion unless you have an extra argument to collect the result so far:
grade([], N, N).
grade([S-A|Rest], Acc, N) :-
( S == A
-> NewAcc is Acc + 1
; NewAcc = Acc
),
grade(Rest, NewAcc, N).
(you need to "initialize" the accumulator when you call the predicate)
?- grade(List, 0, N).
So for your case:
use your initial approach, but fix how N is calculated taking either of the two approaches shown
represent your list of pairs as an actual list of pairs
if you are working with lists, there are other techniques available, see library(aggregate), for example. Might be useful for more complex problems of the same kind.
The below predicate adds item X to list S. It works fine.
addToSet(X, S, S) :-
atomic(X),
member(X, S),
!.
addToSet(X, S, [X|S]) :-
atomic(X).
I am trying to extend it to a predicate which adds list [H|T] to set S. It works perfect if [H|T] is only 2 items long... i.e. if T is also atomic.
addToSet([], S, S).
addToSet([H,T], S, S2) :-
addToSet(H, S, S1),
addToSet(T, S1, S2).
For example, addToSet([5,6],[1,2,3,4],X). works as I desire. However, addToSet([5,6,7],[1,2,3,4],X). does not work at all. I am stumped... there is obviously something wrong with the last 2-3 lines of my code, but I cannot figure it out. Any tips?
Thanks!
addToSet([H,T], S, S2) should be addToSet([H|T], S, S2). As it is, you're matching a list containing exactly H and T, not a list with head H and tail T. Looks like just a typo.