Search code examples
prologexpert-systemprolog-assert

Prolog and "reverse" output in expert system


i am new to Prolog. I mabaged to understand how to do simple expert system, like

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



hypothesize(car) :- car, !.
hypothesize(van) :- van,!.
hypothesize(bike) :- bike, !.
hypothesize(mini) :- mini, !.
hypothesize(tank) :-tank, !.
hypothesize(sau) :- sau, !.
hypothesize(excavator) :- excavator, !.
hypothesize(bulldozer) :- bulldozer, !.
hypothesize(rocket) :- rocket, !.
hypothesize(shuttle) :- shuttle , !.
hypothesize(destroyer) :- destroyer, !.
hypothesize(civil_plane) :- civil_plane, !.
hypothesize(unknown).

/* Vehicle identification rules */ 
sau :- grounder, 
        verify(has_gun),
        verify(long_fire).
tank :- grounder,
        verify(has_gun),
        verify(short_fire).
excavator :- grounder,
        verify(no_gun),
        verify(have_ladle).
bulldozer :- grounder,
        verify(no_gun),
        verify(no_ladle).
car :- grounder,
        verify(big_body),
        verify(for_passengers).
van :- grounder,
        verify(big_body),
        verify(for_cargo).
bike :- grounder,
        verify(small_body),
        verify(two_wheels).
mini :- grounder,
        verify(small_body),
        verify(four_wheels).

rocket :- flying,
        verify(cosmos_flying),
        verify(can_return).
shuttle :- flying,
        verify(cosmos_flying),
        verify(cant_return).
destroyer :- flying,
        verify(air_flying),
        verify(warmade).
civil_plane :- flying,
        verify(air_flying),
        verify(civil).




/* classification rules */ 
grounder :- verify(has_wheels), !.
grounder :- verify(have_engine).

flying :- verify(has_wings), !.
flying :- verify(has_jets).

/* how to ask questions */ 
ask(Question) :- 
    write('Does the vehicle 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. 

And this is good, but the question is - how to do "back" output, like, i will type "tank" in prolog window, and he will give all the parts this tanks consist - like, gun - yes, short rifle - yes, wings - no, e.t.c

Is it possible to do in expert system like this, or i have to do another programm?

Thank you for your reply


Solution

  • Because your clauses are simple, I think you could do it using clause/2 like this:

    ?- clause(grounder, X).
    X =  (verify(has_wheels), !) ;
    X = verify(have_engine).
    

    This is going to look something like this:

    part(Vehicle, Part) :-
        clause(Vehicle, Body),
        part_of_body(Body, Part).
    part_of_body(verify(Part), Part).
    part_of_body(X, Part) :-
        atom(X), X \= '!', part(X, Part).
    part_of_body((X,Y), Part) :-
          part_of_body(X, Part)
        ; part_of_body(Y, Part).
    

    This is a little bit of a meta-interpreter. clause/2 allows you to inspect Prolog's rules programmatically. It's a reflection mechanism. clause(Head, Body) unifies Head with the name of the rule and Body with its body—in other words, Head :- Body. So when we say clause(tank, Body), we get back:

    Body =  (grounder, verify(has_gun), verify(short_fire)).
    

    Decomposing this is somewhat taxing; after all, you have to traverse as much of Prolog itself as you use in your rules. But that's what the rest of the code does. First, we fetch the clause in part/2: note that the type I expect in for Part is that of a vehicle type atom. Once we fetch the clause we hand it off to part_of_body to decompose Prolog's structure.

    From here, a base case would be something like verify(has_gun), which seems to include the piece you're interested in (has_gun) and that's what the rule part_of_body(verify(Part), Part) says.

    The recursive cases are the ones that handle the rest of the structure, so for instance, if I see an atom such as grounder, I recursively look up those parts, which is what part_of_body(X, Part) does. I'm not thrilled about having to test specifically for ! but there is a weakness in my approach there which I haven't fixed yet.

    The main recursive case is the one that handles conjunctions: (X,Y). Here I just say, find some parts in the first part of the conjunction and then try the second.

    ?- part(tank, Part).
    Part = has_wheels ;
    Part = have_engine ;
    Part = has_gun ;
    Part = short_fire.
    

    So this database can be worked backwards, it just isn't particularly convenient. :) A better data model would go a long way toward improving this situation.