11. Applications of Logic Programming

advertisement
10. Applications of Logic Programming
PS — Applications of Logic Programming
Roadmap
1. Search problems
— SEND + MORE = MONEY
2. Symbolic Interpretation
— Definite Clause Grammars
— Interpretation as Proof
— An interpreter for the calculator language
© O. Nierstrasz
10.2
PS — Applications of Logic Programming
Reference
>
The Ciao Prolog System Reference Manual, Technical
Report CLIP 3/97.1, www.clip.dia.fi.upm.es
© O. Nierstrasz
10.3
PS — Applications of Logic Programming
Roadmap
1. Search problems
— SEND + MORE = MONEY
2. Symbolic Interpretation
— Definite Clause Grammars
— Interpretation as Proof
— An interpreter for the calculator language
© O. Nierstrasz
10.4
PS — Applications of Logic Programming
1. Solving a puzzle
 Find values for the letters so the following equation
holds:
SEND
+MORE
----MONEY
© O. Nierstrasz
10.5
PS — Applications of Logic Programming
A non-solution:
We would like to write:
soln0 :- A is 1000*S + 100*E + 10*N + D,
B is 1000*M + 100*O + 10*R + E,
C is 10000*M + 1000*O + 100*N + 10*E + Y,
C is A+B,
showAnswer(A,B,C).
showAnswer(A,B,C)
writeln([])
writeln([X|L])
© O. Nierstrasz
:- writeln([A, ‘ + ‘, B, ‘ = ‘, C]).
:- nl.
:- write(X), writeln(L).
10.6
PS — Applications of Logic Programming
A non-solution ...
?- soln0.
 » evaluation_error: [goal(_1007 is 1000 * _1008 +
100 * _1009 + 10 * _1010 + _1011),
argument_index(2)]
[Execution aborted]
But this doesn’t work because “is” can only evaluate expressions over
instantiated variables.
?- 5 is 1 + X.
 » evaluation_error: [goal(5 is
1+_64),argument_index(2)]
[Execution aborted]
© O. Nierstrasz
10.7
PS — Applications of Logic Programming
A first solution
So let’s instantiate them first:
digit(0). digit(1). digit(2). digit(3). digit(4).
digit(5). digit(6). digit(7). digit(8). digit(9).
digits([]).
digits([D|L]) :- digit(D), digits(L).
% pick arbitrary digits:
soln1 :- digits([S,E,N,D,M,O,R,E,M,O,N,E,Y]),
A is 1000*S + 100*E + 10*N + D,
B is 1000*M + 100*O + 10*R + E,
C is 10000*M + 1000*O + 100*N + 10*E + Y,
C is A+B,
% check if solution is found
showAnswer(A,B,C).
© O. Nierstrasz
10.8
PS — Applications of Logic Programming
A first solution ...
This is now correct, but yields a trivial solution!
soln1.
 0 + 0 = 0
yes
© O. Nierstrasz
10.9
PS — Applications of Logic Programming
A second (non-)solution
So let’s constrain S and M:
soln2 :- digits([S,M]),
not(S==0), not(M==0),
% backtrack if 0
digits([N,D,M,O,R,E,M,O,N,E,Y]),
A is 1000*S + 100*E + 10*N + D,
B is 1000*M + 100*O + 10*R + E,
C is 10000*M + 1000*O + 100*N + 10*E + Y,
C is A+B,
showAnswer(A,B,C).
© O. Nierstrasz
10.10
PS — Applications of Logic Programming
A second (non-)solution ...
Maybe it works. We’ll never know ...
soln2.
 [Execution aborted]
after 8 minutes still running ...
What went wrong?
© O. Nierstrasz
10.11
PS — Applications of Logic Programming
A third solution
Let’s try to exercise more control by instantiating variables bottom-up:
sum([],0).
sum([N|L], TOTAL) :-
sum(L,SUBTOTAL),
TOTAL is N + SUBTOTAL.
% Find D and C, where ∑L is D + 10*C, digit(D)
carrysum(L,D,C) :- sum(L,S), C is S/10, D is S - 10*C.
?- carrysum([5,6,7],D,C).
 D = 8
C = 1
© O. Nierstrasz
10.12
PS — Applications of Logic Programming
A third solution ...
We instantiate the final digits first, and use the carrysum to constrain
the search space:
soln3 :-
© O. Nierstrasz
digits([D,E]), carrysum([D,E],Y,C1),
digits([N,R]), carrysum([C1,N,R],E,C2),
digit(O), carrysum([C2,E,O],N,C3),
digits([S,M]), not(S==0), not(M==0),
carrysum([C3,S,M],O,M),
A is 1000*S + 100*E + 10*N + D,
B is 1000*M + 100*O + 10*R + E,
C is A+B,
showAnswer(A,B,C).
10.13
PS — Applications of Logic Programming
A third solution ...
This is also correct, but uninteresting:
soln3.

9000 + 1000 = 10000
yes
© O. Nierstrasz
10.14
PS — Applications of Logic Programming
A fourth solution
Let’s try to make the variables unique:
% There are no duplicate elements in the argument list
unique([X|L]) :- not(in(X,L)), unique(L).
unique([]).
in(X, [X|_]).
in(X, [_|L]) :- in(X, L).
?- unique([a,b,c]).
 yes
?- unique([a,b,a]).
 no
© O. Nierstrasz
10.15
PS — Applications of Logic Programming
A fourth solution ...
soln4 :- L1 = [D,E], digits(L1), unique(L1),
carrysum([D,E],Y,C1),
L2 = [N,R,Y|L1], digits([N,R]), unique(L2),
carrysum([C1,N,R],E,C2),
L3 = [O|L2], digit(O), unique(L3),
carrysum([C2,E,O],N,C3),
L4 = [S,M|L3], digits([S,M]),
not(S==0), not(M==0), unique(L4),
carrysum([C3,S,M],O,M),
A is 1000*S + 100*E + 10*N + D,
B is 1000*M + 100*O + 10*R + E,
C is A+B,
showAnswer(A,B,C).
© O. Nierstrasz
10.16
PS — Applications of Logic Programming
A fourth solution ...
This works (at last), in about 1 second on a G3 Powerbook.
soln4.
 9567 + 1085 = 10652
yes
© O. Nierstrasz
10.17
PS — Applications of Logic Programming
Roadmap
1. Search problems
— SEND + MORE = MONEY
2. Symbolic Interpretation
— Definite Clause Grammars
— Interpretation as Proof
— An interpreter for the calculator language
© O. Nierstrasz
10.18
PS — Applications of Logic Programming
2. Symbolic Interpretation
>
Prolog is an ideal language for implementing small
languages:
— Implement BNF using Definite Clause Grammars
— Implement semantic rules directly as Prolog rules
© O. Nierstrasz
10.19
PS — Applications of Logic Programming
Goal-directed interpretation
“ON 0 TOTAL OFF”
Lexer
Input string
[on, 0, total, off]
Parser
Token stream
prog
Parse tree
stmt
expr(0)
© O. Nierstrasz
Interpreter
Output value
[0]
10.20
PS — Applications of Logic Programming
Roadmap
1. Search problems
— SEND + MORE = MONEY
2. Symbolic Interpretation
— Definite Clause Grammars
— Interpretation as Proof
— An interpreter for the calculator language
© O. Nierstrasz
10.21
PS — Applications of Logic Programming
Definite Clause Grammars
Definite clause grammars are an extension of context-free
grammars.
A DCG rule in Prolog takes the general form:
head --> body.
meaning “a possible form for head is body”.
The head specifies a non-terminal symbol, and the body
specifies a sequence of terminals and non-terminals.
© O. Nierstrasz
10.22
PS — Applications of Logic Programming
Definite Clause Grammars ...
Non-terminals may be any Prolog term (other than a
variable or number).
> A sequence of zero or more terminal symbols is written
as a Prolog list. A sequence of ASCII characters can be
written as a string.
> Side conditions containing Prolog goals may be written
in { } braces in the right-hand side of a grammar rule.
>
© O. Nierstrasz
10.23
PS — Applications of Logic Programming
DCG translation
Grammar rules are just syntactic sugar for ordinary Prolog clauses.
Each grammar rule takes an input string, analyses some initial portion,
and produces the remaining portion (possibly enlarged) as output for
further analysis.
p(X) --> q(X).
translates to
p(X, S0, S) :- q(X, S0, S).
Stuff to parse
p(X, Y) -->
q(X),
r(X, Y),
s(Y).
© O. Nierstrasz
translates to
p(X, Y,
q(X,
r(X,
s(Y,
What’s left after parsing
S0, S) :S0, S1),
Y, S1, S2),
S2, S).
10.24
PS — Applications of Logic Programming
Roadmap
1. Search problems
— SEND + MORE = MONEY
2. Symbolic Interpretation
— Definite Clause Grammars
— Interpretation as Proof
— An interpreter for the calculator language
© O. Nierstrasz
10.25
PS — Applications of Logic Programming
Example
This grammar parses an arithmetic expression (made up of
digits and operators) and computes its value.
expr(Z) --> term(X), "+", expr(Y), {Z is X + Y}.
expr(Z) --> term(X), "-", expr(Y), {Z is X - Y}.
expr(X) --> term(X).
term(Z) --> number(X), "*", term(Y), {Z is X * Y}.
term(Z) --> number(X), "/", term(Y), {Z is X / Y}.
term(Z) --> number(Z).
number(C) --> "+", number(C).
number(C) --> "-", number(X), {C is -X}.
number(X) --> [C], {0'0=<C, C=<0'9, X is C - 0'0}.
© O. Nierstrasz
10.26
PS — Applications of Logic Programming
How does it work?
DCG rules are just syntactic sugar for normal Prolog rules.
expr(Z) --> term(X), "+", expr(Y), {Z is X + Y}.
translates to:
expr(Z, S0, S) :term(X, S0, S1),
‘C’(S1,43,S2),
expr(Y, S2, S),
Z is X + Y .
% input and goal
% pass along state
% “+” = [43]
‘C’ is a built-in predicate to recognize terminals.
© O. Nierstrasz
10.27
PS — Applications of Logic Programming
How to use this?
The query
?- expr(Z, "-2+3*5+1", []).
will compute Z=14.
© O. Nierstrasz
10.28
PS — Applications of Logic Programming
Roadmap
1. Search problems
— SEND + MORE = MONEY
2. Symbolic Interpretation
— Definite Clause Grammars
— Interpretation as Proof
— An interpreter for the calculator language
© O. Nierstrasz
10.29
PS — Applications of Logic Programming
Lexical analysis
We can use DCGs for both scanning and parsing.
Our lexer will convert an input atom into a list of tokens:
lex(Atom, Tokens) :name(Atom, String),
scan(Tokens, String, []), !.
scan([T|Tokens]) --> whitespace0, token(T), scan(Tokens).
scan([]) --> whitespace0.
© O. Nierstrasz
10.30
PS — Applications of Logic Programming
Recognizing Tokens
We will represent simple tokens by Prolog atoms:
token(on)
token(total)
token(off)
token(if)
token(last)
token(',')
token('+')
token('*')
token('(')
token(')')
© O. Nierstrasz
-->
-->
-->
-->
-->
-->
-->
-->
-->
-->
"ON".
"TOTAL".
"OFF".
"IF".
"LASTANSWER".
",".
"+".
"*".
"(".
")".
10.31
PS — Applications of Logic Programming
Recognizing Numbers
and a number N by the term num(N):
token(num(N))
--> digits(DL), { asnum(DL, N, 0) }.
digits([D|L])
digits([D])
--> digit(D), digits(L).
--> digit(D).
digit(D)
--> [D], { "0" =< D, D =< "9" }.
How would you implement asnum/3?
© O. Nierstrasz
10.32
PS — Applications of Logic Programming
Concrete Grammar
To parse a language, we need an unambiguous (i.e. concrete)
grammar!
p
s
e
e0
e1
e2
e3
© O. Nierstrasz
::=
::=
|
::=
::=
|
::=
|
::=
|
::=
|
|
‘ON’ s
e ‘TOTAL’ s
e ’ TOTAL’ ‘OFF’
e0
‘IF’ e1 ‘,’ e1 ‘,’ e1
e1
e2 ‘+’ e1
e2
e3 ‘*’ e2
e3
‘LASTANSWER’
num
‘(‘ e0 ‘)’
10.33
PS — Applications of Logic Programming
Parsing with DCGs
The concrete grammar is easily written as a DCG:
prog(S)
stmt([E|S])
stmt([E])
expr(E)
e0(if(Bool, Then, Else))
e0(E)
e1(plus(E1,E2))
e1(E)
e2(times(E1,E2))
e2(E)
e3(last)
e3(num(N))
e3(E)
© O. Nierstrasz
--> [on], stmt(S).
--> expr(E), [total], stmt(S).
--> expr(E), [total, off].
--> e0(E).
--> [if], e1(Bool), [','],
e1(Then), [','], e1(Else).
--> e1(E).
--> e2(E1), ['+'], e1(E2).
--> e2(E).
--> e3(E1), ['*'], e2(E2).
--> e3(E).
--> [last].
--> [num(N)].
--> ['('], e0(E), [')'].
10.34
PS — Applications of Logic Programming
Representing Programs as Parse Trees
We have chosen to represent expressions as Prolog terms, and
programs and statements as lists of terms:
parse(Atom, Tree) :lex(Atom, Tokens),
prog(Tree, Tokens, []).
parse(
'ON (1+2)*(3+4) TOTAL LASTANSWER + 10 TOTAL OFF',
[ times(plus(num(1),num(2)),
plus(num(3),num(4))),
plus(last,num(10))
]).
© O. Nierstrasz
10.35
PS — Applications of Logic Programming
Testing
We exercise our parser with various test cases:
check(Goal) :- Goal, !.
check(Goal) :write('TEST FAILED: '),
write(Goal), nl.
parseTests :check(parse('ON 0 TOTAL OFF', [num(0)])),
...
© O. Nierstrasz
10.36
PS — Applications of Logic Programming
Interpretation as Proof
> One can view the execution of a program as a step-by-
step “proof” that the program reaches some terminating
state, while producing output along the way.
—The program and its intermediate states are represented as
structures (typically, as syntax trees)
—Inference rules express how one program state can be
transformed to the next
© O. Nierstrasz
10.37
PS — Applications of Logic Programming
Building a Simple Interpreter
We define semantic predicates over the syntactic elements of our
calculator language.
peval(S,L)
seval([E], Prev, [Val])
seval([E|S], Prev, [Val|L])
:::-
xeval(num(N), _, N).
xeval(plus(E1,E2), Prev, V)
:-
seval(S,
xeval(E,
xeval(E,
seval(S,
0, L).
Prev, Val).
Prev, Val),
Val, L).
xeval(E1, Prev, V1),
xeval(E2, Prev, V2),
V is V1+V2.
eval(Expr, Val) :- parse(Expr, Tree), peval(Tree, Val).
eval('ON (1+2)*(3+4) TOTAL LASTANSWER + 10 TOTAL OFF', X).
 X = [21, 31]
© O. Nierstrasz
10.38
PS — Applications of Logic Programming
Testing the interpreter
We similarly define tests for the interpreter.
evalTests :check(eval('ON 0 TOTAL OFF', [0])),
check(eval('ON 5 + 7 TOTAL OFF', [12])),
...
© O. Nierstrasz
10.39
PS — Applications of Logic Programming
A top-level script
Finally, we can package the interpreter as a ciao module,
and invoke it from a script:
#!/bin/sh
exec ciao-shell $0 "$@" # -*- mode: ciao; -*:- use_module(calc, [eval/2, test/0]).
main([])
:- test.
main(Argv)
:- doForEach(Argv).
doForEach([]).
doForEach([Arg|Args]) :write(Arg), nl,
eval(Arg, Val),
write(Val), nl,
doForEach(Args).
© O. Nierstrasz
10.40
PS — Applications of Logic Programming
What you should know!
 What are definite clause grammars?
 How are DCG specifications translated to Prolog?
 Why are abstract grammars inappropriate for parsing?
 Why are left-associative grammar rules problematic?
 How can we represent syntax trees in Prolog?
© O. Nierstrasz
10.41
PS — Applications of Logic Programming
Can you answer these questions?
 What happens when we ask digits([A,B,A])?
 How many times will soln2 backtrack before finding a






solution?
How would you check if the solution to the puzzle is
unique?
How would you generalize the puzzle solution to solve
arbitrary additions?
Why must DCG side conditions be put in { curly
brackets }?
What exactly does the ‘C’ predicate do?
Why do we need a separate lexer?
How would you implement an interpreter for the
assignment language we defined earlier?
© O. Nierstrasz
10.42
PS — Applications of Logic Programming
License
>
http://creativecommons.org/licenses/by-sa/2.5/
Attribution-ShareAlike 2.5
You are free:
• to copy, distribute, display, and perform the work
• to make derivative works
• to make commercial use of the work
Under the following conditions:
Attribution. You must attribute the work in the manner specified by the author or licensor.
Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting
work only under a license identical to this one.
• For any reuse or distribution, you must make clear to others the license terms of this work.
• Any of these conditions can be waived if you get permission from the copyright holder.
Your fair use and other rights are in no way affected by the above.
© O. Nierstrasz
10.43
Download