skip to Main Content

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


  1. First let’s take a look at the queries in question. Your first query…

    ?- value(kiwi, active_at, night).
    true.
    

    … succeeds with the first rule of value/3. You can see that if you try the goals manually. The query…

    ?- Query =.. [kiwi, active_at, night].
    

    … succeeds and unifies the variable Query with kiwi(active_at, night):

    ?- Query =.. [kiwi, active_at, night].
    Query = kiwi(active_at, night).
    

    The next goal calls Query

    ?- call(kiwi(active_at, night)).
    true.
    

    … 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…

    ?- value(kiwi, active_at, daylight).
    true ;
    false.
    

    … that succeeds as well. The first rule of value/3 unifies Query with kiwi(active_at, daylight) and subsequently calls it:

    ?- call(kiwi(active_at, daylight)).
    false.
    

    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

    ?- parent(kiwi,ParentName).
    ParentName = bird ;
    false.
    

    … succeeds and is unifying ParentName with bird due to the first argument of the disjunction…

    ?- Query =.. [kiwi, a_kind_of, ParentName].
    Query = kiwi(a_kind_of, ParentName).
    

    … and the subsequent call/1 in parent/2:

    ?- call(kiwi(a_kind_of, ParentName)).
    ParentName = bird.
    

    Now the second goal of the second rule of value/3 calls itself, starting with the first rule again:

    ?- value(bird, active_at, daylight).
    true.
    

    This succeeds and therefore the query…

    ?- value(kiwi, active_at, daylight).
    true
    

    … succeeds as well. Note that Prolog is waiting for input after answering true and if you press ; it is searching for further solutions:

    ?- value(kiwi, active_at, daylight).
    true ;
    

    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:

    ?- value(kiwi, active_at, daylight).
    true ;
    false.
    

    However, if you ask at what time kiwis are active Prolog will answer:

    ?- value(kiwi, active_at, X).
    X = night.
    

    The reason why daylight is not derived here is again the cut. After the first rule of value/3 succeeds for X = 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:

    iscallable(bird).                                 % <- new fact
    iscallable(albatross).                            % <- new fact
    iscallable(kiwi).                                 % <- new fact
    iscallable(albert).                               % <- new fact
    
    value(Frame, Slot, Value):-
        iscallable(Frame),                            % <- new goal iscallable/1
        Query =.. [Frame, Slot, Value],
        call(Query).                                  % <- removed cut here
    value(Frame, Slot, Value):-
        parent(Frame, ParentName),
        value(ParentName, Slot, Value).
    
    parent(Frame, ParentName):-
        iscallable(Frame),                            % <- new goal iscallable/1
        (Query =.. [Frame, a_kind_of, ParentName];
         Query =.. [Frame, instance_of, ParentName]),
        call(Query).
    

    With these alterations you’ll consistently get both solutions:

    ?- value(kiwi, active_at,X).
    X = night ;
    X = daylight ;
    false.
    
    ?- value(kiwi, active_at,night).
    true ;
    false.
    
    ?- value(kiwi, active_at,daylight).
    true ;
    false.
    

    To see why you need something like iscallable/1 in rule 1 of value/3, remove it and consider the following query:

    ?- value(kiwi, active_at, X).
    X = night ;
    X = daylight ;
    ERROR: value/3: Undefined procedure: animal/2
       Exception: (9) animal(active_at, _G4462) ? creep
       Exception: (8) value(animal, active_at, _G4462) ? creep
    

    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:

    ?- value(kiwi, active_at,X).
    X = night ;
    X = daylight ;
    ERROR: parent/2: Undefined procedure: animal/2
       Exception: (10) animal(a_kind_of, _G4558) ? creep
    

    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:

    nocturnalbird(active_at, night).  % <- new fact
    
    %kiwi(a_kind_of, bird).           % <- remove fact
    kiwi(a_kind_of, nocturnalbird).   % <- new fact
    %kiwi(active_at, night).          % <- remove fact
    kiwi(colour, brown).
    kiwi(size, 24).
    

    This yields the desired results:

    ?- value(kiwi, active_at,X).
    X = night ;
    false.
    
    ?- value(kiwi, active_at,night).
    true ;
    false.
    
    ?- value(kiwi, active_at,daylight).
    false.
    

    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:

    value(Frame, Slot, Value):-
        iscallable(Frame),               
        call(Frame, Slot, Value).                    % <- here
    value(Frame, Slot, Value):-
        parent(Frame, ParentName),
        value(ParentName, Slot, Value).
    
    parent(Frame, ParentName):-
        iscallable(Frame),
        (call(Frame, a_kind_of, ParentName);         % <- here
         call(Frame, instance_of, ParentName)).      % <- here
    
    Login or Signup to reply.
  2. I came across this very late, but another way of handling this is to change the first clause of value to:

    value(Frame, Slot, Value):-
        call(Frame, Slot, V), !,    % Value is directly retrieved
        V = Value.                  % Slot might already inherit value so check
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search