This discussion illustrates how the program is used, what it can do, and some key points about how it represents, stores, and retrieves information that it has "learned" as it interacts with the user.
?- run. >> principia is a book Asserted "book(principia)." >> bertrand wrote every book Asserted "writes(bertrand, _G332):-book(_G332)." >> what did bertrand write principia. >> what did allen write no. >> who wrote every book Error: "too difficult." >> who wrote principia bertrand. >> bruce read every book that bertrand wrote Asserted "reads(bruce, _G379):-book(_G379), writes(bertrand, _G379)." >> shrdlu is a book Asserted "book(shrdlu)." >> what did bruce read principia, shrdlu. >>As is evident, the program stores information about what has been asserted and uses that information to answer questions, provided that it can parse the assertions and questions that are entered.
Successful parsing depends on syntactic correctness. Parsable assertions result in the message "Asserted: C" where C is a Prolog rule or fact. Not shown in this excange is the program's adding this fact to the database with which it has started (facts and rules defining the basic grammar and lexicon), and the program's translation of the original assertion into a logical form, which is its semantic representation.
Parsable questions are answered by a message "yes" (if the question is a yes/no question and the answer is true according to the facts and rules stored in the database during the processing of prior assertions), "no" (if the answer is not so justified), or a list of all values that answer the question (if the question is not a yes/no question).
run :- write('>> '), % prompt the user read_sent(Words), % read a sentence talk(Words, Reply), % process it with TALK print_reply(Reply), % generate a reply run. % pocess more sentences %%% talk(Sentence, Reply) %%% %%% Sentence ==> sentence to form a reply to %%% Reply <== appropriate reply to the sentence talk(Sentence, Reply) :- % parse the sentence parse(Sentence, LF, Type), % convert the FOL logical form into a Horn % clause, if possible clausify(LF, Clause, FreeVars), !, % concoct a reply, based on the clause and % whether sentence was a query or assertion reply(Type, FreeVars, Clause, Reply). talk(Sentence, error('too difficult')).
The logical form for an intransitive verb is an encoding of the lambda expression \x.verb(x), where x is a placeholder for the subject. For instance, "halts" has a logical form that encodes the expression \x.halts(x), or X^ 'halts(X).
iv(nonfinite, LF) --> [IV], {iv(IV, _, _, _, _, LF)}. iv(finite, LF) --> [IV], {iv(_, IV, _, _, _, LF)}. iv(finite, LF) --> [IV], {iv(_, _, IV, _, _, LF)}. iv(past_participle, LF) --> [IV], {iv(_, _, _, IV, _, LF)}. iv(pres_participle, LF) --> [IV], {iv(_, _, _, _, IV, LF)}. iv(halt, halts, halted, halted, halting, X^ `halts(X)). iv(run, runs, ran, run, running, X^ `runs(X)).Transitive verbs have as a logical form an encoding of the lambda expression \x.\y.verb(x, y), where x is the subject of the verb and y is the object. For instance, "speaks" is an encoding of \x.\y.speaks(x, y), or X^Y^ 'speaks(X, Y).
tv(nonfinite, LF) --> [TV], {tv(TV, _, _, _, _, LF)}. tv(finite, LF) --> [TV], {tv(_, TV, _, _, _, LF)}. tv(finite, LF) --> [TV], {tv(_, _, TV, _, _, LF)}. tv(past_participle, LF) --> [TV], {tv(_, _, _, TV, _, LF)}. tv(pres_participle, LF) --> [TV], {tv(_, _, _, _, TV, LF)}. tv(write, writes, wrote, written, writing, X^Y^ `writes(X,Y)). tv(read, reads, read, read, reading, X^Y^ `reads(X,Y)). tv(speak, speaks, spoke, spoken, speaking, X^Y^ `speaks(X,Y)). tv(meet, meets, met, met, meeting, X^Y^ `meets(X,Y)). tv(concern, concerns, concerned, concerned, concerning, X^Y^ `concerns(X,Y)). tv(run, runs, ran, run, running, X^Y^ `runs(X,Y)).Auxiliary verbs (to, does, did, ...), relative pronouns (that, who, whom), wh-words (who, whom, what) are enoded in their usual way.
Determiners are encoded with logical forms that reflect their meaning. For example, if we assert "Bertrand wrote every book", the system needs a logical form that will say, in effect, that "for every X , if X is a book then bertrand wrote X." The logical form for "every" in the lexicon has that structure:
det(every, (X^S1) ^ (X^S2) ^ all(X, S1=>S2)).
Similarly, the logical form for determiners "a" and "some" are coded as follows:
det(a, (X^S1) ^ (X^S2)
^ exists(X, S1&S2)).
det(some, (X^S1) ^ (X^S2) ^ exists(X, S1&S2)).
There are two different types of nouns, those which represent classifications of objects or people, and proper nouns (which represent specific objects and people themselves -- e.g., principia). The first type of noun has a logical form which allows proper nouns to become members of that classification. For instance, the classification "author" has the logical form \x.author(x), which is encoded X^ `author(X). The second type has itself as its logical form.
n(author, X^ `author(X)). pn(principia, principia).
s(S, GapInfo) --> np(VP^S, nogap), vp(finite, VP, GapInfo). sinv(S, GapInfo) --> aux(finite/Form, VP1^VP2), np(VP2^S, nogap), vp(Form, VP1, GapInfo). q(S => `answer(X)) --> whpron, vp(finite, X^S, nogap). q(S => `answer(X)) --> whpron, sinv(S, gap(np, X)). q(S => `answer(yes)) --> sinv(S, nogap). q(S => `answer(yes)) --> [is], np((X^S0)^S, nogap), np((X^true)^exists(X,S0&true), nogap). np(NP, nogap) --> det(N2^NP), n(N1), optrel(N1^N2). np(NP, nogap) --> pn(NP). np((X^S)^S, gap(np, X)) --> []. vp(Form, X^S, GapInfo) --> tv(Form, X^VP), np(VP^S, GapInfo). vp(Form, VP, nogap) --> iv(Form, VP). vp(Form1, VP2, GapInfo) --> aux(Formrlogina1/Form2, VP1^VP2), vp(Form2, VP1, GapInfo). vp(Form1, VP2, GapInfo) --> rov(Form1/Form2, NP^VP1^VP2), np(NP, GapInfo), vp(Form2, VP1, nogap). vp(Form2, VP2, GapInfo) --> rov(Form1/Form2, NP^VP1^VP2), np(NP, nogap), vp(Form1, VP1, GapInfo). vp(finite, X^S, GapInfo) --> [is], np((X^P)^exists(X,S&P), GapInfo). optrel((X^S1)^(X^(S1&S2))) --> relpron, vp(finite, X^S2, nogap). optrel((X^S1)^(X^(S1&S2))) --> relpron, s(S2, gap(np, X)). optrel(N^N) --> [].
parse(Sentence, LF, assertion) :- s(LF, nogap, Sentence, []).
The reply to an assertion is made simply be reporting to the user the Prolog fact or rule that was generated from the logical form after the paring of the sentence. The reply function also adds this fact or rule to the Prolog program, so that it can participate in answering future queries. This is accomplished by the Prolog function "assert", which takes any Prolog fact or rule and adds it dynamically to the database.
reply(assertion, _FreeVars, Assertion, asserted(Assertion)) :- assert(Assertion), !.
parse(Sentence, LF, query) :- q(LF, Sentence, []).
For example, the parse for the sentence "who wrote principia" wlll return the logical form
LF = `writes(X, principia)=> `answer(X)
The reply to a query is made by first finding a set of all the answers that satisfy the query, replying with that set (if it exists), or "no" if it doesn't.
reply(query, FreeVars, (answer(Answer):-Condition), Reply) :-
(setof(Answer, FreeVars^Condition, Answers) -> Reply = answer(Answers)
;
(Answer = yes -> Reply = answer([no]) ;
Reply = answer([none]))), !.
This can be read as an ordinary "if-then-else" statement, where the arrows -> denote "then" and the semicolons (;) denote "or else". So the first line generates the Reply if such a set is found, and the second generates the Reply if the answer is "yes," and otherwise the third line generates the Reply.
The setof function builds an ordered set of instances of Answer, called Answers (in the form of a list without duplicates) that satisfies the goal FreeVars^Condition. For instance, if the goal is answer(X) := writes(X, principia), then the call is setof(X, FreeVars^writes(X, principia), Answers). If the fact writes(bertrand, principia) is in the database at the time, then setof will return Answers = [bertrand]. Otherwise, setof will fail.
clausify(FOL, Clause, FreeVars)Universally quantified logical forms are clausified by stripping the quantifier from the clausified version of the logical form. For instance, clausify(all(B, book(B) & wrote(bertrand, B)), Clause, FreeVars) leaves Clause = book(B), wrote(bertrand, B) and FreeVars = [B]. This makes sense, since both forms are logically equivalent to "B is a book if and only if bertrand wrote B."
clausify(all(X,F0),F,[X|V]) :- clausify(F0,F,V).For implications, the consequent C0 is clausifed as the literal C and the antecedent A0 is clausified as A. Then the Prolog clause C :- A, itself an implication, is formed out of these results. The list V of free variables is passed along in this process.
clausify(A0=>C0,(C:-A),V) :- clausify_literal(C0,C), clausify_antecedent(A0,A,V).Literals are left unchanged by clausify, though an empty free variable list is generated.
clausify(C0,C,[]) :- clausify_literal(C0,C).
The function clausify_antecedent(FOL, Clause, FreeVars) leaves literals unchanged (except the literal marker is removed). For conjunctions, clausify-antecedent clausifies each conjunct separately and then combines them with a comma to form the right-hand side of a Prolog rule. For existentials, the quantifier is stripped and the free variable is added to the free variable list.
clausify_antecedent(L0,L,[]) :- clausify_literal(L0,L).
clausify_antecedent(E0&F0, (E,F), V) :- clausify_antecedent(E0,E,V0),
clausify_antecedent(F0,F,V1),
conc(V0,V1,V).
clausify_antecedent(exists(X,F0), F, [X|V]) :- clausify_antecedent(F0,F,V).
clausify_literal(`L, L).