I'm reading "Seven languages in seven weeks" atm, and I'm stumped over some Prolog query that I don't understand the 'no' response to.
The friends.pl
file looks like this:
likes(wallace, cheese).
likes(grommit, cheese).
likes(wendolene, sheep).
friend(X, Y) :- \+(X = Y), likes(X, Z), likes(Y, Z).
I can do some trivial queries on it, such as:
| ?- ['friends'].
compiling /home/marc/btlang-code/code/prolog/friends.pl for byte code...
/home/marc/btlang-code/code/prolog/friends.pl compiled, 12 lines read - 994 bytes written, 8 ms
yes
| ?- friend(wallace,grommit).
yes
| ?- friend(wallace,wendolene).
no
This is all as expected. Now, I want to introduce a variable in the query. My intent being that Prolog will give me a list of all of Wallace's friends. I'm expecting X = grommit
, but I'm getting no
:
| ?- trace.
The debugger will first creep -- showing everything (trace)
yes
{trace}
| ?- friend(wallace,X).
1 1 Call: friend(wallace,_16) ?
2 2 Call: \+wallace=_16 ?
3 3 Call: wallace=_16 ?
3 3 Exit: wallace=wallace ?
2 2 Fail: \+wallace=_16 ?
1 1 Fail: friend(wallace,_16) ?
no
{trace}
It doesn't even try to unify X
(_16
) with grommit
. Why?
A variable in Prolog is a string of letters, digits, and underscores ( _ ) beginning either with a capital letter or with an underscore. Examples: X , Sister , _ , _thing , _y47 , First_name , Z2 The variable _ is used as a "don't-care" variable, when we don't mind what value the variable has.
The unique feature of Prolog is that it automatically chooses the facts and rules needed to solve a query. But how does it make its choice? It starts by trying to solve each goal in a query, left to right (recall goals are connected using “,” which is the and operator).
body the last part of a Prolog rule. It is separated from the head by the neck symbol, written as :- . It has the form of a comma-separated list of goals, each of which is a the name part of a functor, possibly followed by a comma-separated list of arguments, in parentheses.
A Prolog program consists of a number of clauses. Each clause is either a fact or a rule. After a Prolog program is loaded (or consulted) in a Prolog interpreter, users can submit goals or queries, and the Prolog intepreter will give results (answers) according to the facts and rules.
It is the definition of friend:
friend(X, Y) :- \+(X = Y), likes(X, Z), likes(Y, Z).
The important thing here is that you start with \+(X = Y)
which is normally defined as:
\+ Goal :- Goal,!,fail
Note that this means that if goal succeeds, you are sure to fail. Free variables (ones that havent been assigned) will always unify, and thus be equal, so you will always fail with a free variable. It will thus never assign a value to X or Y if it doesn't already have one.
Instead
friend(X, Y) :- likes(X, Z), likes(Y, Z), \+(X = Y)
will behave more as you expect.
The problem here is that prolog gives you powerful ways to control the flow of programs, but those dont really fit nicely with its more logic oriented design. It should be possible to express "negation as failure" type constraints in a way that does not produce these problems. I'm not a huge prolog fan for this reason.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With