Prolog: Stating more facts in less statements - prolog

I have a problem in Prolog and I really really need help with it.
There are people (male and female) and they have propterties:
Name
Gender
Age
Weight
Hair color
and
favourite song
favourite sport
favourite book
I need state these facts about a person, but I am not supposed to state more than 3 facts!
could someone help me?

Here's an easy way:
person(PersonName, Gender, Age, Weight, HairColor).
favorite(PersonName, Song, Sport, Book).
There's no reason you have to limit yourself to arity-1 or -2 facts. You can design your Prolog fact database the same way you would design a relational database. Prolog will index the first term of the fact automatically anyway, so make that a candidate key and observe as your program performs well despite the apparent searching. :)
Edit: To illustrate a few queries:
person(maria, female, 28, 61, blond).
Is there a person named Maria?
?- person(maria, _, _, _, _).
yes.
What is Maria's favorite sport?
?- favorite(maria, _, Sport, _).
Sport = tennis ;
no.
etc. For more details, see #mbratch's comment below.

Related

Understanding how the member function works

I am working on a Prolog program. I am given a bunch of facts in the form fan(name, list of novels liked). I am writing program that will tell the names of the books that three people, sally, mark and rob have in common without repeating the names of the book. I understand how to get the intersection between two people (shown below), but I don't know how to proceed from here.
mutual_novels(Book) :-
fan(sally,S),
fan(mark,M),
fan(rob,R),
novel(Book, _),
member(Book,P),
member(Book,R).
You're on the good way, Prolog will unify directly the Book variable, so you only have to add one member clause more:
mutual_novels(Book) :-
fan(sally,S),
fan(mark,M),
fan(rob,R),
novel(Book, _),
member(Book,S),
member(Book,R),
member(Book,M).
The predicate will give you only the instances Book that assert it (member of the three lists at the same time).

SWI Prolog - Using a predicate with another predicate

I'm relatively new to prolog, and I'll try and explain this the best I can. Say that I have a small knowledge base of restaurants, where it has the name, cuisine, and price.
restaurant(spaghetti, italian, 20).
restaurant('naan bread', indian, 30).
...
And I have some people that like certain restaurants, such as:
likes(adam, restaurant, italian).
Where Adam likes italian restaurants. The main issue I'm having is that if I do a query such as:
likes(adam, spaghetti).
or
likes(adam, _, spaghetti).
It only comes up with false, no matter what I put. I have done a lot of research, but can't seem to get it to work - since I'm newish at prolog I don't really understand it. I have looked at books like 'Programming in Prolog' by Clocksin and Mellish and various websites, but I can't seem to find an answer or one I understand.
Based on your comment
It's only a knowledge base of restaurants. So there aren't any other rules.
we'll take it from there and start with the given rules.
restaurant(spaghetti, italian, 20).
restaurant('naan bread', indian, 30).
Next is the query you are trying
likes(adam, spaghetti).
Which is valid, but as we note in the comments some facts are missing.
The simplest fact to make the query correct would be
likes(adam, spaghetti).
but you have other queries like
likes(adam, restaurant, italian).
and facts like
restaurant('naan bread', indian, 30).
which suggest that you are aware that there are relationships between the four entities, adam, itialian and spaghetti, 20 (price).
There are endless ways to make relationships but for this example we will keep it more on the simple side.
person(adam).
person(mary).
food_nationality(spaghetti, italian).
food_nationality(hamburger, americian).
food_nationality('naan bread', indian).
food_price(spaghetti, 20).
food_price(hamburger, 30).
food_price('naan bread', 30).
likes(adam,italian).
likes(mary,american).
Now that we have some facts if you wanted to know what it cost for adam to eat a food he likes we start with the facts and see what we can conclude.
We see the fact
person(adam).
but that only tells us adam is a person and doesn't lead to any more information for our question.
We also see the fact
likes(adam,italian).
which tells us that adam likes italian but doesn't give us a specific food.
We also see
food_nationality(spaghetti, italian).
So we know adam likes italian and italian has spagehetti but we still need a price.
We also see
food_price(spaghetti, 20).
So we know adam likes italian and italian has spagehetti and spaghetti cost 20. So the answer is that for adam to eat something he likes it will cost 20.
As a Prolog predicate it would be
cost_to_eat(Person,Price) :-
likes(Person,Nationality),
food_nationality(Food, Nationality),
food_price(Food, Price).
and running that for adam
?- cost_to_eat(adam,Price).
Price = 20.
It also works for mary
?- cost_to_eat(mary,Price).
Price = 30.
and it also works if you give just a price
?- cost_to_eat(Person,20).
Person = adam ;
false.
and it also works it you ask
?- cost_to_eat(Person,Cost).
Person = adam,
Cost = 20 ;
Person = mary,
Cost = 30.
So let's just say you have the following facts, as suggested in your question, with no extra and no changes:
restaurant(spaghetti, italian, 20).
restaurant('naan bread', indian, 30).
likes(adam, restaurant, italian).
Now you'd like to ask the question "What dish does Adam like?"
We can add this simple rule:
likes(Person, Dish) :-
likes(Person, restaurant, Type),
restaurant(Dish, Type, _).
Now you can go ahead and ask the following:
?- likes(adam, Dish), write(Dish).
This spits out the answer spaghetti.

How to use the cut operator?

I've been learning and I came across a problem. I understood the cut operator from the tutorials but I'm trying to solve a problem and I cannot understand the solution.
Problem:
If the car color is red, made in Italy, then it's a Ferrari. If it's red but made in Germany (or any other country. could be more than one), it's Benz. If it's not red and is big, it's ford. If it's not red and not big, it's Toyota.
That is:
red & Italy: Ferrari
red & Germany (or not Italy): Benz
not red & big: ford
not red & not big: Toyota
Given some facts for a particular car object:
color(cx, red).
speed(cx, 220).
make(cx, italy).
type(cx, sport).
I want to write a predicate brand(X, name) that will return the brand of the particular car objects, like:
brand(X, ferrari):-
color(X,red), make(X,T), T=italy.
brand(X, benz) :-
color(X,red), not(make(X,italy)).
brand(X, ford) :-
not(color(X,red)), size(X,big).
brand(X, toyota) :-
not(color(X,red)), not(size(X,big)).
Question is how (and where) do I use the cut operator here so that it doesn't check the same property (eg: here "make") twice? I can't seem to wrap my head around this.
If I check for red and then for make, if the make turns out not italy, how do I write the brand(X, brand_name) for a set of facts for car object "ck" such that it doesn't check the make again? It appears impossible to me.
The short answer: Don't.
Almost invariably, using !/0 will lead to loss of valid solutions.
Always keep in mind that Prolog programs are typically much more general than programs in other languages. In particular, keep in mind that users can always post the most general query, which in your case is:
?- brand(X, Y).
If you have !/0 in this definition, then yes, you will likely "check only once", but on the other hand, you will fail to generate all valid answers.
One good way out is to use if_/3 from library(reif). For example, in your case, you can write the facts as:
color(cx, red, true).
speed(cx, 220, true).
make(cx, italy, true).
type(cx, sport, true).
And now you can write:
car_type(C, Type) :-
if_(color(C,red),
if_(make(C, italy), Type=ferrari, Type=bentz),
if_(type(C, big), Type=ford, Type=toyota)).
Importantly, the most general query will still work even if there are multiple solutions:
?- car_type(C, Type).
C = cx,
Type = ferrari.

Solve riddle in prolog

I want to solve this riddle in prolog:
The students Lily, Jack and Daisy go to the same university. All of them come from a different country and have different hobbies. They all go to a university in the USA, where one of them is living. Lily has better grades then the one who comes from Italy. Jack has better grades then the one who likes reading books. The best grades has the one who likes football. Jack comes from Germany and Daisy likes cooking.
Who is who (name, country, hobby, grades)?
The correct solution should be:
Lily, USA, Reading Books, Grade 2
Jack, Germany, Football, Grade 1
Daisy, Italy, Cooking, Grade 3
The Problem I have right now is that I don't know how I could solve this riddle. How should I define the facts and what's the best way to solve the riddle?
The trick to answer these puzzle questions in Prolog is to generate (retrieve) possible answers and then test them against the logical constraints. So, if Lily is person P1, then retrieve any person P2 and test if that person is from italy. And so forth with the other rules.
That means, in first instance, you need some clauses with possible countries, possible hobbies and possible grades. Not all possibilities are necessary, because some are already ruled out by the question.
The solution below, based on arbitrarily making Lily person 1, Jack person 2 and Daisy person 3.
Load in to Prolog and query who(P1,C1,H1,G1, P2,C2,H2,G2, P3,C3,H3,G3).
country(italy).
country(usa).
hobby(football).
hobby(reading).
grade(c:1).
grade(b:2).
grade(a:3).
who(lily,C1,H1,Grade1, jack,germany,H2,Grade2, daisy,C3,cooking,Grade3):-
country(C1), country(C3), C1 \= C3,
hobby(H1), hobby(H2), H1 \= H2,
grade(G1:Grade1), grade(G2:Grade2), grade(G3:Grade3),
G1 \= G2, G2 \= G3, G1 \= G3,
(C3=italy, G1#>G3),
(H1=reading, G2#>G1),
((H1=football, G1#>G2, G1#>G3); (H2=football, G2#>G1, G2#>G3)).
First, from filling in what we get from the first statement, we have the following.
(Lily, _, _, _)
(Jack,Germany, _, _)
(Daisy, _, Cooking, _)
Where the _ state we don't know something. I should also say that this isn't necessarily prolog, it's more common sense than anything.
We get the phrase "Lily has better grades then the one who comes from Italy", this means that Daisy is from Germany and Lily is from the USA- since Jack's from Germany.
(Lily, USA, _, Grade>Daisy)
(Jack,Germany, _, _)
(Daisy, Italy, Cooking, Grade<Lily)
Next, we have "Jack has better grades then the one who likes reading books", which gives us the fact that he would be the football player, and the next line tells us he has the best grade. We can then promptly fill up the remained and we get:
(Lily, USA, Reading, Grade2)
(Jack,Germany, Football, Grade1)
(Daisy, Italy, Cooking, Grade3)
There could be a program written in prolog that can solve this puzzle in a very roundabout way, but this puzzle is more specific than a general case.
Here is my take. It is essentially what #RdR has, just that I broke up the logic into more predicates, also to have a less overloaded who() main predicate.
name(lily). % (1)
name(jack).
name(daisy).
country(italy).
country(usa).
country(germany).
hobby(football).
hobby(cooking).
hobby(reading).
grade(1).
grade(2).
grade(3).
student(N,C,H,G):- % (2)
name(N), country(C), hobby(H), grade(G).
permute(P,X,Y,Z):- (4)
call(P,X), call(P,Y), call(P,Z) % (6)
, X\=Y, Y\=Z, X\=Z.
students(A,B,C):- (3)
permute(name,N1,N2,N3) % (5)
, permute(country,C1,C2,C3)
, permute(hobby,H1,H2,H3)
, permute(grade,G1,G2,G3)
, A = student(N1,C1,H1,G1) % (7)
, B = student(N2,C2,H2,G2)
, C = student(N3,C3,H3,G3)
.
who(A,B,C):- % (8)
students(A,B,C)
, A = student(lily,C1,H1,G1) % (9)
, B = student(jack,C2,H2,G2)
, C = student(daisy,C3,H3,G3)
, C2 = germany % (10)
, H3 = cooking
, (( C2=italy -> G1 < G2) % (11)
;( C3=italy -> G1 < G3))
, (( H1=reading -> G2 < G1)
;( H3=reading -> G2 < G3))
, (( H1=football -> G1 < G2, G1 < G3)
;( H2=football -> G2 < G1, G2 < G3)
;( H3=football -> G3 < G1, G3 < G2))
.
% Running it:
% ?- who(A,B,C).
% A = student(lily, usa, reading, 2),
% B = student(jack, germany, football, 1),
% C = student(daisy, italy, cooking, 3) ;
% false.
Discussion
So there is quite a bit going on here (which intrigued me), and several choices that could be made differently (hence it is probably a nice contrast to #RdR's solution).
As others pointed out one aspect is how to encode the information that is
given in the problem description. You can express it very concretely (solving
just this case), or be more general (to e.g. allow extending the problem to
more than 3 students).
What makes this problem different from similar others is that you have a mix
of constraints that affect either a singel student ("Jack comes from
Germany"), affect two students ("Lily's grades are better than the one from
Italy"), or involves all of them ("The one liking football has the best
grades").
Moreover, you have disjunction contraints ("They are all from different
contries, and have different hobbies"). Prolog is very good at going through
all the possible instances of a fact, but it is more complicated to have it
pick one instance and leave this one out for the next call of the predicate.
This forces you to find a way to get a set of values from a fact that are
pairwise distinct. (E.g. when Prolog settles for Lily's hobby being reading,
it mustn't also assign reading as Jack's hobby).
So after listing all known facts and their possible values (1) I first
defined a predicate student/4 (2) to simply state that a student has these
4 properties. This produces all possible combination of students and their
attributes, also allowing that they all have the same name, come from the
same country, asf.
It is a good example how to create a result set in Prolog that is way too
large, and then try to narrow it further down (like someone else wrote).
Further predicates could make use of this "generator" and filter more and more
solutions from its result set. This is also easier to test, on each stage you
can check if the intermediate output makes sense.
In a next predicate, students/3 (3), I try exactly what I mentioned
earlier, creating student instances that at least don't use the same
attribute twice (like two students having the same hobby). To achive this I
have to run through all my attribute facts (name/1, country/1, ...), get
three values for each, and make sure they are pairwise distinct.
To not having to explicitly do this for each of the attributes where the
implementation would be always the same except for the name of the
attribute, I constructed a helper predicate permute/4 (4) that I can pass
the attribute name and it will look up the attribute as a fact three times,
and makes sure the bound values are all not the same.
So when I call permute(name,N1,N2,N3) in students/3 (5) it will result in
the lookups call(P,X), call(P,Y), call(P,Z) (6) which results in the same as
invoking name(X), name(Y), name(Z). (As I'm collecting 3 different values
from always 3 facts of the same attribute this is effectively the same as doing the 3-permutations
of a 3-value set, hence the name of helper predicate.)
When I get to (7) I know I have distinct values for each student attribute,
and I just distribute them across three student instances. (This should
actually work the same without the student/4 predicate as you can always
make up structured terms like this on the fly in Prolog. Having the student
predicate offers the additional benefit of checking that no foolish students
can be constructed, like "student(lily, 23, asdf, -7.4)".)
So :- students(A,B,C). produces all possible combinations of 3 students and
their attributes, without using any of the involved attributes twice. Nice.
It also wraps the (more difficult) student() structures in handy
single-letter variables which makes the interface much cleaner.
But aside from those disjointness constraints we haven't implemented any of
the other constraint. These now follow in the (less elegant) who/3
predicate (8). It basically uses students/3 as a generator and tries to
filter out all unwanted solutions by adding more constraints (Hence it has
basically the same signature as students/3.)
Now another interesting part starts, as we have to be able not only to filter
individual student instances, but also to refer to them individually
("Daisy", "Jack", ...) and their respective attributes ("Daisy's hobby"
etc.). So while binding my result variable A, B and C, I pattern-match on
particular names. Hence the literal names lily, jack asf from (9). This
relieves me from considering cases where Lily might come in first, or as
second, or as third (as students/3 would generate such permutations). So
all 3-sets of students that don't come in that order are discarded.
I could just as well have done this later in an explicit constraint like N1 =
lily asf. I do so now enforcing the simple facts that Jack is from Germany
and Daisy likes cooking (10). When those fail Prolog backtracks to the
initial call to students/3, to get another set of students it can try.
Now follow the additional known facts about Lily's grades, Jack's grades, and
the grades of the football lover (11). This is particularly ugly code.
For one, it would be nice to have helper predicate that would be able return
the answer to the query "the student with attribute X". It would take the
current selection of students, A, B and C, an attribute name ('country') and a
value ('italy') and would return the appropriate student. So you could just
query for the student from Italy, rather than assuming it must be the second
OR the third student (as the problem description suggests that Lily herself
is not from Italy).
So this hypothetical helper predicate, let's call it student_from_attribute
would need another helper that finds a value in a student structure by name
and return the corresponding value. Easy in all languages that support some
kind of object/named tuple/record where you can access fields within it by
name. But vanilla Prolog does not. So there would be some lifting to be done,
something that I cannot pull off of the top of my head.
Also the who/3 predicate could take advantage of this other helper, as you
would need a different attribute from the student returned from
student_from_attribute, like 'grade', and compare that to Lily's grade.
That would make all those constraints much nicer, like
student_from_attribute([A,B,C], country, italy, S), attrib_by_name(S, grade,
G), G1 < G. This could be applied the same way to the reading and football
constraint. That would not be shorter, but cleaner and more general.
I'm not sure anybody would read all this :-). Anyway, these considerations made the puzzle interesting for me.

Solving Caliban problems with prolog

I'm working on solving a logic puzzle using prolog for school. Here's the clues:
Brown, Clark, Jones and Smith are 4 substantial citizens who serve their
community as achitect, banker, doctor and lawyer, though not necessarily
respectively.
Brown, who is more conservative than Jones but more liberal than Smith,
is a better golfer than the men who are younger than he is and has a
larger income than the men who are older than Clark.
The banker, who earns more than the architect, is neither the youngest
nor the oldest.
The doctor, who is a poorer golfer than the lawyer, is less conservative
than the architect.
As might be expected, the oldest man is the most conservative and has the
largest income, and the youngest man is the best golfer.
What is each man's profession?
hint: to rank people for weath, ability, relative age, etc
use the numbers 1,2,3,4 Be careful to state whether 1 represents,
e.g., youngest or oldest. Doing this makes comparisons easy to code.
To code (as follows) interprets all the relationships, given by the clues, as a list of lists, wherein each list defines the
%[profession,surname,politics,relative_age, relative_salary, golf_ability]:
profession(L) :- L = [[_,'Brown',_,_,_,_],[_,'Jones',_,_,_,_],[_,'Clark',_,_,_,_],
[_,'Smith',_,_,_,_]],
member([_,'Brown',P1,A6,M3,G3],L),
member([_,'Jones',P2,_,_,_],L),
member([_,'Clark',_,A3,_,_],L),
member([_,'Smith',P3,_,_,_],L),
moreconservative(P1,P2),
moreliberal(P1,P3),
bettergolfer(G3,younger(_,A6)),
richer(M3,older(_,A3)),
member(['banker',_,_,A1,M1,_],L),
member(['architect',_,P5,_,M2,_],L),
richer(M1,M2),
(A1 = 2;A1 = 3),
member(['doctor',_,P4,_,_,G1],L),
member(['lawyer',_,_,_,_,G2],L),
worsegolfer(G1,G2),
moreliberal(P4,P5),
member([_,_,4,4,4,_],L),
member([_,_,_,1,_,4],L).
I define the relative_politics,relative_salary,relative_age, and golf_ability relationships like so
EG:
richer(4,1).
moreconservative(4,1).
poorer(1,4).
poorer(1,3).
And it goes on for all relationships.
I think I have faithfully translated all of the clues to prolog but it just says fail when I query the database. EG:
?- profession(L).
fail.
I am using NU Prolog. I'm wondering if I made an error in my translation of the clues or I omitted a fact that is needed for the database to satisfy all the conditions of the list L.
bettergolfer(G3,younger(_,A6)) ... it doesn't work this way, in Prolog. Instead, have this
( member( X,L), age(X,AX), golf(X,GX),
( younger(AX,A6) -> better_golfer(G3,GX) ; true )),
.....
age( [_,_,_,A,_,_],A).
golf([_,_,_,_,_,G],G).
.....
this means, all the persons (including none) that are younger than Brown, must be poorer golfers than he is.
There is a catch here, too. Since we're told about the men younger than Brown, it means there must exist at least one such man (unlike in the mathematical definition of implication). We have to code this too. For example,
( member(X,L), age(X,AX), younger(AX,A6) -> true ),
.....
(using unique names for the new logvars of course). You'll have to make the same transformation for your richer(M3,older(_,A3)).
Great idea BTW, to have the comparison predicates defined in a generative fashion:
poorer(1,2).
poorer(1,3).
poorer(1,4).
poorer(2,3).
poorer(2,4).
poorer(3,4).
richer(A,B):- poorer(B,A)
If you were to define them as arithmetic comparisons, poorer(A,B):- A<B., you could potentially run into problems with uninstantiated variables (as recently discussed here).

Resources