X 1 /a

CS4026
Formal Models of Computation
Part II
The Logic Model
Lecture 6 – Arithmetic, fail and the cut
Arithmetic
• Arithmetic expressions trees like any other terms:
?- X = 10 + (2 * 4).
X = 10 + (2 * 4) ?
yes
? -
X
+
*
10
2
4
• 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 is 10 + (2 * 4).
X = 18 ?
yes
? -
formal models of computation
2
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 + 4
Res = 7
formal models of computation
3
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
4
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
5
Arithmetic: an Example
• First attempt
length(L,S):length(L,S):% S is the length of list L
LL == [],
[],
%% if
if LL is
is an
an empty
empty list
list
SS == 0.
0.
%% its
its length
length SS is
is zero
zero
length(L,S):length(L,S):%% otherwise
otherwise
LL == [X|Xs],
[X|Xs],
%% if
if LL is
is aa list
list [X|Xs]
[X|Xs]
SS is
is 11 ++ length(Xs,S).
length(Xs,S). %% its
its length
length is
is 11 plus
plus length
length of
of tail
tail
Careful! Predicate calls are true or false!
This expression does not make sense!
formal models of computation
6
Arithmetic: an Example
• Second attempt
(Can you see what’s wrong?)
length(L,S):L = [],
S = 0.
length(L,S):L = [X|Xs],
S is 1 + SXs,
length(Xs,SXs).
%
%
%
%
%
%
%
S is the length of list L
if L is an empty list
its length S is zero
otherwise
if L is a list [X|Xs]
length S is 1 plus length of tail
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
7
Arithmetic: an Example
• Third attempt:
length(L,S):L = [],
S = 0.
length(L,S):L = [X|Xs],
length(Xs,SXs),
S is 1 + SXs.
%
%
%
%
%
%
%
S is the length of list L
if L is an empty list
its length S is zero
otherwise
if L is a list [X|Xs]
get the length SXs of its tail Xs
add 1 to the length of its tail
• Simplified version:
length([],0).
length([_|Xs],S):length(Xs,SXs),
S is 1 + SXs.
%
%
%
%
the empty list has length 0
a non-empty list [_|Xs] has size S
get the length SXs of its tail Xs
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
8
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
9
Failure-driven loops
• Prolog offers a built-in predicate fail which
always fails: ?- fail.
no
• We can use this predicate to define a failuredriven loop, an alternative to recursion:
p(a).
p(b).
p(c).
loopFail:p(X),
write(X),
nl,
fail.
loopFail.
% loopFail succeeds if
%
we can prove p(X) and
%
write the value of X and
%
skip a line.
%
fail and BACKTRACK!!
% if no (more) answers, stop
formal models of computation
10
Failure-driven loops
• Execution:
p(a).
p(b).
p(c).
loopFail:p(X),
write(X),
nl,
fail.
loopFail.
?- loopFail.
a
b
c
yes
?-
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.
{X1/b}
Backtrack!!
loopFail:p(X1),write(X1),nl,fail.
{X1/c}
Backtrack!!
loopFail:- No more values for p(X)!
p(X1),write(X1),nl,fail.
loopFail.
formal models of computation
11
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
12
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).
Ans = 2 ? ;
no
?-
Force backtrack…
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/a}
Fail!!Backtrack…
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y
/b} 1/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} No other proof for s(Y1)!
r(Y1):- p(X1),q(X1,Y1),!,s(Y1).
{X1/b,Y1/2} Cannot backtrack over “!”
formal models of computation
13
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
14
Avoiding unnecessary work with “!”
• Example:
prize(X):- customer(X),eligiblePrize(X).
• 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?
formal models of computation
15
Avoiding unnecessary work with “!”
• Surely, this is a lot better:
prize(X):- customer(X),!,eligiblePrize(X).
• 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…
formal models of computation
16
Avoiding unnecessary work with “!”
• Another type of case: “disjoint” cases
if X < 3 then Y = 0.
if X  3 and X < 6 then Y = 1.
if X  6 then Y = 2.
• In Prolog:
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
• Let’s try this:
?- range(1,Y), Y > 2.
?- 1 < 3, 0 > 2.
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
?- 0 > 2.
Backtrack!
formal models of computation
17
Avoiding unnecessary work with “!”
• Another (more concrete) case:
if X < 3 then Y = 0.
if X  3 and X < 6 then Y = 1.
if X  6 then Y = 2.
• In Prolog:
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
• Let’s try this:
?- range(1,Y), Y > 2.
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
18
Avoiding unnecessary work with “!”
• Another (more concrete) case:
if X < 3 then Y = 0.
if X  3 and X < 6 then Y = 1.
if X  6 then Y = 2.
• In Prolog:
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
• Let’s try this:
?- range(1,Y), Y > 2.
?- 1 >= 6, 2 > 2.
?- range(1,Y), Y > 2.
no
range(X,0):- X < 3.
range(X,1):- X >= 3, X < 6.
range(X,2):- X >= 6.
Backtrack!
Fail!!
formal models of computation
19
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:
range(X,0):- X < 3,!.
range(X,1):- X >= 3, X < 6,!.
range(X,2):- X >= 6.
% no need to add a cut here!!
• The previous query is:
?- range(1,Y), Y > 2.
?- 1 < 3, 0 > 2.
?- 0 > 2.
?- range(1,Y), Y > 2.
no
range(X,0):- X < 3,!.
range(X,1):- X >= 3, X < 6,!
range(X,2):- X >= 6.
Backtrack!
Fail!!
formal models of computation
20
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).
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
21
Avoiding “bad” loops with cuts
• Fixing the problem with a “!”:
Incidentally…
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?
?- countDown(5,Nos).
Nos = [5,4,3,2,1] ? ;
no
?-
formal models of computation
22
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
23
Cut-Fail Combination
• We can combine “!” and “fail” to represent
exceptions:
different(X,X):- !,fail.
different(_,_).
– 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!!
formal models of computation
24
Negation as Failure
• The cut-fail combination allows us to define a kind
of logical negation:
– not(Goal) is true if Goal is false
not(G):- call(G),!,fail. % if G holds, then fail
not(_).
% otherwise not(G) holds
– 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”
formal models of computation
25
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
26
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!
formal models of computation
27