Stuck on a Prolog problem. I know the answer (because I did it on paper first), but I cannot figure out how to get Prolog to come up with the answer.
Problem:
Bill eats a snack every night, having a different fruit and different
nuts each night. From the statements below, identify what Bill had for
a snack for each weeknight last week.
a) The apple was eaten later in the week than the mango.
b) The banana was eaten later in the week than both the almonds and
peanuts, but earlier in the week than the pear.
c) The cashews were eaten earlier in the week than both the banana and
the apricot, but later in the week than the peanuts.
d) The pecans were not eaten the evening after the almonds.
e) Bill ate walnuts one night.
Note that the problem is about 5 weeknights (Monday through Friday),
and mentions 5 kinds of fruit and 5 kinds of nuts. Your program should
solve the problem and print out the solution, which will be a set of 5
triples like (Monday, apple, pecans), ... (Friday, mango, walnuts).
Clearly, these are not the correct answers, but just values to show
you what the solution will look like.
Code so far:
before_in_week(X, Y, Days) :-
nth1(Xi, Days, X),
nth1(Yi, Days, Y),
Xi < Yi.
print_solve([Head|Tail]) :-
write(Head),
nl,
print_solve(Tail).
solve(A) :-
% all triples
A = [[day1, fruit1, nut1],
[day2, fruit2, nut2],
[day3, fruit3, nut3],
[day4, fruit4, nut4],
[day5, fruit5, nut5]],
Days = [monday, tuesday, wednesday, thursday, friday],
Days = [day1, day2, day3, day4, day5],
Fruits = [apple,banana,pear,mango,apricot],
permutation(Fruits, [fruit1, fruit2, fruit3, fruit4, fruit5]),
Nuts = [almonds,pecans,cashews,peanuts,walnuts],
permutation(Nuts, [nut1, nut2, nut3, nut4, nut5]),
% clue 1 - mango before apple
fruit5 \= mango,
member([C1,mango,_], A),
member([C2,apple,_], A), before_in_week(C1,C2,Days),
% clue 2 - banana after almonds and peanuts, but before pear
fruit5 \= banana,
member([C1,banana,_], A),
member([C2,pear,_], A), before_in_week(C1,C2,Days),
member([C3,_,almonds], A), before_in_week(C3,C1,Days),
member([C4,_,peanuts], A), before_in_week(C4,C1,Days),
% clue 3 - cashews before banana and apricot, but after peanuts
nut5 \= peanuts,
member([C1,_,cashews], A),
member([C2,_,peanuts], A), before_in_week(C1,C2,Days),
member([C3,banana,_], A), before_in_week(C3,C1,Days),
member([C4,apricot,_], A), before_in_week(C4,C1,Days),
% clue 4 - pecans not night after almonds
nut5 \= almonds,
% clue 5 - ate walnuts one night
print_solve(A).
First, there is really no need to print anything manually. Prolog's top level does this for you, if you enter the query solve(A). yet,
second, there is no solution. That is really what you are interested in. There is a very simple and very general method to narrow down the source of failure. Simply generalize away all the goals, one after the other. I like to do this by adding a * in front like so:
:- op(950, fy, *).
*_0.
solve(A) :-
* A = [[day1, fruit1, nut1], [day2, fruit2, nut2], [day3, fruit3, nut3],
[day4, fruit4, nut4], [day5, fruit5, nut5]],
Days = [monday|_/*[tuesday, wednesday, thursday, friday]*/],
Days = [day1|_/*[day2, day3, day4, day5]*/],
* Fruits = [apple,banana,pear,mango,apricot],
* permutation(Fruits, [fruit1, fruit2, fruit3, fruit4, fruit5]),
* Nuts = [almonds,pecans,cashews,peanuts,walnuts],
* permutation(Nuts, [nut1, nut2, nut3, nut4, nut5]),
% clue 1 - mango before apple
* fruit5 \= mango,
* member([C1,mango,_], A),
* member([C2,apple,_], A), before_in_week(C1,C2,Days),
% clue 2 - banana after almonds and peanuts, but before pear
* fruit5 \= banana,
* member([C1,banana,_], A),
* member([C2,pear,_], A), before_in_week(C1,C2,Days),
* member([C3,_,almonds], A), before_in_week(C3,C1,Days),
* member([C4,_,peanuts], A), before_in_week(C4,C1,Days),
% clue 3 - cashews before banana and apricot, but after peanuts
* nut5 \= peanuts,
* member([C1,_,cashews], A),
* member([C2,_,peanuts], A), before_in_week(C1,C2,Days),
* member([C3,banana,_], A), before_in_week(C3,C1,Days),
* member([C4,apricot,_], A), before_in_week(C4,C1,Days),
% clue 4 - pecans not night after almonds
* nut5 \= almonds.
% clue 5 - ate walnuts one night
In this program slice, which is a generalization of your original program, it boils down to the inability to succeed for
Days = [monday|_], Days = [day1|_]
You have to change there something. day1 is a constant, it rather should be a variable.
Later, replace all X \= const by dif(X, const).
Your biggest issue is that you are using atoms (fruit4) but you should use variables (Fruit4). Note the capitalization at the start.
Also, you're doing a permutation that you don't need. Prolog does all of the permutations you need via backtracking. That's what make Prolog such an interesting language.
Try this code:
?- solve(A),print_solve(A).
solve(A) :-
A = [[monday,_,_],[tuesday,_,_],[wednesday,_,_],[thursday,_,_],[friday,_,_]],
%clue 1 - mango before apple
before([_,mango,_],[_,apple,_],A),
% clue 2 - banana after almonds and peanuts, but before pear
before([_,_,almonds],[_,banana,_],A),
before([_,_,peanuts],[_,banana,_],A),
before([_,banana,_],[_,pear,_],A),
% clue 3 - cashews before banana and apricot, but after peanuts
before([_,_,cashews],[_,banana,_],A),
before([_,_,cashews],[_,apricot,_],A),
before([_,_,peanuts],[_,_,cashews],A),
% clue 4 - pecans not night after almonds
append(H,[[_,_,almonds],[_,_,_]|T],A),
(member([_,_,pecans],H);member([_,_,pecans],T)),
% clue 5 - ate walnuts one night
member([_,_,walnuts],A),
true.
print_solve([]).
print_solve([Head|Tail]) :-
write(Head),
nl,
print_solve(Tail).
before(X,Y,Days) :-
append(A,B,Days),
member(X,A),
member(Y,B).
That gives me:
[monday, mango, peanuts]
[tuesday, apple, cashews]
[wednesday, apricot, almonds]
[thursday, banana, walnuts]
[friday, pear, pecans]
Yes.
The puzzle can be easily solved by means of one of workhorses of Prolog: generate-and-test. The key is modelling expressions over domain variables (constraints) making easy to check if they are satisfied.
snacks(Week) :-
% model the problem with domain variables,
% make the symbolic associations explicit
% this is the 'generation phase'
Nuts = [
almonds:Almonds,
cashews:Cashews,
pecans:Pecans,
peanuts:Peanuts,
walnuts:_Walnuts
],
Fruits = [
apple:Apple,
banana:Banana,
pear:Pear,
mango:Mango,
apricot:Apricot
],
% since we are going to use plain arithmetic, assign numbers before attempt to evaluate constraints
assign_days(Nuts),
assign_days(Fruits),
% now the 'application symbols' are bound to integers, then we can
% code actual constraint expressions in a simple way...
% this is the 'test phase'
% a) The apple was eaten later in the week than the mango.
Apple>Mango,
% b) The banana was eaten later in the week than both the almonds and peanuts,
% but earlier in the week than the pear.
Banana>Almonds,Banana>Peanuts,Banana<Pear,
% c) The cashews were eaten earlier in the week than both the banana and the apricot,
% but later in the week than the peanuts.
Cashews<Banana,Cashews<Apricot,Cashews>Peanuts,
% d) The pecans were not eaten the evening after the almonds.
Pecans=\=Almonds+1,
% e) Bill ate walnuts one night.
% no constraints, just existance
% when we get here, domain variables satisfy the constraints
% just format the workspace in easy to read list
findall((Day,Fruit,Nut),(
nth1(NDay,['Monday','Tuesday','Wednesday','Thursday','Friday'],Day),
memberchk(Fruit:NDay,Fruits),
memberchk(Nut:NDay,Nuts)
),Week).
assign_days(Snacks) :-
numlist(1,5,Nums),
permutation(Nums,Perm),
maplist([Day,_:Day]>>true,Perm,Snacks).
I'm having some issues with the findall/3 in Prolog.
Facts:
%
country(dublin,ireland).
country(cork,ireland).
country(london,uk).
country(rome,italy).
country(moscow,russia).
country(hongkong,china).
country(amsterdam,holland).
country(berlin,germany).
country(paris,france).
country(newyork,usa).
country(chicago,usa).
country(sao_paulo,brazil).
country(rio,brazil).
I need to write predicate to show the connections from city X to city Y (one by one). X and Y are the two inputs (city) and T is the output. Each solution T is a list of all the cities connecting X to Y (X and Y included).
Example:
| ?- trip(rome,dublin,T).
T=[rome,london,dublin] ;
//first solution T=[rome,paris,dublin];
//second solution
my try is
path(X,Y,[X|Y]):- edge(X,Y).
path(X,Y,[]):- edge(X,Z),not(member(Z,V)),path(Z,Y,[Z|V]).
Any ideas would be greatly appreciated.
Cheers,
I think your question is: "how can I display all possible trip between two country ?". If so I advice you to do something like that:
trip(X,Y,T) :- X \= Y,
path(X, Y, [X], T).
path(Departure, Arrival, Visited, go(Departure, Arrival)) :- direct(Departure, Arrival) , !.
path(Departure, Arrival, Visited, go(Departure, Intermediate, GO)) :-
direct(Departure, Intermediate),
Intermediate \= Departure,
\+ member(Intermediate,Visited),
path(Intermediate, Arrival, [Intermediate|Visited], GO).
So I also advice you to modify the facts in your knowledge base like:
direct(rome, moskow).
direct(moskow, paris).
direct(paris, chicago).
direct(sau_paolo, rio).
direct(rio, honkong).
and so on.
What you did is logically uncorrect. How can you know if you can travel from a city to another only knowing where they are? There should be some facts that state that is possible travel from a city to another, e.g. direct(rome, milan) (or travel(rome, milan), you choose the functor).
I hope to have help you.
If you have some dubt, just ask ;)
by use prolog
Timetable of air routes
Each air route can be described by a structure:
country of departure, country of landing, date of departure, time of departure, duration, tariffs.
Tariff can described by a structure:
price for business class, price for economy class.
Implement the following rules:
• Find all routes from the given country
• Find all routes to the given country with duration less then than the given number
• Find all routes with the cheapest price for economy class
• Find all prices for flying from one given country to another given country
I am working through sample questions while studying, using SWI-Prolog. I have reached the last section of this question, where I have to recursively (I hope) compare elements of a list containing 'researcher' structures to determine whether or not the researchers have the same surname, and, if they do, return the Forename and Surname of the group leader for that list.
There is only one list that meets this criteria and it has four members, all with the same surname. However, the correct answer is returned FOUR times. I feel my solution is inelegant and is lacking. Here is the question:
The following Prolog database represents subject teaching teams.
% A research group structure takes the form
% group(Crew, Leader, Assistant_leader).
%
% Crew is a list of researcher structures,
% but excludes the researcher structures for Leader
% and Assistant_leader.
%
% researcher structures take the form
% researcher(Surname, First_name, expertise(Level, Area)).
group([researcher(giles,will,expertise(3,engineering)),
researcher(ford,bertha,expertise(2,computing))],
researcher(mcelvey,bob,expertise(5,biology)),
researcher(pike,michelle,expertise(4,physics))).
group([researcher(davis,owen,expertise(4,mathematics)),
researcher(raleigh,sophie,expertise(4,physics))],
researcher(beattie,katy,expertise(5,engineering)),
researcher(deane,fergus,expertise(4,chemistry))).
group([researcher(hardy,dan,expertise(4,biology))],
researcher(mellon,paul,expertise(4,computing)),
researcher(halls,antonia,expertise(3,physics))).
group([researcher(doone,pat,expertise(2,computing)),
researcher(doone,burt,expertise(5,computing)),
researcher(doone,celia,expertise(4,computing)),
researcher(doone,norma,expertise(2,computing))],
researcher(maine,jack,expertise(3,biology)),
researcher(havilland,olive,expertise(5,chemistry))).
Given this information, write Prolog rules (and any additional predicates required) that can be used to return the following:
the first name and surname of any leader whose crew members number more than one and who all have the same surname. [4 marks]
This is the solution I presently have using recursion, though it's unnecessarily inefficient as for every member of the list, it compares that member to every other member. So, as the correct list is four members long, it returns 'jack maine' four times.
surname(researcher(S,_,_),S).
checkSurname([],Surname):-
Surname==Surname. % base case
checkSurname([Researcher|List],Surname):-
surname(Researcher,SameSurname),
Surname == SameSurname,
checkSurname(List,SameSurname).
q4(Forename,Surname):-
group(Crew,researcher(Surname,Forename,_),_),
length(Crew,Length),
Length > 1,
member(researcher(SameSurname,_,_),Crew),
checkSurname(Crew,SameSurname).
How could I do this without the duplicate results and without redundantly comparing each member to every other member each time? For every approach I've taken I am snagged each time with 'SameSurname' being left as a singleton, hence having to force use of it twice in the q4 predicate.
Current output
13 ?- q4(X,Y).
X = jack,
Y = maine ; x4
A compact and efficient solution:
q4(F, S) :-
group([researcher(First,_,_), researcher(Second,_,_)| Crew], researcher(S, F, _), _),
\+ (member(researcher(Surname, _, _), [researcher(Second,_,_)| Crew]), First \== Surname).
Example call (resulting in a single solution):
?- q4(X,Y).
X = jack,
Y = maine.
You are doing it more complicated than it has to be. Your q4/2 could be even simpler:
q4(First_name, Surname) :-
group(Crew, researcher(Surname, First_name, _E), _A),
length(Crew, Len), Len > 1,
all_same_surname(Crew).
Now you only need to define all_same_surname/1. The idea is simple: take the surname of the first crew member and compare it to the surnames of the rest:
all_same_surname([researcher(Surname, _FN, _E)|Rest]) :-
rest_same_surname(Rest, Surname).
rest_same_surname([], _Surname).
rest_same_surname([researcher(Surname, _FN, _E)|Rest), Surname) :-
rest_same_surname(Rest, Surname).
(Obviously, all_same_surname/1 fails immediately if there are no members of the crew)
This should be it, unless I misunderstood the problem statement.
?- q4(F, S).
F = jack,
S = maine.
How about that?
Note: The solution just takes the most straight-forward approach to answering the question and being easy to write and read. There is a lot of stuff that could be done otherwise. Since there is no reason not to, I used pattern matching and unification in the heads of the predicates, and not comparison in the body or extra predicates for extracting arguments from the compound terms.
P.S. Think about what member/2 does (look up its definition in the library, even), and you will see where all the extra choice points in your solution are coming from.
Boris did answer this question already, but I want to show the most concise solution I could come with. It's just for the educational purposes (promoting findall/3 and maplist/2):
q4(F, S) :-
group(Crew, researcher(S, F, _), _),
findall(Surname, member(researcher(Surname, _, _), Crew), Surnames),
Surnames = [ First, Second | Rest ],
maplist(=(First), [ Second | Rest ]).
I am trying to create a prolog rule which will generate all the people in a social network using S number degrees of separation.
This is the rule that i have made but it is only printing empty lists. Can somebody please help me into helping me understand why this is happening and me where i am going wrong?:
socialN(_,N):- N<1,!.
socialN(_,N,_,_):- N<1,!.
socialN(P1,Separation,S1,S):-
(message(P1,P2,_); message(P2,P1,_)),
D is Separation-1,
\+(member(P2,S1)),
append(P2,S1,S2),socialN(P1,D,S2,S),!.
socialN(P2,Separation,S,S).
These are the facts:
message(allan, steve, 2013-09-03).
message(nayna, jane, 2013-09-03).
message(steve, jane, 2013-09-04).
message(steve, allan, 2013-09-04).
message(mark, martin, 2013-09-04).
message(martin, steve, 2013-09-04).
message(allan, martin, 2013-09-05).
E.g. Mark’s network includes just Martin for 1 degree of separation; it includes Martin, Steve and Allan for 2 degrees of separation; and Martin, Steve, Allan and Jane for 3.
I see you are using append and member, so I suppose you are trying to build up a list of people. I was a bit surprised that you were not using findall. Like this:
allDirectLinks(P1, L) :- findall(P2, directlyLinked(P1, P2), L).
directlyLinked(P1, P1).
directlyLinked(P1, P2) :- message(P1, P2, _).
directlyLinked(P1, P2) :- message(P2, P1, _).
From there, you can write a recursive function to find the indirect links:
socialN(0, P, [P]) :- !.
socialN(N, P1, L3) :-
N>0, !,
N1 is N-1,
socialN(N1, P1, L1)
maplist(allDirectLinks, L1, L2),
append(L2, L3).
For example, this yields in Y a list of people separated 2 steps or less from Mark:
socialN(2, mark, X), list_to_set(X, Y).
Please note, Mark himself is included in the resulting list (being a 'level 0' link); I suppose it cannot be too hard to filter that out afterwards.
I hope this makes sense; I am a bit rusty, haven't done any Prolog in 25 years.
EDIT: explanation of the rules I defined:
directlyLinked: true if there is a message between two persons (regardless of the direction of the message)
allDirectLinks: accumulates into list L all persons directly linked to a given person P1; just read the manual about findall
socialN: builds up a list of people connected to a given person (P) at a distance less than or equal to a given distance (N)
socialN(0, ...): at distance 0, every person is linked to himself
socialN(N, ...): makes a recursive call to get a list of connections at distance N-1, then uses maplist to apply allDirectLinks to every connection found, and finally uses append to concatenate the results together.