1
votes

This program ask the user questions about attributes of animals, and expects answers in a "yes/no" fashion. It uses its identification rules to determine the animal that you have chosen.

I would like to know how prolog can show/list predicates with a certain property.

For example: What query could be 'asked' to SWI-prolog to list all animals that are mammals?

Expected answer to the query is: cheetah, tiger.

Please consider the following code example:

/* animal.pro
  animal identification game.  

    start with ?- go.     */

go :- hypothesize(Animal),
      write('I guess that the animal is: '),
      write(Animal),
      nl,
      undo.

/* hypotheses to be tested */
hypothesize(cheetah)   :- cheetah, !.
hypothesize(tiger)     :- tiger, !.
hypothesize(giraffe)   :- giraffe, !.
hypothesize(zebra)     :- zebra, !.
hypothesize(ostrich)   :- ostrich, !.
hypothesize(penguin)   :- penguin, !.
hypothesize(albatross) :- albatross, !.
hypothesize(unknown).             /* no diagnosis */

/* animal identification rules */
cheetah :- mammal, 
           carnivore, 
           verify(has_tawny_color),
           verify(has_dark_spots).
tiger :- mammal,  
         carnivore,
         verify(has_tawny_color), 
         verify(has_black_stripes).
giraffe :- ungulate, 
           verify(has_long_neck), 
           verify(has_long_legs).
zebra :- ungulate,  
         verify(has_black_stripes).

ostrich :- bird,  
           verify(does_not_fly), 
           verify(has_long_neck).
penguin :- bird, 
           verify(does_not_fly), 
           verify(swims),
           verify(is_black_and_white).
albatross :- bird,
             verify(appears_in_story_Ancient_Mariner),
             verify(flys_well).

/* classification rules */
mammal    :- verify(has_hair), !.
mammal    :- verify(gives_milk).
bird      :- verify(has_feathers), !.
bird      :- verify(flys), 
             verify(lays_eggs).
carnivore :- verify(eats_meat), !.
carnivore :- verify(has_pointed_teeth), 
             verify(has_claws),
             verify(has_forward_eyes).
ungulate :- mammal, 
            verify(has_hooves), !.
ungulate :- mammal, 
            verify(chews_cud).

/* how to ask questions */
ask(Question) :-
    write('Does the animal have the following attribute: '),
    write(Question),
    write('? '),
    read(Response),
    nl,
    ( (Response == yes ; Response == y)
      ->
       assert(yes(Question)) ;
       assert(no(Question)), fail).

:- dynamic yes/1,no/1.

/* How to verify something */
verify(S) :-
   (yes(S) 
    ->
    true ;
    (no(S)
     ->
     fail ;
     ask(S))).

/* undo all yes/no assertions */
undo :- retract(yes(_)),fail. 
undo :- retract(no(_)),fail.
undo.

Source of original tutorial: https://www.cpp.edu/~jrfisher/www/prolog_tutorial/2_17.html

1
Unfortunately, the code in the tutorial is poorly written, in my opinion. It's written specifically to handle setting up conditions in the database according to user yes/no questions. It doesn't lend itself to querying, for example, what animals are mammals. The knowledge about the attributes for a given animal are contained within the behavior of predicates that succeed conditionally depending upon responses from the user. Details about the animals should be asserted inthe database as facts. Then a query could be easily constructed to answer various questions about animals. - lurker

1 Answers

2
votes

I agree with @lurker that this database is badly designed, but that doesn't mean you can't get some value out of it. For instance, this query will tell you all the "animals" that are mammals:

  ?- current_predicate(Animal, _), 
     clause(Animal, Clause), 
     Clause = ','(mammal, _).
Animal = ungulate,
Clause =  (mammal, verify(has_hooves), !) ;

Animal = ungulate,
Clause =  (mammal, verify(chews_cud)) ;

Animal = tiger,
Clause =  (mammal, carnivore, verify(has_tawny_color), verify(has_black_stripes)) ;

Animal = cheetah,
Clause =  (mammal, carnivore, verify(has_tawny_color), verify(has_dark_spots)) ;

false.

The manual includes a section on examining the program which you can use to discover facts about your program. You can also use listing/1 if you just want to peruse them yourself, but a structured decomposition with clause/2 and current_predicate/2 will enable you to examine them programmatically, which is probably what you want.

Half the battle in programming is figuring out your data structures. It's true that you can treat predicates as data in Prolog; as you see here, it isn't a particularly ergonomic way to structure data. It would be better to have a general search strategy that works by examining the database, I think. This looks to me like "old school" Prolog with a lot of state. I would definitely consider a redesign.