Formal Models of Computation Part II The Logic Model
description
Transcript of Formal Models of Computation Part II The Logic Model
CS4026Formal Models of
ComputationPart II
The Logic ModelLecture 6 – Arithmetic, fail and the
cut
formal models of computation 2
Arithmetic• Arithmetic expressions trees like any other
terms:
• Prolog does not treat arithmetic expressions differently, unless asked to do so.
• In order to evaluate arithmetic expressions we use the built-in “is”
?- X = 10 + (2 * 4).
X = 10 + (2 * 4) ?
yes
? -
X
4
10
+
2
*
?- X is 10 + (2 * 4).
X = 18 ?
yes
? -
formal models of computation 3
Arithmetic• The syntax of the built-in “is” is:
Variable is ArithmeticExpression• For instance:
Result is (10 * 7) NewRes is OldRes + 1
• Variables that appear on the expression– Must be instantiated when expression is
evaluated– Otherwise execution error!
• We can delay the evaluation until all variables have value:?- Exp = A + B, A = 3, B = 4, Res is Exp.A = 3,B = 4,Exp = 3 + 4Res = 7
formal models of computation 4
Evaluation in Haskell and Prolog• In Haskell, expressions are always evaluated
if they contribute to the final result. Terms stand for their results.
• In Prolog, every term stands for itself. Evaluation only happens when explicitly invoked by a built-in like “is”.
• Prolog functors (even arithmetic ones) are like Haskell constructors (e.g. :)
formal models of computation 5
Arithmetic: an Example• How can we find the size (no. elements) of a
list?• Simple recursive formulation:
– The number of elements of the empty list is zero– The number of elements of a list [X|Xs] is one
plus the number of elements of Xs.• Predicate length with 2 arguments:
– 1st argument is the list– 2nd argument is the size of the list
• For example:?- length([a,b,c,d],Length).Length = 4 ??- length([1,[2,3],4],L).L = 3 ??-
formal models of computation 6
Arithmetic: an Example• First attempt
length(L,S):- L = [], % if L is an empty list S = 0. % its length S is zerolength(L,S):- % otherwise L = [X|Xs], % if L is a list [X|Xs] S is 1 + length(Xs,S). % its length is 1 plus length of tail
length(L,S):- % S is the length of list L L = [], % if L is an empty list S = 0. % its length S is zerolength(L,S):- % otherwise L = [X|Xs], % if L is a list [X|Xs] S is 1 + length(Xs,S). % its length is 1 plus length of tail
Careful! Predicate calls are true or false!This expression does not make sense!
length(L,S):- % S is the length of list L L = [], % if L is an empty list S = 0. % its length S is zerolength(L,S):- % otherwise L = [X|Xs], % if L is a list [X|Xs] S is 1 + length(Xs,S). % its length is 1 plus length of tail
formal models of computation 7
Arithmetic: an Example• Second attempt
(Can you see what’s wrong?)
length(L,S):- % S is the length of list L L = [], % if L is an empty list S = 0. % its length S is zerolength(L,S):- % otherwise L = [X|Xs], % if L is a list [X|Xs] S is 1 + SXs, % length S is 1 plus length of tail length(Xs,SXs). % get the length SXs of tail Xs
length(L,S):- % S is the length of list L L = [], % if L is an empty list S = 0. % its length S is zerolength(L,S):- % otherwise L = [X|Xs], % if L is a list [X|Xs] S is 1 + SXs, % length S is 1 plus length of tail length(Xs,SXs). % get the length SXs of tail Xs
Careful! When this expression is evaluated, the value of SXs won’t yet exist!!
formal models of computation 8
Arithmetic: an Example• Third attempt:
• Simplified version:
length(L,S):- % S is the length of list L L = [], % if L is an empty list S = 0. % its length S is zerolength(L,S):- % otherwise L = [X|Xs], % if L is a list [X|Xs] length(Xs,SXs), % get the length SXs of its tail Xs S is 1 + SXs. % add 1 to the length of its tail
length([],0). % the empty list has length 0length([_|Xs],S):- % a non-empty list [_|Xs] has size S length(Xs,SXs), % get the length SXs of its tail Xs S is 1 + SXs. % add 1 to the length of its tail
length([],0). % the empty list has length 0length([_|Xs],S):- % a non-empty list [_|Xs] has size S length(Xs,SXs), % get the length SXs of its tail Xs S is 1 + SXs. % add 1 to the length of its tail
Anonymous variable, used as “place holder”. In this context, we don’t care what the value of the element is – we just want to count them!!
formal models of computation 9
Arithmetic: Summary• Arithmetic: via “is” built-in.• Some operators:
+, –, *, / (add, subtract, multiply, divide)// (integer division)mod (modulo)
• All variables on expression must have values, otherwise execution error!!
• Because of this restriction, we ought to bear in mind the order in which Prolog proves/executes the body of a clause (or a query).
formal models of computation 10
Failure-driven loops• Prolog offers a built-in predicate fail
which always fails:
• We can use this predicate to define a failure-driven loop, an alternative to recursion:
?- fail.
no
p(a).
p(b).
p(c).
loopFail:- % loopFail succeeds if
p(X), % we can prove p(X) and
write(X), % write the value of X and
nl, % skip a line.
fail. % fail and BACKTRACK!!
loopFail. % if no (more) answers, stop
formal models of computation 11
Failure-driven loops• Execution:
p(a).
p(b).
p(c).
loopFail:-
p(X),
write(X),
nl,
fail.
loopFail.
?- loopFail.
loopFail:-
p(X1),write(X1),nl,fail.
loopFail:-
p(X1),write(X1),nl,fail.
loopFail:-
p(X1),write(X1),nl,fail.
{X1/a}
loopFail:-
p(X1),write(X1),nl,fail.
{X1/a}
?- loopFail.
a
loopFail:-
p(X1),write(X1),nl,fail.
{X1/a} Backtrack!!
loopFail:-
p(X1),write(X1),nl,fail.
{X1/a} Backtracking skips over built-ins!!
loopFail:-
p(X1),write(X1),nl,fail.
loopFail:-
p(X1),write(X1),nl,fail.
{X1/b}
loopFail:-
p(X1),write(X1),nl,fail.
{X1/b}
loopFail:-
p(X1),write(X1),nl,fail.
{X1/b} Backtrack!!
loopFail:-
p(X1),write(X1),nl,fail.
loopFail:-
p(X1),write(X1),nl,fail.
{X1/c}
loopFail:-
p(X1),write(X1),nl,fail.
{X1/c}
loopFail:-
p(X1),write(X1),nl,fail.
{X1/c} Backtrack!!
loopFail:-
p(X1),write(X1),nl,fail.
No more values for p(X)!
loopFail.
?- loopFail.
a
b
?- loopFail.
a
b
c
?- loopFail.
a
b
c
yes
?-
formal models of computation 12
Controlling Backtracking via cuts (!)• Prolog’s backtracking mechanism may, in
some cases, lead to inefficiencies.• We can control backtracking via the built-in
“!”, called “cut”.• The “!” is used as an ordinary predicate, in
the body of the clause or query – it always succeeds!
• However, the “!” causes the execution to commit to the current clause and to the solutions (proofs) of the goals to its left.
• Example:p(A,B,C,D):- q(A), r(A,B), !, s(B,C),t(A,D).
formal models of computation 13
Controlling Backtracking via cuts (!)• Example:
p(a).
p(b).
p(c).
q(b,2).
q(c,3).
s(2).
s(3).
r(Y):- p(X),q(X,Y),!,s(Y).
?- r(Ans).
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).r(Y1):- p(X1),q(X1,Y1),!,s(Y1).r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/a}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/a}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/a} Fail!!Backtrack…
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2}?- r(Ans).
Ans = 2 ?
?- r(Ans).
Ans = 2 ? ; Force backtrack…
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2}
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2} No other proof for s(Y1)!
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2} Cannot backtrack over “!”
?- r(Ans).
Ans = 2 ? ;
no
?-
Force backtrack…
formal models of computation 14
Commitment in Haskell and Prolog• In some ways, the Prolog cut (!) is similar in
spirit to a Haskell guard (|)– Both cause a commitment to the current
clause/case• But there are differences as well:
– Guards come before the critical tests, cuts come after
– Cuts also commit to choices made in subgoals on the left
• Let’s look at some ways in which the cut can be useful
formal models of computation 15
Avoiding unnecessary work with “!” • Example:
• Suppose– customer(X) picks out a customer from a
database sorted (decreasing order) by amount of money spent;
– eligiblePrize(X) checks if amount of money spent makes customer eligible to win a prize;
– If the best (first) customer does not qualify, why let Prolog try all the other 250000 customers with less money spent?
prize(X):- customer(X),eligiblePrize(X).
formal models of computation 16
Avoiding unnecessary work with “!” • Surely, this is a lot better:
• This new version saves 250000 unnecessary attempts!
• We can only add this cut because we know the first answer is the only one that is any good…
prize(X):- customer(X),!,eligiblePrize(X).
formal models of computation 17
Avoiding unnecessary work with “!” • Another type of case: “disjoint” cases
• In Prolog:
• Let’s try this:
if X < 3 then Y = 0.if X 3 and X < 6 then Y = 1.if X 6 then Y = 2.
?- range(1,Y), Y > 2.
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.?- 1 < 3, 0 > 2.
?- 0 > 2.
Backtrack!
formal models of computation 18
Avoiding unnecessary work with “!” • Another (more concrete) case:
• In Prolog:
• Let’s try this:
if X < 3 then Y = 0.if X 3 and X < 6 then Y = 1.if X 6 then Y = 2.
?- range(1,Y), Y > 2.
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.?- 1>=3, 1<6, 1 > 2.
Backtrack!
formal models of computation 19
Avoiding unnecessary work with “!” • Another (more concrete) case:
• In Prolog:
• Let’s try this:
if X < 3 then Y = 0.if X 3 and X < 6 then Y = 1.if X 6 then Y = 2.
?- range(1,Y), Y > 2.
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.?- 1 >= 6, 2 > 2. Backtrack!
?- range(1,Y), Y > 2.
noFail!!
formal models of computation 20
Avoiding unnecessary work with “!” • The values of Y are mutually exclusive
– There is no point in trying different clauses!!– Let’s add cuts to reflect this:
• The previous query is:
?- range(1,Y), Y > 2.
range(X,0):- X < 3,!.
range(X,1):- X >= 3, X < 6,!.
range(X,2):- X >= 6. % no need to add a cut here!!
range(X,0):- X < 3,!.
range(X,1):- X >= 3, X < 6,!
range(X,2):- X >= 6.?- 1 < 3, 0 > 2.
?- 0 > 2. Backtrack!
?- range(1,Y), Y > 2.
noFail!!
formal models of computation 21
Cuts can be necessary: Avoiding bad loops• Example: build a list with decreasing
numbers countDown(0,[]).
countDown(N,[N|Ns]):-
NN is N – 1,
countDown(NN,Ns).
?- countDown(5,Nos).?- countDown(5,Nos).
Nos = [5,4,3,2,1] ?
?- countDown(5,Nos).
Nos = [5,4,3,2,1] ? ;
?- countDown(5,Nos).
Nos = [5,4,3,2,1] ? ;
?- countDown(5,Nos).
Nos = [5,4,3,2,1] ? ;
?- countDown(5,Nos).
Nos = [5,4,3,2,1] ? ;
LOOP!!
Cause: Being asked for more solutions, PROLOG will match countDown(0,[]) with the recursive rule. This will force it to compute countDown(-1,Ns), and so on until the stack overflows.
formal models of computation 22
Avoiding “bad” loops with cuts• Fixing the problem with a “!”:
countDown(0,[]):-
!.
countDown(N,[N|Ns]):-
NN is N – 1,
countDown(NN,Ns).
The new countDown on the left still has a problem: if we try?- countDown(-1,L).
The program would loop forever (actually, a stack overflow will stop it). Can you fix it?
Incidentally…
?- countDown(5,Nos).?- countDown(5,Nos).
Nos = [5,4,3,2,1] ?
?- countDown(5,Nos).
Nos = [5,4,3,2,1] ? ;
no
?-
formal models of computation 23
When to use cuts (!)• Cuts aren’t always necessary – don’t add
them “just in case”!! • There is usually no reason to add more than
one “!” on one clause.• There is no easy way to tell when to use cuts:
– You have seen some cases as guidelines…– Is the first answer enough? Do we need all
answers?– Is there a risk of an accidental loop?– Will backtracking allow clauses to be wrongly
used?• Ultimately, we (programmers) should be able
to decide where/if to add cuts…
formal models of computation 24
Cut-Fail Combination• We can combine “!” and “fail” to represent
exceptions:
– First clause defines when different fails– Second clause is an “else”, where all other cases
are dealt;– Notice the anonymous variables – they are not the
same!!
different(X,X):- !,fail.
different(_,_).
formal models of computation 25
Negation as Failure• The cut-fail combination allows us to define a
kind of logical negation:– not(Goal) is true if Goal is false
– Built-in call(Goal) attempts to prove Goal• For the in-house logicians:
– not(G) means “G cannot be proved”– not(G) does not mean “G can be proved false”– Can you tell these apart?
• This is called “negation as failure”– It is OK if we adopt the “closed world assumption”
not(G):- call(G),!,fail. % if G holds, then fail
not(_). % otherwise not(G) holds
formal models of computation 26
Declarative vs. Procedural Meanings• A Prolog program has two “meanings”
– Declarative: the logical relationships (i.e. the results)
– Procedural: the results and how they were computed
• Good Prolog programs:– Exploit the declarative side of logics
(relationships)– Take into account procedural aspects (efficiency).
• Declarative programs:– Allow for multiple answers and multiple uses of a
predicate (e.g., the member predicate)• Procedural programs:
– Single answers, single use of predicates
formal models of computation 27
Control in Logic Programming• In logic programming, you have to think
about control as well as logic:– To appropriately “time” arithmetic– To avoid loops– To avoid unnecessary backtracking
• The cut is an explicit control mechanism. The ordering of clauses and goals within clauses is less explicit but just as important
• But you also need to think about the logic!