324 Lecture Week 12 - Winter 2006 ================================= Lists ----- Literals enclosed by [ ], elements separated by commas, optional | for rest of list (similar to cons). E.g. [csc, 3.24, c], [a, b | [c, d]] Head-rest (car-cdr) recursion: Let's define in(X, L) to mean X is in L. X is in L iff it's the head, or in the rest. in(X, [X|_]). in(X, [_|Y]) :- in(X, Y). Trace and try the following: ?- in(a, [c, b, a]). ?- in(a, [a, b, a]). ?- in(X, [c, b, a]). ?- in(a, [X, b, c]). ?- in(a, [X, b, Y]). ?- in(a, [X, b, X]). ?- in(a, [X, a, Y]). ?- in(a, [X, a, X]). ?- in(a, L). ?- in(A, L). Our in very invertible. Let's define double(L, L2) to mean L2 is L with the elements doubled. double([], []). double([H|R], [H2|R2]) :- H2 is 2 * H, double(R, R2). ?- double([1, 2, 3], [2, 4, 6]). Yes ?- double([1, 2, 3], [2, X, 6]). X = 4 ?- double([1, 2, 3], X). X = [2, 4, 6] ?- double(X, [2, 4, 6]). ERROR Our double requires L to be fully-instantiated. Structures ---------- E.g. book(war_and_peace, person(tolstoy, leo), 1863) Lists are a special case: .(head, rest). ?- .(X, Y) = [a, b, c]. X = a, Y = [b, c] Arithmetic expressions are a special case, as mentioned above. ?- X + Y = 1 + 2 X = 1, Y = 2 Let's model propositional logic. We'll use constant symbols for propositional variables, structures and(_,_), or(_, _), not(_). Define wff(X) to mean X is a well-formed propositional formula: wff(X) :- atom(X), X \= []. wff(and(X,Y)) :- wff(X), wff(Y). wff(or(X,Y)) :- wff(X), wff(Y). wff(not(X)) :- wff(X). ?- wff(and(not(p), or(q, not(r)))). Yes. Define eval(P, T) to mean P is true when the propositional variables in list T are considered true: eval(X, T) :- atom(X), in(X, T). eval(and(X, Y), T) :- eval(X, T), eval(Y, T). eval(or(X, _), T) :- eval(X, T). eval(or(_, X), T) :- eval(X, T). eval(not(X), T) :- not(eval(X, T)). ?- eval(and(not(p),or(q,r)),[p]). No ?- eval(and(not(p),or(q,r)),[q]). Yes How invertible is eval? ?- eval(p,[X]). X = p ?- eval(and(p,or(q,r)),[X,Y]). X = p, Y = q ; X = p, Y = r ; X = q, Y = p ; X = r, Y = p ?- eval(not(p),[q]). Yes ?- eval(not(p),[X]). No Problem: eval(not(p),[X]) leads to not(eval(p,[X])) which asks whether ~ -] X, eval(p, [X]) But we've been thinking of queries as wrapped in an existential of the free variables, so probably thought of it as -] X, ~ eval(p,[X]). Formal Specifications ===================== Formal Specifications I & II: http://www.cs.toronto.edu/~sheila/324/f05/slides/index.html Grammar Rules in Prolog ======================= Tokenization & Template Systems ------------------------------- Convert an input string into distinct pieces - tokens. Template system - match tokenized strings directly to templates / stored patterns, accompanied by a translation schema. Inflexible, and insufficient to capture any natural language. i.e., John arrived. Max said John arrived. Bill claimed Max said John arrived. Mary thought Bill claimed Max said John arrived. etc. Generative Grammars ------------------- Human (natural) languages have an infinite number of sentence structures. To recognize languages, must capture its recursive structure - formal grammars We want a finite representation of an infinite set -- just like problems in set theory! eg. 0 is an even number. From any even number you can get another by adding or subtracting 2. Generative grammar vs. recognizer - generate strings in the language, or - recognize whether a string is in the language Context Free Language (in BNF): S -> NP VP NP -> D N VP -> V NP VP -> V S D -> the | a N -> dog | cat | boy | girl V -> chased | saw | said | believed Tree for "the dog chased the cat" Tree for "the boy believed the girl said the cat chased the dog" Parsing in Prolog ----------------- X = list of words we're starting with Z list of words left over after removing a particular constituent s(X,Z) :- np(X,Y), vp(Y,Z). np(X,Z) :- d(X,Y), n(Y,Z). n([dog|Z], Z). ?- s([the,cat,saw,the,dog], []). -- is it a sentence? ?- sentence(X,[]). -- generate a valid sentence DCG notation ------------ These rules are tedious to write... Definite Clause Grammars in Prolog simplifies things. We can rewrite using DCG notation: s --> np,vp. n --> [dog]. v --> [gives,up]. Nonterminals in the CFG (BNF) are written as normal functors, terminals are put in lists, Prolog code appears inside braces. The X and Z variables are automatically added as the last two arguments of the grammar predicates. Power of DCG: can extend standard BNF rules in CFG using Prolog variables, perhaps to add understanding, agreement, or code generation. Eg. recognizing a^N b^N c^N: s --> a(N), b(N), c(N). a(N) --> [a], a(N1), {N is N1+1}. a(0) --> [ ]. b(N) --> [b], b(N1), {N is N1+1}. b(0) --> [ ]. c(N) --> [c], c(N1), {N is N1+1}. c(0) --> [ ].