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.
Related
I'm trying to use Prolog to solve a simple game in which there are 3 players: Alice, Bob and Charlie. Each player secretly chooses a card to play, where cards can either be red or blue. The cards are then shuffled and turned over.
Assuming Alice's viewpoint, we know which card she played. Therefore if the revealed cards are blue, blue and red, and Alice played red, then she can deduce that both Bob and Charlie played blue. I'm struggling to define the appropriate causes for this. Here are my basic facts:
player(alice).
player(bob).
player(charlie).
color(blue).
color(red).
% The overturned cards -- all players can see these. Order is unimportant.
cards([blue, red, red]).
played(alice, blue).
From this, we should be able to deduce that Bob and Charlie each played red. I'm unsure how to tell Prolog that each of the three players played exactly one of the cards in the cards([blue, red, red]) fact. Or perhaps something along the lines of num_cards(blue, 1), num_cards(red, 2) would be better?
As a slightly more difficult example, it should be possible for the following facts to be used in deducing that Charlie played a red card (for example if Alice was able to peek at the card played by Bob):
player(alice).
player(bob).
player(charlie).
color(blue).
color(red).
% The overturned cards -- all players can see these. Order is unimportant.
cards([blue, red, red]).
played(alice, red).
played(bob, blue).
The most similar SO question I was able to find was this one, but I haven't been able to apply it to my problem. The CLPFD library definitely seems relevant.
Already for a really well-done background search: +1! The question you linked to and Boris's solution is indeed strongly related to this task.
First, I would like to point out that your task looks easy on the surface and is something that prima facie looks suitable for Prolog beginners to tackle. In my view, such tasks are not easy at all, and the difficulty quickly gets out of hand when additional knowledge (such as: who knows what, throughout different layers) must also be represented. I can well imagine that beginners who get such tasks as assignments will quickly walk away with the feeling "I could have easily solved this in Java, but it is impossible in Prolog". The fact that they could not have solved it in Java either usually does not bother them in the slightest.
In this concrete case, clpfd constraints are indeed a great fit. In fact, the global_cardinality/2 constraint which is for example available in SICStus Prolog solves both examples easily.
When using CLP(FD) constraints, the trick is to map your domain of interest to integers. In this case, I will (arbitrarily) use:
1 for blue
2 for red.
Also, I will simply use variables, one for each person, standing for the concrete "card" (i.e., integer) the person played. The idea is to specify what we know, and let the constraint solver figure out the rest.
So we have, in the first case:
?- global_cardinality([Alice,Bob,Charlie], [1-1,2-2]),
Alice = 1.
Alice = 1,
Bob = Charlie, Charlie = 2.
And in the second case:
?- global_cardinality([Alice,Bob,Charlie], [1-1,2-2]),
Alice = 2,
Bob = 1.
Alice = Charlie, Charlie = 2,
Bob = 1.
So in both cases, simply stating the given constraints suffices to deduce the single respective solution, just like you expected.
my hint needs a 'syntactical adaptation' to fit your question: from my viewpoint, it's essential to separate the facts expressing a particular query from entities identities: so, I propose a game/2 assessing the relation between a set of cards (those overturned, of course) and an association of player and played card, expressed as Player-Card:
player(alice).
player(bob).
player(charlie).
color(blue).
color(red).
game(Cs, [P-C|Ps]) :-
player(P), color(C),
select(C, Cs, Other),
game(Other, Ps),
\+ memberchk(P-_, Ps).
game([], []).
now, a lot of specific queries can be answered. For example
game([blue, red, red], [alice-blue,bob-B,charlie-C]).
B = C, C = red ;
B = C, C = red ;
false.
or
?- so:game([blue, red, red], [alice-blue,X,Y]).
X = bob-red,
Y = charlie-red ;
...
or
?- game([blue, red, red], [alice-red, bob-blue, Y]).
Y = charlie-red ;
...
To avoid duplicates solutions, setof/3 can be used.
I got a database that looks like
hasChild(person1, person2).
hasChild(person1, person3).
hasChild(person4, person5).
Which means that (for example) person1 has child named person2.
I then create a predicate that identifies if the person is a parent
parent(A):- hasChild(A,_).
Which identifies if the person is a parent, i.e. has any children
Then I try to create a predicate childless(A) that should return true if the user doesn't have any children which is basically an inverse of parent(A).
So I have 2 questions here:
a) is it possible to somehow take an "inverse" of a predicate, like childless(A):-not(parent(A)). or in any other way bypass this using the hasChild or any other method?
b) parent(A) will return true multiple times if the person has multiple children. Is it possible to make it return true only once?
For problem 1, yes. Prolog is not entirely magically delicious to some because it conflates negation and failure, but you can definitely write:
childless(X) :- \+ hasChild(X, _).
and you will see "true" for people that do not have children. You will also see "true" for vegetables, minerals, ideologies, procedures, shoeboxes, beer recipes and unfeathered bipeds. If this is a problem for you, a simple solution is to improve your data model, but complaining about Prolog is a very popular alternative. :)
For problem 2, the simplest solution is to use once:
parent(A) :- once(hasChild(A, _)).
This is a safer alternative to using the cut operator, which would look like this:
parent(A) :- hasChild(A, _), !.
This has a fairly significant cost: parent/1 will only generate a single valid solution, though it will verify other correct solutions. To wit:
?- parent(X).
X = person1.
Notice it did not suggest person4. However,
?- childless(person4).
true.
This asymmetry is certainly a "code smell" to most any intermediate Prolog programmer such as myself. It's as though Prolog has some sort of amnesia or selective hearing depending on the query. This is no way to get invited to high society events!
I would suggest that the best solution here (which handles the mineral/vegetable problem above as well) is to add some more facts about people. After all, a person exists before they have kids (or do they?) so they are not "defined" by that relationship. But continuing to play the game, you may be able to circumvent the problem using setof/3 to construct a list of all the people:
parent(Person) :-
setof(X, C^hasChild(X, C), People),
member(Person, People).
The odd expression C^hasChild(X, C) tells Prolog that C is a free variable; this ensures that we get the set of all things in the first argument of hasChild/2 bound to the list People. This is not first-order logic anymore folks! And the advantage here is that member/2 will generate for us as well as check:
?- parent(person4).
true.
?- parent(X).
X = person1 ;
X = person4.
Is this efficient? No. Is it smart? Probably not. Is it a solution to your question that also generates? Yes, it seems to be. Well, one out of three ain't bad. :)
As a final remark, some Prolog implementations treat not/1 as an alias for \+/1; if you happen to be using one of them, I recommend you not mistake compatibility with pre-ISO conventions for a jovial tolerance for variety: correct the spelling of not(X) to \+ X. :)
Here's another way you could do it!
Define everything you know for a fact as a Prolog fact, no matter if it is positive or negative.
In your sample, we define "positives" like person/1 and "negatives" like childless/1. Of course, we also define the predicates child_of/2, male/1, female/1, spouse_husband/2, and so on.
Note that we have introduced quite a bit of redundancy into the database.
In return, we got a clearer line of knowns/unknowns without resorting to higher-order constructs.
We need to define the right data consistency constraints:
% There is no person which is neither male nor female.
:- \+ (person(X), \+ (male(X) ; female(X))).
% Nobody is male and female (at once).
:- \+ (male(X), female(X)).
% Nobody is childless and parental (at once).
:- \+ (childless(X), child_of(_,X)).
% There is no person which is neither childless nor parental.
:- \+ (person(X), \+ (childless(X) ; child_of(_,X))).
% There is no child which is not a person.
:- \+ (child_of(X,_), \+ person(X)).
% There is no parent which is not a person.
:- \+ (child_of(_,X), \+ person(X)).
% (...plus, quite likely, a lot more integrity constraints...)
This is just a rough sketch... Depending on your use-cases you could do the modeling differently, e.g. using relations like parental/1 together with suitable integrity constraints. YMMY! HTH
I am working on a dictionary-like program with prolog, and my code goes like this:
define(car,vehicle).
define(car,that).
define(car,has).
define(car,four).
define(car,wheels).
define(wheels,round).
define(wheels,object).
define(wheels,used).
define(wheels,in).
define(wheels,transportation).
defined(X):-define(X,_).
anotherdefined(X):- \+ undefined(X).
undefined(X):- \+define(X,_).
I am trying to write a defined/1 predicate which will give me:
?-defined(X).
X = car ;
X = wheels ;
false.
Yet, my defined/1 gives me X=car. five times (naturally) for everytime it counters define(car,_).
and my anotherdefined/1 gives me only true. What is the method to stop prolog backtracking to the other instances of define(car,_).,and skip to define(wheels,_).?
Edit: I have written the following lines to get the result I want with givedefinedword/1,
listdefined(X):-findall(Y,defined(Y),Z),sort(Z,X).
givedefinedword(X):-listdefined(List),member(X,List).
However since I wanted an efficient predicate (which I will use in many others) it beats the purpose. This predicate does too much process.
Or, Would it be better to use a predicate that modifies the code? say prepares a list of defined words, and modifies it when new definitions are added.
Thanks.
If you change define to relate items and lists, like
definelist(car, [vehicle, that, has, four, wheels]).
% etc.
defined(X) :- definelist(X, _).
then defined will no longer produce duplicates, nor require linear space.
Of course, a query define(X, Y) must now be performed as definelist(X, L), member(Y, L). If you want this to be efficient as well, you may need to duplicate all definitions.
What are you trying to achieve with your program? It seems that you want to have facts in the form:
"A car is a vehicle that has four wheels"
"Wheels are round objects used in transportation" (a bit vague)
How are you going to use these facts? #larsmans suggestion if perfectly fine, if you want to just have your statement as a "sentence". It really depends what you will do with the information though.
Consider structuring the information in your database:
is(car, vehicle).
is(bicycle, vehicle).
is(boat, vehicle).
has(car, wheel(four)).
has(car, motor).
has(bicycle, wheel(two)).
Given this database, you can at least ask a question like, "what vehicles are there?", "does a bicycle have a motor?", or maybe, "how many wheels does a car have?", or "which vehicles have no wheels?"
?- is(X, vehicle).
?- has(bicycle, motor).
?- has(car, wheel(N)).
?- is(X, vehicle), \+ has(X, wheel(_)).
and so on.
Once you have defined your problem better, you can define your data structures better, which will make writing a program to solve your problem easier.
For example if there are facts of who likes which color and everyone likes green, but not everyone likes pink: likes(X, green) is true for all X, can Prolog somehow give a result that likes(X, green) is true for all X-es?
Lets say that there's also information about the gender of people within the rules in the form female(deby). male(robert). If according to the data set all females but not all males like pink, can Prolog return something like this: likes(X, pink) :-female(X)
Edit: When I say "I want to ask" in the last paragraph, I mean my query to Prolog should have this meaning.
To be clear I don't want to specify what Prolog should check. In English I don't want to ask, whether everyone likes green, but I want to ask: Can you make new rules based on the facts available?
facts:
male(albert).
male(brett).
female(chloe).
female(deby).
likes(albert, green).
likes(brett, green).
likes(chloe, green).
likes(deby, green).
likes(albert, pink).
likes(chloe, pink).
likes(deby, pink).
If you want to check if everyone likes green, then you just need to define what everyone means. Let's suppose it means "all the males and females". Then you could do this:
everyone_likes(Color) :-
everyone(Everyone),
all_likes(Everyone, Color).
everyone(Everyone) :-
findall(E, (male(E) ; female(E)), Everyone).
all_likes([Person|People], Color) :-
likes(Person, Color),
all_likes(People, Color).
all_likes([], _).
Option 2
An alternative to the above is to think about the logic in the negative. If everyone likes green, that is the opposite of someone doesn't like green. Expressing that in Prolog:
everyone_likes(Color) :-
is_color(Color),
\+ ( (male(P) ; female(P)),
\+ likes(P, Color)
).
Rather than having to define who everyone is in this version, we need an is_color fact and/or predicate which defines a valid color (if we want the everyone_likes(X) query to work). This could be define simply as:
is_color(pink).
is_color(green).
is_color(orange).
...
Or, you could define it as a predicate which derives the choices of color from other facts.
Yes, you can make new rules based on the facts available but I would suspect that it's very dependent on specific Prolog implementation. If you're working on SWI-Prolog, you can look at chapter 4.13 ("Database") in documentation and see how to use constructing predicates: assert/1, clause/2, clause/3 and others. It might be helpful to see description for copy_predicate_clauses/2.
Clue
Four guests (Colonel Mustard, Professor Plum, Miss Scarlett, Ms. Green) attend a dinner party at the home of Mr. Boddy. Suddenly, the lights go out! When they come back, Mr Boddy lies dead in the middle of the table. Everyone is a suspect. Upon further examination, the following facts come to light:
Mr Boddy was having an affair with Ms. Green.
Professor Plum is married to Ms. Green.
Mr. Boddy was very rich.
Colonel Mustard is very greedy.
Miss Scarlett was also having an affair with Mr. Boddy.
There are two possible motives for the murder:
Hatred: Someone hates someone else if that other person is having an affair with his/her spouse.
Greed: Someone is willing to commit murder if they are greedy and not rich, and the victim is rich.
Part A: Write the above facts and rules in your Prolog program. Use the following names for the people: colMustard, profPlum, missScarlet, msGreen, mrBoddy. Be careful about how you encode (or don’t encode) symmetric relationships like marriage - you don’t want infinite loops! married(X,Y) :- married(Y,X) % INFINITE LOOP
?-suspect(Killer,mrBoddy)
Killer = suspect_name_1
Killer = suspect_name_2
etc.
Part B: Write a predicate, suspect/2, that determines who the suspects may be, i.e. who had a motive.
?-suspect(Killer,mrBoddy)
Killer = unique_suspect.
Part C: Add a single factto your database that will result in there being a unique suspect.
Clearly indicate this line in your source comments so that it can be removed/added for
grading.
?-suspect(Killer,mrBoddy)
Killer = unique_suspect.
Whenever I type in
suspect(Killer,mrBoddy).
I get
suspect(Killer,mrBoddy).
Killer = profPlum
I'm missing
Killer = colMustard.
Here's my source.
%8) Clue
%facts
affair(mrBoddy,msGreen).
affair(missScarlett, mrBoddy).
affair(X,Y) :- affair(X,Y), affair(Y,X).
married(profPlum, msGreen).
married(X,Y) :- married(X,Y), married(Y,X).
rich(mrBoddy).
greedy(colMustard).
%rules
hate(X,Y) :- married(X,Spouse), affair(Y,Spouse).
greed(X,Y) :- greedy(X), not(rich(X)), rich(Y).
%suspect
suspect(X,Y):- hate(X,Y).
suspect(X,Y):- greed(X,Y).
There are two kinds of problems with your program. One is on the procedural level: you observed that Prolog loops; the other is on the logical level — Prolog people call this rather the declarative level. Since the first annoying thing is this endless loop, let's first narrow that down. Actually we get:
?- suspect(Killer,mrBoddy).
Killer = profPlum ;
ERROR: Out of local stack
You have now several options to narrow down this problem. Either, go with the other answer and call up a tracer. While the tracer might show you the actual culprit it might very well intersperse it with many irrelevant steps. So many that your mind will overflow.
The other option is to manually modify your program by adding goals false into your program. I will add as many false goals as I can while still getting a loop. The big advantage is that this way you will see in your source the actual culprit (or to be more precise one of potentially many such culprits).1 After trying a bit, this is what I got as failure-slice:
?- suspect(Killer,mrBoddy), false.
married(profPlum, msGreen) :- false.
married(X,Y) :- married(X,Y), false, married(Y,X).
hate(X,Y) :- married(X,Spouse), false, affair(Y,Spouse).
suspect(X,Y):- hate(X,Y), false.
suspect(X,Y):- false, greed(X,Y).
All remaining parts of your program were irrelevant, that is, they are no longer used. So essentially the rule
married(X,Y) :- married(X,Y), married(Y,X).
is the culprit.
Now, for the declarative part of it. What does this rule mean anyway? To understand it, I will interpret :- as an implication. So provided what is written on the right-hand side is true, we conclude what is written on the left-hand side. In this case:
Provided X is married to Y and Y is married to X
we can conclude that
X is married to Y.
This conclusion concluded what we have assumed to be true anyway. So it does not define anything new, logically. You can just remove the rule to get same results — declaratively. So married(profPlum, msGreen) holds but married(msGreen, profPlum) does not. In other words, your rules are not correct, as you claim.
To resolve this problem, remove the rule, rename all facts to husband_wife/2 and add the definition
married(M,F) :- husband_wife(M,F).
married(F,M) :- husband_wife(M,F).
So the actual deeper problem here was a logical error. In addition to that Prolog's proof mechanism is very simplistic, turning this into a loop. But that is not much more than a welcome excuse to the original logical problem.2
Footnotes:1 This method only works for pure, monotonic fragments. Non-monotonic constructs like not/1 or (\+)/1 must not appear in the fragment.
2 This example is of interest to #larsmans.
The problem is the recursive rules of the predicates affair/2 and married/2. Attempting to use them easily leads to an endless loop (i.e. until the stack memory is exhausted). You must use a different predicate in each case to represent that if X is having an affair with Y, then Y is having an affair with X. You also need to change your definition of the suspect/2 predicate to call those new predicates.
To better understand why you get an endless loop, use the trace facilities of your Prolog system. Try:
?- trace, suspect(Killer, mrBoddy).
and go step by step.