This is a code sample took from Ivan Bratko’s book “Prolog Programming for Artificial Intelligence – 3rd edition – pg 377”. It works perfectly for the query ?-value(kiwi, active_at, night)
. by giving the result as true
.
But it also gives the result as true
for the query ?- value(kiwi, active_at, daylight).
and it should be false
.
How should I modify it, or am I missing something?
%--------Define frames
bird(a_kind_of, animal).
bird(active_at, daylight).
albatross(a_kind_of, bird).
albatross(colour, black).
albatross(size, 10).
kiwi(a_kind_of, bird).
kiwi(active_at, night).
kiwi(colour, brown).
kiwi(size, 24).
albert(instance_of, albatross).
albert(size, 10).
%--------Inference in Frames
value(Frame, Slot, Value):-
Query =.. [Frame, Slot, Value],
call(Query), !. %Value is directly retrieved
value(Frame, Slot, Value):-
parent(Frame, ParentName),
value(ParentName, Slot, Value). % More general rule
parent(Frame, ParentName):-
(Query =.. [Frame, a_kind_of, ParentName];
Query =.. [Frame, instance_of, ParentName]),
call(Query).
2
Answers
First let’s take a look at the queries in question. Your first query…
… succeeds with the first rule of value/3. You can see that if you try the goals manually. The query…
… succeeds and unifies the variable
Query
withkiwi(active_at, night)
:The next goal calls
Query
…… and succeeds since you have a fact
kiwi(active_at, night).
. The next goal!
prevents Prolog from backtracking and searching for further solutions therefore the query?- value(kiwi, active_at, night).
succeeds deterministically (you don’t have to press;
after the first solution). Now let’s take a look at your second query…… that succeeds as well. The first rule of value/3 unifies
Query
withkiwi(active_at, daylight)
and subsequently calls it:This call fails since you don’t have a suitable fact. Note that at this point the rule can’t succeed, so Prolog doesn’t bother with the last goal and moves on to the second rule of value/3. Here the first goal
… succeeds and is unifying
ParentName
withbird
due to the first argument of the disjunction…… and the subsequent call/1 in parent/2:
Now the second goal of the second rule of value/3 calls itself, starting with the first rule again:
This succeeds and therefore the query…
… succeeds as well. Note that Prolog is waiting for input after answering
true
and if you press;
it is searching for further solutions:Although the goal
value(bird, active_at, daylight)
succeeded deterministically due to the cut in the first rule of value/3, the first goal (parent(kiwi,ParentName)
) of the calling predicate (the second rule of value/3) left a choice point open (see the query above) and now Prolog is backtracking there to search for other solutions. But there are none so Prolog answers:However, if you ask at what time kiwis are active Prolog will answer:
The reason why
daylight
is not derived here is again the cut. After the first rule of value/3 succeeds forX = night
it prevents Prolog from searching for other solutions. From a logical point of view this behaviour is outright incorrect: Either kiwis are active at day and night, then the last query should yield both solutions, or they are nocturnal, then the query?- value(kiwi, active_at, daylight).
should fail. I don’t want to speculate on the intended behaviour of the predicate value/3, so I will refer to the Wikipedia entry on kiwi instead which states: Kiwi are shy and usually nocturnal. Their mostly nocturnal habits may be a result of habitat intrusion by predators, including humans. In areas of New Zealand where introduced predators have been removed, such as sanctuaries, kiwi are often seen in daylight.If you want to have both solutions I would suggest to add some facts iscallable/1 (don’t call it callable/1 though, since there’s already a built-in with that name) that cover your facts, add it in value/3 and parent/2 and remove the cut like so:
With these alterations you’ll consistently get both solutions:
To see why you need something like iscallable/1 in rule 1 of value/3, remove it and consider the following query:
The predicate value/3 is trying to call a predicate animal/2 that does not exist. To see why you need something like iscallable/1 in parent/2, remove it and consider the following query:
On the other hand, if you opt for the night solution only, you can define a fact nocturnalbird/2 that describes activity at night time and alter the facts kiwi/2 accordingly:
This yields the desired results:
If you plan to extend this example to include animal properties for nocturnal birds as well, you’ll need to add a fact
nocturnalbird(a_kind_of, animal).
as well as include iscallable/1 in value/3 and parent/2 like above.EDIT:
In fact, you don’t need =../2 here at all, as pointed out by @false in the comments. You can simply use call/N instead:
I came across this very late, but another way of handling this is to change the first clause of value to: