prologprolog-directive-dynamic

Use dynamic/1 and var/1 Create Prolog clauses that allow the following


?- say([the, capital, of, switzerland, is, bern]). 
Thank you. 
?- say([the, capital, of, switzerland, is, bern]). 
I already know that. 
?- say([the, capital, of, switzerland, is, zurich]). 
No, you said the capital of switzerland is bern. 
?- say([the, capital, of, france, is, bern]). 
No, you said bern is the capital of switzerland. 
?- say([the, capital, of, What, is, bern]). 
What = switzerland. 
?- say([the, capital, of, switzerland, is, What]). 
What = Bern. 

The last two is easy, I use say([the, capital, of , switzerland, is, bern]). But how to make Prolog output Thank you instead of true? And what about other sentence? Thanks a lot.

I try to write some code, but it not work so well.

:- dynamic say/1.
say([the, capital, of, switzerland, is, bern]) :- write('Thank you').
say([the, capital, of, switzerland, is, bern]) :- write('I already know that.').
say([the, capital, of, switzerland, is, X]):-
    X\==bern, write('No, you said the capital of switzerland is bern.').
say([the, capital, of, X, is, bern]):-
    X\==switzerland, write('No, you said bern is the capital of switzerland.').

Solution

  • I think the point of the exercise is not to make the program say "Thank you" when the sentence [the, capital, of, switzerland, is, bern] is input, but rather to use variables, assert, and queries to make it learn and recall things, and do something vaguely smart.

    The head of your rule should be something like say([the, capital, of, Subj, is, Obj]), where Subj and Obj are variables for the subject and the object of that relationship, so that your rule can match multiple sentences.

    The dynamic predicate should not be say/1. That is the hand-crafted predicate that does the knowledge store/recall. You should have a different predicate to store your knowledge, let's call it capital/2.

    :- dynamic capital/2.
    

    You will use assertz(capital(a, b)). to insert a fact in the knowledge base, and capital(X, Y) to query for it.

    In the first use case, check if the variables are ground (so it means we are instructed about something), and check also if we do not know already the same fact, and then insert the fact into the knowledge base:

    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        \+ capital(Subj, Obj), !,
        assertz(capital(Subj, Obj)),
        write([thank, you]).
    

    For the second case, we again check the variables are ground, but then we do the opposite check, and say that we already know that:

    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        capital(Subj, Obj), !,
        write([i, already, know, that]).
    

    The third and fourth cases work in a similar way, with ground variables, but look for a mismatch between the instruction and what's in the knowledge base:

    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        capital(Subj, Obj1),
        Obj1 \= Obj, !,
        write([no, you, said, the, capital, of, Subj, is, Obj1]).
    
    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        capital(Subj1, Obj),
        Subj1 \= Subj, !,
        write([no, you, said, Obj, is, the, capital, of, Subj1]).
    

    Finally, the last case is for query answering, so Subj or Obj (or both) must be non-ground variables, and we print what's in the knowledge base:

    say([the, capital, of, Subj, is, Obj]) :-
        (var(Subj) ; var(Obj)),
        capital(Subj, Obj), !,
        write([the, capital, of, Subj, is, Obj]).
    

    Note that because the program uses cuts (!) and assertz/1, the order of the rules is important. Changing the order would give incorrect results.

    Here's the complete program:

    :- dynamic capital/2.
    
    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        capital(Subj, Obj1),
        Obj1 \= Obj, !,
        write([no, you, said, the, capital, of, Subj, is, Obj1]).
    
    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        capital(Subj1, Obj),
        Subj1 \= Subj, !,
        write([no, you, said, Obj, is, the, capital, of, Subj1]).
    
    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        \+ capital(Subj, Obj), !,
        assertz(capital(Subj, Obj)),
        write([thank, you]).
    
    say([the, capital, of, Subj, is, Obj]) :-
        ground((Subj, Obj)),
        capital(Subj, Obj), !,
        write([i, already, know, that]).
    
    say([the, capital, of, Subj, is, Obj]) :-
        (var(Subj) ; var(Obj)),
        capital(Subj, Obj), !,
        write([the, capital, of, Subj, is, Obj]).
    

    and a test run:

    ?- say([the, capital, of, switzerland, is, bern]).
    [thank,you]
    true.
    
    ?- say([the, capital, of, switzerland, is, bern]).
    [i,already,know,that]
    true.
    
    ?- say([the, capital, of, switzerland, is, zurich]).
    [no,you,said,the,capital,of,switzerland,is,bern]
    true.
    
    ?- say([the, capital, of, france, is, bern]).
    [no,you,said,bern,is,the,capital,of,switzerland]
    true.
    
    ?- say([the, capital, of, What, is, bern]).
    [the,capital,of,switzerland,is,bern]
    What = switzerland.
    
    ?- say([the, capital, of, switzerland, is, What]).
    [the,capital,of,switzerland,is,bern]
    What = bern.