How does this Zebra solution in prolog work? - prolog

zebra_owner(Owner) :-
houses(Hs),
member(h(Owner,zebra,_,_,_), Hs).
water_drinker(Drinker) :-
houses(Hs),
member(h(Drinker,_,_,water,_), Hs).
houses(Hs) :-
length(Hs, 5), % 1
member(h(english,_,_,_,red), Hs), % 2
member(h(spanish,dog,_,_,_), Hs), % 3
member(h(_,_,_,coffee,green), Hs), % 4
member(h(ukrainian,_,_,tea,_), Hs), % 5
adjacent(h(_,_,_,_,green), h(_,_,_,_,white), Hs), % 6
member(h(_,snake,winston,_,_), Hs), % 7
member(h(_,_,kool,_,yellow), Hs), % 8
Hs = [_,_,h(_,_,_,milk,_),_,_], % 9
Hs = [h(norwegian,_,_,_,_)|_], % 10
adjacent(h(_,fox,_,_,_), h(_,_,chesterfield,_,_), Hs), % 11
adjacent(h(_,_,kool,_,_), h(_,horse,_,_,_), Hs), % 12
member(h(_,_,lucky,juice,_), Hs), % 13
member(h(japanese,_,kent,_,_), Hs), % 14
adjacent(h(norwegian,_,_,_,_), h(_,_,_,_,blue), Hs), % 15
member(h(_,_,_,water,_), Hs), % one of them drinks water
member(h(_,zebra,_,_,_), Hs). % one of them owns a zebra
adjacent(A, B, Ls) :- append(_, [A,B|_], Ls).
adjacent(A, B, Ls) :- append(_, [B,A|_], Ls).
How come the houses(Hs) predicate not fail the various member(elem, list) checks, if the list is actually empty all the time? I know it might sound like a dumb question, but wrapping my head around Prolog is actually hard, especially after years of Object-oriented programming. Any help is appreciated!
Edit: I didn't mention the query I was asking prolog, which is this zebra_owner(Owner)
Edit 2: Also posting the text of the problem (which is kinda famous) for reference:
Five colored houses in a row, each with an owner, a pet, cigarettes, and a drink.
The English lives in the red house.
The Spanish has a dog.
They drink coffee in the green house.
The Ukrainian drinks tea.
The green house is next to the white house.
The Winston smoker has a serpent.
In the yellow house they smoke Kool.
In the middle house they drink milk.
The Norwegian lives in the first house from the left.
The Chesterfield smoker lives near the man with the fox.
In the house near the house with the horse they smoke Kool.
The Lucky Strike smoker drinks juice.
The Japanese smokes Kent.
The Norwegian lives near the blue house.
Who owns the zebra and who drinks water?

How come the Houses(Hs) predicate not fail the various member(elem, list) checks, if the list is actually empty all the time?
That's because the list isn't actually empty! The empty list in Prolog is []. It contains no elements.
But the list inside a call to houses(Hs) is controlled by the first goal in that predicate, namely length(Hs, 5). What does this goal mean?
An important thing to learn about Prolog is that a goal or query doesn't just mean "is this statement true?". A Prolog goal or query means "under what circumstances is this statement true?". Prolog will try to do work to describe circumstances to you under which your query becomes true.
So even if we know absolutely nothing about Hs before, when executing this length query, GNU Prolog says:
| ?- length(Hs, 5).
Hs = [_,_,_,_,_]
So length(Hs, 5) becomes true if Hs is of the form [_, _, _, _, _]. This is not an empty list. It is not even a possibly empty list. It is a list that definitely contains exactly five elements. We don't know anything about those elements yet! But the length of the list is definitely fixed.
Maybe you got mislead by the fact that Prolog allows you to talk about a list that definitely has elements but whose elements are not yet known. This is a concept that is not possible in typical object oriented languages. But the fact that we don't know the elements doesn't mean that there is nothing there, i.e., that the list is empty.
As for how the member calls succeed: Again, they aren't just checks for "is X a member of Hs". They are questions meaning "under what circumstances is X a member of Hs?". Again, Prolog will do some work for you to describe those circumstances:
| ?- length(Hs, 5), member(x, Hs).
Hs = [x,_,_,_,_] ? ;
Hs = [_,x,_,_,_] ? ;
Hs = [_,_,x,_,_] ? ;
Hs = [_,_,_,x,_] ? ;
Hs = [_,_,_,_,x]
So x is a member of Hs if x is the first element of Hs, or the second, or the third, or so on. In each of these cases, "describing the circumstances" means that Prolog actually binds the appropriate element of the list (which before was a variable) to the element x. For each of these possibilities, further computations following the member goal could further instantiate the list. This is what builds up the solutions to the Zebra puzzle: Instead of just answering "yes" to the question "does the puzzle have a solution", Prolog builds up a data structure actually describing a solution.

The houses list Hs is not empty at all, ever. It is created right at the very beginning with
length(Hs, 5)
So it is a list with five initially uninitialized logical variables, which get instantiated by all the subsequent goals when they are called in turn, in particular by those same member/2 goals you mention.
To see an example of this, try:
32 ?- L=[A,B,C], member(1, L).
L = [1, B, C] ;
L = [A, 1, C] ; % three solutions are found by Prolog
L = [A, B, 1]. % in turn, one after another
Those calls which can't be fulfilled (where no instantiation of the remaining free logical variables is possible) cause that specific search branch to fail.
An example:
33 ?- L=[1,2,C], member(3,L). % a check succeeds, while
L = [1, 2, 3], C = 3. % instantiating the `C` logvar
34 ?- L=[1,2,3], member(3,L). % a check succeeds
L = [1, 2, 3].
35 ?- L=[1,2,3], member(4,L). % a check fails
false.
Only successful instantiations remain, in the end, thus forming the solution, when all the logical variables in Hs become fully instantiated, ground.

Related

Prolog Room Adjacency Puzzle

I am working on an assignment and I have to write a prolog program to solve a puzzle where there are 5 rooms and there are five people, Hunter, Laura, Arnie, Addiley and Ramey. Hunter cannot be in room 5, Laura cannot be in room 1, Arnie cannot be in room 1 or 5, Arnie cannot be adjacent to Laura or Addiley and Ramey is in a room with a higher index than Laura. I have seen various other answers for the exact same problem but no solution has worked thus far.
I have gotten most of the logic down, however the logic to check whether a person is adjacent to another person seems to not be working.
Here is my code:
layout([bedroom(_, 1), bedroom(_, 2), bedroom(_, 3), bedroom(_, 4), bedroom(_, 5)]).
adj(B,C):- B is C+1; B is C-1.
hallway(X) :- layout(X),
member(bedroom(hunter, H), X),
member(bedroom(laura, L), X),
member(bedroom(arnie, N), X),
member(bedroom(addiley, A), X),
member(bedroom(ramey, R), X),
H \= 5,
L \= 1,
N \= 1,
N \= 5,
N \= adj(N, L),
N \= adj(N, A),
R > L.
The resulting output is:
X = [bedroom(hunter,1),bedroom(laura,2),bedroom(arnie,3),bedroom(addiley,4),bedroom(ramey,5)]
However in this output, Arnie is adjacent to Laura and Addiley, seemingly breaking the rules that put into the adjacency logic. Why is it when I check for adjacency nothing seems to happen?
You are very close (and upvote having code and asking a specific question). The problem is here:
N \= adj(N, L),
N \= adj(N, A),
adj(N, L) is true if they are adjacent numbers, and false if they aren't. It doesn't return a value, it just is true or isn't true and the code continues forwards or stops and backtracks and changes member() and then tries again; so you just need:
not(adj(N, L)),
not(adj(N, A)),
to say that "it must not be true that Arnie and Laura have adjacent room numbers".
Compare that with your line in English: "Arnie's room number must not be equal to whether Arnie's room number is adjacent to Laura's", which makes no sense. (Or, more specifically since = is different in Prolog, your line says "Arnie's room number variable N must not unify with the term adj(N, L). Well, member() bound variable N to hold a room number at this point, so variable N can't also be bound to the line of code adj(N, L). So the number and the code are always different, the lines were always true and they weren't testing anything useful).

How to check order in prolog?

I am trying to solve this puzzle in prolog
Five people were eating apples, A finished before B, but behind C. D finished before E, but behind B. What was the finishing order?
My current solution has singleton variable, I am not sure how to fix this.
finishbefore(A, B, Ls) :- append(_, [A,B|_], Ls).
order(Al):-
length(Al,5),
finishbefore(A,B,Al),
finishbefore(C,A,Al),
finishbefore(D,E,Al),
finishbefore(B,D,Al).
%%query
%%?- order(Al).
Here is a pure version using constraints of library(clpz) or library(clpfd). The idea is to ask for a slightly different problem.
How can an endpoint in time be associated to each person respecting the constraints given?
Since we have five persons, five different points in time are sufficient but not strictly necessary, like 1..5.
:- use_module(library(clpz)). % or clpfd
:- set_prolog_flag(double_quotes, chars). % for "abcde" below.
appleeating_(Ends, Zs) :-
Ends = [A,B,C,D,E],
Zs = Ends,
Ends ins 1..5,
% alldifferent(Ends),
A #< B,
C #< A,
D #< E,
B #< D.
?- appleeating_(Ends, Zs).
Ends = [2, 3, 1, 4, 5], Zs = [2, 3, 1, 4, 5].
There is exactly one solution! Note that alldifferent/1 is not directly needed since nowhere is it stated that two persons are not allowed to end at precisely the same time. In fact, above proves that there is no shorter solution. #CapelliC's solution imposes an order, even if two persons finish ex aequo. But for the sake of compatibility, lets now map the solution back to your representation.
list_nth1(Es, N, E) :-
nth1(N, Es, E).
appleeatingorder(OrderedPeople) :-
appleeating_(Ends, Zs),
same_length(OrderedPeople, Ends),
labeling([], Zs), % not strictly needed
maplist(list_nth1(OrderedPeople), Ends,"abcde"). % effectively enforces alldifferent/1
?- appleeatingorder(OrderedPeople).
OrderedPeople = [c,a,b,d,e].
?- appleeatingorder(OrderedPeople).
OrderedPeople = "cabde".
The last solution using double quotes produces Scryer directly. In SWI use library(double_quotes).
(The extra argument Zs of appleeating_/2 is not strictly needed in this case, but it is a very useful convention for CLP predicates in general. It separates the modelling part (appleeating_/2) from the search part (labeling([], Zs)) such that you can easily try various versions for search/labeling at the same time. In order to become actually solved, all variables in Zs have to have an actual value.)
Let's correct finishbefore/3:
finishbefore(X, Y, L) :-
append(_, [X|R], L),
memberchk(Y, R).
then let's encode the known constraints:
check_finish_time(Order) :-
forall(
member(X<Y, [a<b,c<a, d<e,d<b]),
finishbefore(X,Y,Order)).
and now let's test all possible orderings
?- permutation([a,b,c,d,e],P),check_finish_time(P).
I get 9 solutions, backtracking with ;... maybe there are implicit constraints that should be encoded.
edit
Sorry for the noise, have found the bug. Swap the last constraint order, that is b<d instead of d<b, and now only 1 solution is allowed...

How do add degree of separation?

In Prolog, how would I make a rule that checks how many people come in one persons network and then query it on degree of separation?
For example, if in Facebook my name is John; and I have one friend Tom, and Tom has one friend Lucy, and Lucy has one friend Ben, and Ben has one friend Josh, and Josh has one friend Nancy.
I want to create a rule in Prolog that tests how many people are in my network and also tell Prolog to return names up to a given number of degree of separation.
e.g. if I query something like;
?- mynetwork(josh,2).
Prolog should return
John
Tom
Lucy
or
John is friends with Tom
Tom is friends with Lucy
Welcome to Prolog!
First you're going to need some facts:
friend(john, tom).
friend(tom, lucy).
friend(lucy, ben).
...
For simplicity, let's consider the case where friendships are directed: I can friend you, but that doesn't mean you friend me.
Let's say we're friends of degree 1 if I have friended you. That would look like this:
network(Person, 1, Friend) :- friend(Person, Friend).
Now the inductive case is one in which we've found a friend through a friend. That's going to look like this:
network(Person, N1, FoaF) :-
N1 > 1,
N0 is N1-1,
network(Person, N0, Friend),
network(Friend, 1, FoaF).
Using is/2 you can be sure the predicate will be ill-behaved. For instance, if you omit the > 1 constraint, you will be able to ask questions and get N back that you won't if you include it. But you'll also get errors about being out of local stack. So if you can afford to, bring in clpfd now:
:- use_module(library(clpfd)).
network(Person, 1, Friend) :- friend(Person, Friend).
network(Person, N1, FoaF) :-
N1 #> 0,
N0 #= N1-1,
network(Person, N0, Friend),
network(Friend, 1, FoaF).
This should work for all input cases you care to try, though it still has no way to know when you're out of friendship levels.
?- network(john, N, X).
N = 1,
X = tom ;
N = 2,
X = lucy ;
N = 3,
X = ben ;
^CAction (h for help) ? abort
% Execution Aborted
?- network(john, 3, X).
X = ben ;
false.
?- network(john, 2, X).
X = lucy ;
false.
Edit Let me answer your questions out of order.
Where is the print statement?
By design, I haven't used one. Until here, we're just using the Prolog REPL (read-eval-print loop) to do our I/O. This is the natural way to work with Prolog. You'll save yourself a lot of heartache later if you go to some trouble to separate predicates that do I/O and user presentation from predicates concerned with meaning. This is just a petite application of model-view separation. You also benefit from keeping side-effects quarantined in their own predicates. As long as the pure logical part of your program is self-contained, you will always be able to build and compose with it.
How would you print upto a given number of people. e.g. if you type in network(john, 3, X). then it should print out upto N=3 X=ben
I would be inclined to call this predicate show_network/2 instead and keep the separation going. You could do it on the cheap with a failure-driven loop like so:
show_network(Person, Max) :-
between(1, Max, N),
network(Person, N, Friend),
format('~w is friends with ~w\n', [Person, Friend]),
fail.
show_network(_, _).
This will work like so:
?- show_network(john, 3).
john is friends with tom.
john is friends with lucy.
john is friends with ben.
true.
There are other approaches too, for instance, you could use forall/2:
show_network(Person, Max) :-
forall(
(between(1, Max, N), network(Person, N, Friend)),
format('~w is friends with ~w\n', [Person, Friend])).
The relationship between the failure-driven loop and that one should be pretty clear. You could also manually get the list and then process it with maplist/2 or something:
show_network(Person, Max) :-
findall(friend(Person,Friend),
(between(1, Max, N), network(Person, N, Friend)),
Friends),
maplist(show_friend, Friends).
show_friend(friend(Person, Friend)) :-
format('~w is friends with ~w\n', [Person, Friend]).

swi prolog solve

I am using SWI Prolog for windows 7 and one of my assignments is a basic logic puzzle. We have been given a sample solution to a seperate problem - including its source code.
Its the "Its a tie" problem. However I do not know how to get results using the Solve:- predicate. After consulting the .pl file do I have to input a specific command to the console or anything like that?
Thanks for any help.
Source Code (I did not write this code) :
% Problem #1, "It's a tie", Dell Logic Puzzles, October 1999
% Each man (mr so-and-so) got a tie from a relative.
tie(cupids).
tie(happy_faces).
tie(leprechauns).
tie(reindeer).
mr(crow).
mr(evans).
mr(hurley).
mr(speigler).
relative(daughter).
relative(father_in_law).
relative(sister).
relative(uncle).
solve :-
tie(CrowTie), tie(EvansTie), tie(HurleyTie), tie(SpeiglerTie),
all_different([CrowTie, EvansTie, HurleyTie, SpeiglerTie]),
relative(CrowRelative), relative(EvansRelative),
relative(HurleyRelative), relative(SpeiglerRelative),
all_different([CrowRelative, EvansRelative, HurleyRelative, SpeiglerRelative]),
Triples = [ [crow, CrowTie, CrowRelative],
[evans, EvansTie, EvansRelative],
[hurley, HurleyTie, HurleyRelative],
[speigler, SpeiglerTie, SpeiglerRelative] ],
% 1. The tie with the grinning leprechauns wasn't a present from a daughter.
\+ member([_, leprechauns, daughter], Triples),
% 2. Mr. Crow's tie features neither the dancing reindeer nor the yellow happy faces.
\+ member([crow, reindeer, _], Triples),
\+ member([crow, happy_faces, _], Triples),
% 3. Mr. Speigler's tie wasn't a present from his uncle.
\+ member([speigler, _, uncle], Triples),
% 4. The tie with the yellow happy faces wasn't a gift from a sister.
\+ member([_, happy_faces, sister], Triples),
% 5. Mr Evans and Mr. Speigler own the tie with the grinning leprechauns
% and the tie that was a present from a father-in-law, in some order.
( (member([evans, leprechauns, _], Triples),
member([speigler, _, father_in_law], Triples)) ;
(member([speigler, leprechauns, _], Triples),
member([evans, _, father_in_law], Triples)) ),
% 6. Mr. Hurley received his flamboyant tie from his sister.
member([hurley, _, sister], Triples),
tell(crow, CrowTie, CrowRelative),
tell(evans, EvansTie, EvansRelative),
tell(hurley, HurleyTie, HurleyRelative),
tell(speigler, SpeiglerTie, SpeiglerRelative).
% Succeeds if all elements of the argument list are bound and different.
% Fails if any elements are unbound or equal to some other element.
all_different([H | T]) :- member(H, T), !, fail.
all_different([_ | T]) :- all_different(T).
all_different([_]).
tell(X, Y, Z) :-
write('Mr. '), write(X), write(' got the '), write(Y),
write(' tie from his '), write(Z), write('.'), nl.
After consulting the file, just type the name of the predicate, with the arguments in parentheses (if there are any), followed by a period. If you have more than one predicate in your query, separate them by commas. In your case (I called the file solve.pl):
?- [solve].
% solve compiled 0.00 sec, 18 clauses
true.
?- solve.
Mr. crow got the cupids tie from his daughter.
Mr. evans got the leprechauns tie from his uncle.
Mr. hurley got the reindeer tie from his sister.
Mr. speigler got the happy_faces tie from his father_in_law.
true ;
false.
?-

Is a predicate with variable arity acceptable in Prolog?

Is it possible to have a "variable arity predicate" in Prolog?
I mean something like this:
my_predicate( [a,b,c], [a,c], [a], [a,b,c,d], N, RESULT)
with the number of initial lists unknown at the beginning?
Using the univ operator ( =.. ) it would be possible to unify it with a list of terms and traversing it like every other list. But how to write the goal?
my_predicate(??) =.. [??]
I really don't know if this is even possible..
you can define predicates with different arities that have the same name but they will be different predicates.
foo(1).
foo(2,1).
?-foo(2).
false
my suggestion is to change the encoding; instead of a number of initial lists, have a list of initial lists.
the other solution would be to write predicates for all the possible numbers of arguments (or dynamically generate them).
As #thanosQR suggests, it probably is the best to change your representation to some list.
There are, however - very seldom but nevertheless - situations where you want to define a predicate for many different arities. In this very rare case, you can define such a predicate manually. That is, for each arity manually. Of course, you will only define several cases. As an example, see library(lambda).
You always can go up to one level :) Many years ago I saw implementation of ANSI prolog interpter in Turbo Prolog. Idea was very simple, enclose all user-space facts and rules in single fact backed by assert/retract-like operations.
Consider enclosing all your targets in another compose:
target(my_predicate( [a,b,c], [a,c], [a], [a,b,c,d], N, RESULT)) :- RESULT=[a], N=1.
target(H) :- H =.. [my_predicate|_].
target(using_my_predicate(X, Y)) :- target(my_predicate(X,1,Y)).
Some prologs (at least YAP) have directives to declare handlers for unknown targets:
:- module(sumtest).
target(sum(0)).
target(H) :-
H =.. [sum, S, X|XS],
H1 =.. [sum, S1|XS],
H1,
S is (S1+X).
target(sumtest:G):- target(G). % HACK: strip-off module
:- unknown(_, target(_)).
test:-
sum(X,1), write(X), nl,
sum(Y,2,3), write(Y), nl,
sum(Z,3,4,2), write(Z), nl,
target(sum(X1,1)), write(X1), nl,
target(sum(Y1,2,3)), write(Y1), nl,
target(sum(Z1,3,4,2)), write(Z1), nl.
:- test, halt.
% % yap -l sumtest.pl
% YAP 6.2.0 (amd64): Thu Oct 21 10:31:27 EEST 2010
% MYDDAS version MYDDAS-0.9.1
% 1
% 5
% 9
% 1
% 5
% 9
% % YAP execution halted

Resources