How to use a rule correctly in Prolog - syntax

I have begun on a short journey of trying to understand how Prolog works, I need some help trying to figure out how to approach a problem and what my code is actually doing. I want to build a sentence from prolog and just as a super basic example that doesn't make much sense I want to compose a sentence like 'you are' and 'you art'.
These are the predicates I have:
line(you,[first, type]).
line(thee,[first,old]).
line(thou,[first, new]).
line(are, [second, word]).
line(art, [second, word]).
line(aurt, [second, place]).
Then I created a rule (which I know is wrong but I don't know why):
line(A, [composed, type]):-
line(B, [first, type]),
line(C, [second, word]),
append([B,C],A).
Typing into commandline:
?- line(A, [composed, type]).
false.
But what is not intuitive to me is that typing something like:
?- line(A, [first,type]).
A = you ;
false.
?- line(A, [second,word]).
A = are ;
A = art ;
false.
Give me the words I want. Can someone please help me better understand the way I should compose a rule such that my expected result is something like:
findall(X, line(X, [composed, type]),Y).
Y= you are;
Y= you art;
false.
I hope this makes a little sense and I didn't completely mess up the Prolog syntax in that last block of code. Thank you in advance.

I'm going to show you the proper way to approach this problem, which is with DCGs:
sentence --> noun, verb.
noun --> [you].
verb --> [are].
verb --> [art].
Now you can generate sentences simply by using the DCG driver phrase/2:
?- phrase(sentence, X).
X = [you, are] ;
X = [you, art].
What you have up there, with [first, type] and [second, word], I am having some trouble understanding. But DCGs are a great way to do light NLP tasks like this.

Related

Is there a way to tell Prolog that the argument X of a specific predicate f(X) is always true if the predicate is true?

Apologies if this is a silly question, but I haven't been able to find an answer.
Suppose I have some predicate, proven_true(X), where X is some sort of factual statement like person(bob). Is there any way to tell Prolog that if proven_true(X) is true, then X itself is also true? Say I define proven_true(X) as
proven_true(X) :- condition_1(X), condition_2(X) ... condition_n(X).
and in my facts, all of the above conditions are true for X = person(bob). Then I not only want proven_true(person(bob)) to be true, but also person(bob) to be true.
Obviously for a specific X this would be doable, but I couldn't get it to work for variable X. My first try was something along the lines of
X :- f(x).
but that didn't work because I was treating the head of the rule itself as a variable.
Thanks in advance for any assistance!
Edit:
To clear up some confusion, suppose my code was:
proven_true(X) :- condition_1(X), condition_2(X).
condition_1(dog(fido)).
condition_2(dog(fido)).
Then I could query proven_true(dog(X)) and get fido, but if I queried dog(X), I wouldn't get a result. So if I then wanted to use the fact that fido is a dog as a condition for another rule, I'd have to wrap it in the proven_true() predicate, e.g.:
barks(X) :- proven_true(dog(X)).
What I would like is some way to have X always be true if proven_true(X) is also true. That way, I could write the above rule as
barks(X) :- dog(X).
For a specific term like dog(X), I could achieve this using
dog(X) :- proven_true(dog(X)).
but I'd like to be able to achieve it for all terms. Something like
X :- proven_true(X).
(although this doesn't work). Hopefully that clears up confusion.
You want asserta/1 or assertz/1. It will modify the Prolog database during run-time.
proven_true(X) :- condition_1(X), condition_2(X), assertz(X).
condition_1(dog(fido)).
condition_2(dog(fido)).
assertz adds the assertion at the end of the database.

Prolog Cut operator

I defined my knowledge base as:
edge(mammal,isa,animal).
edge(human,isa,mammal).
edge(simba,isa,human).
edge(animal,swim,bybirth).
edge(human,swim,mustlearn).
path(X,Y) :- edge(X,isa,Y).
path(X,Y) :- edge(X,isa,Z), path(Z,Y).
swim(X,Y) :- edge(X,swim,Y).
swim(X,Y) :- path(X,Z), swim(Z,Y).
Now, to use the above knowledge base, I use the following:
?- swim(simba,bybirth).
?- swim(simba,mustlearn).
And for both the queries, Prolog returns true. I want Prolog to check for the property swim locally first, then look at the direct parent, and so on in a hierarchical fashion. And it should stop searching as soon as we know that Simba "mustlearn" to swim, and shouldn't look any further. Thus, it should return false for the first query and true for the second.
I know it has to be done by limiting backtracking. I tried using the cut and not operators, but couldn't succeed. Is there a way to achieve this?
I tried it and ran into a problem too. I thought this might work:
swim(X,Y) :- once((edge(X,swim,Y); path(X,Z), swim(Z,Y))).
It doesn't work, because if Y is already instantiated on the way in, the first step will fail to unify and it will try the second route going through the human intermediate. So even though the query only produces one result, it can be fooled into producing swim(simba, bybirth). The solution is to force Prolog to commit to a binding on another variable and then check that binding after the commitment:
swim(X,Y) :-
once((edge(X,swim,Method); path(X,Z), swim(Z,Method))),
Method = Y.
This tells Prolog, there is only one way to get to this method, so find that method, and then it must be Y. If you find the wrong method, it won't go on a search, it will just fail. Try it!

Prolog and sibling relationship?

I'm new to Prolog and having a bit of difficulty. I have:
man(ken).
man(tom).
woman(juli).
father(ken, tom).
father(ken, juli).
male(A) :- man(A).
brother(A,B) :- male(A), father(C,A), father(C,B), (A \= B).
I know the male/man is redundant, but it's part of the assignment. Anyway, when I try something like:
|?- brother(tom, juli).
I get "no" as the response. I'm sure I've made a stupid, simple mistake, but my lack of understanding is making it very hard to find. Can anyone see what my problem is?
When you enter:
|?- brother(tom, juli).
You'll see a response something like this (SWI Prolog):
true ? ;
no
| ?-
So it first responds with "true" (gives a match) and then, after you enter ; to show more solutions, it says "no" to indicate there are no further solutions. Some prolog interpreters may say "no" or "false" in this case, with the same meaning. This response from the prolog interpreters initially throws many a new prolog user.
You could, alternatively, press "enter" which just means you're done and don't want to see any further solutions:
true ?
yes
| ?-
Then you get "yes".

Prolog Relational Tracking without Lists

I am trying to get a predicate to relate from 1 fact to another fact and to keep going until a specified stopping point.
For example,
let's say I am doing a logistics record where I want to know who got a package from who, and where did they get it from until the end.
Prolog Code
mailRoom(m).
gotFrom(annie,brock).
gotFrom(brock,cara).
gotFrom(cara,daniel).
gotFrom(daniel,m).
gotFrom(X,Y) :- gotFrom(Y,_).
So what I am trying to do with the predicate gotFrom is for it to recursively go down the list from what ever point you start (ex: gotFrom(brock,Who)) and get to the end which is specified by m, which is the mail room.
Unfortunately when I run this predicate, it reads out,
Who = annie.
Who = brock.
Who = cara.
etc.etc....
I tried stepping through the whole thing but Im not sure where it goes from brock to annie, to cara and all the way down till it cycles through trues for infinity. I have a feeling that it has something to do with the wildcard in the function (_), but Im not sure how else I could express that part of the function in order for the predicate to search for the next fact in the program instead of skipping to the end.
I tried using a backcut (!) in my program but it gives me the same error.
Any help is greatly appreciated. I don't want code I just want to know what I am doing wrong so I can learn how to do it right.
Thanks.
I'm afraid this rule is meaningless:
gotFrom(X,Y) :- gotFrom(Y,_).
There is nothing here to constrain X or Y to any particular values. Also, the presence of singleton variable X and the anonymous variable _ means that basically anything will work. Try it:
?- gotFrom([1,2,3], dogbert).
true ;
true ;
What I think you're trying to establish here is some kind of transitive property. In that case, what you want is probably more like this:
gotFrom(X,Z) :- gotFrom(X, Y), gotFrom(Y, Z).
This produces an interesting result:
?- gotFrom(brock, Who).
Who = cara ;
Who = daniel ;
Who = m ;
ERROR: Out of local stack
The reason for the problem may not be immediately obvious. It's that there is unchecked recursion happening twice in that rule. We recursively unify gotFrom/2 and then we recursively unify it again. It would be better to break this into two predicates so that one of them can be used non-recursively.
got_directly_from(annie,brock).
got_directly_from(brock,cara).
got_directly_from(cara,daniel).
got_directly_from(daniel,m).
gotFrom(X,Y) :- got_directly_from(X, Y).
gotFrom(X,Z) :- got_directly_from(X, Y), gotFrom(Y, Z).
This gives us the desired behavior:
?- gotFrom(brock, Who).
Who = cara ;
Who = daniel ;
Who = m ;
false.
Notice this one is resilient to my attack of meaningless data:
?- gotFrom([1,2,3], dogbert).
false.
Some general advice:
Never ignore singleton variable warnings. They are almost always a bug.
Never introduce a cut when you don't understand what's going on. The cut should be used only where you understand the behavior first and you understand how the cut will affect it. Ideally, you should try to restrict yourself to green cuts—cuts that only affect performance and have no observable effects. If you don't understand what Prolog is up to, adding a red cut is just going to make your problems more complex.

Prolog rule help

I am new to Prolog and am experimenting around with some stuff, in particular i'm thinking about how to do a certain thing in prolog. I am aware of facts and rules, facts being something of the sort
specialCustomer(x). //person x is a specialcustomer
and rules:
totalSpend(x,500) :- specialCustomer(x). //if x spends 500, he is a special customer
Would this fact and rule be valid in prolog? Is the rule wrong? How would i be able to query this through prolog? As in would a call of
totalSpend(bob,500).
be a valid call?
sorry if i am answering my own question, i just seem to be a bit...well confused!
If you want to say that Bob, Jim and everyone who spends more than 500 are special customers, then define some people's spending, you would define it as follows:
specialCustomer(bob).
specialCustomer(jim).
specialCustomer(Who) :-
totalSpend(Who, Amount),
Amount >= 500.
totalSpend(mary, 400).
totalSpend(jack, 600).
totalSpend(pam, 500).
Then you would query it as follows:
?- specialCustomer(jim).
true.
?- specialCustomer(mary).
false.
?- specialCustomer(jack).
true.
?- specialCustomer(pam).
true.
?- specialCustomer(X).
X = bob ;
X = jim ;
X = jack ;
X = pam.
Everything you wrote is syntactically valid, but from your comments it doesn't seem like it does what you want it to.
specialCustomer(x).
Here you're saying specialCustomer(x) is true (and specialCustomer(anything_else) is false).
totalSpend(x,500) :- specialCustomer(x).
Here you're saying that totalSpend(x,500) is true iff specialCustomer(x) is true. Since you already defined special customer to be true, you could just as well have written
totalSpend(x,500).
Your comment makes it look as if you think that the part before the :- is the condition for the part after it, but it's the other way around.
totalSpend(bob,500).
Here you're asking whether totalSpend(bob, 500) is true, but since there is no rule for bob, it will be false.
Note that x and bob are symbols, not variables. So specialCustomer(x) will be true, but specialCustomer(bob) won't be.
Maybe you want x to be a variable? For that it has to be an upper case X.
What you probably want to express is
speccust(bob).
totalSpend(X,500) :- speccust(X).
such that bob is a special customer, and if somebody spent 500, then he is a special customer.
In practice, you would save that to a file, say customer.pl, and for instance in swi-prolog load it by putting ['customer.pl'].
Then, you can put queries to the database. In this case, you maybe want to know who is a special customer, then you would state:
totalSpend(Who, 500).
and receive Who = bob.

Resources