Download presentation
Presentation is loading. Please wait.
Published byAdrian Weeks Modified over 10 years ago
1
CS4026 Formal Models of Computation Part II The Logic Model Lecture 6 – Arithmetic, fail and the cut
2
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 ? -
3
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 + 4 Res = 7
4
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. :)
5
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: –1 st argument is the list –2 nd 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 ? ?-
6
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 zero length(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 zero length(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 zero length(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
7
formal models of computation 7 Arithmetic: an Example Second attempt (Can you see whats wrong?) length(L,S):- % S is the length of list L L = [], % if L is an empty list S = 0. % its length S is zero length(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 zero length(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 wont yet exist!!
8
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 zero length(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 0 length([_|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 0 length([_|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 dont care what the value of the element is – we just want to count them!!
9
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).
10
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
11
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(X 1 ),write(X 1 ),nl,fail. loopFail:- p(X 1 ),write(X 1 ),nl,fail. loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /a} loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /a} ?- loopFail. a loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /a} Backtrack!! loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /a} Backtracking skips over built-ins!! loopFail:- p(X 1 ),write(X 1 ),nl,fail. loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /b} loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /b} loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /b} Backtrack!! loopFail:- p(X 1 ),write(X 1 ),nl,fail. loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /c} loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /c} loopFail:- p(X 1 ),write(X 1 ),nl,fail. {X 1 /c} Backtrack!! loopFail:- p(X 1 ),write(X 1 ),nl,fail. No more values for p(X) ! loopFail. ?- loopFail. a b ?- loopFail. a b c ?- loopFail. a b c yes ?-
12
formal models of computation 12 Controlling Backtracking via cuts (!) Prologs 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).
13
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(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /a} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /a} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /a} Fail!!Backtrack… r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b,Y 1 /2} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b,Y 1 /2} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b,Y 1 /2} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b,Y 1 /2} ?- r(Ans). Ans = 2 ? ?- r(Ans). Ans = 2 ? ; Force backtrack… r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b,Y 1 /2} r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b,Y 1 /2} No other proof for s(Y 1 ) ! r(Y 1 ):- p(X 1 ),q(X 1,Y 1 ),!,s(Y 1 ). {X 1 /b,Y 1 /2} Cannot backtrack over ! ?- r(Ans). Ans = 2 ? ; no ?- Force backtrack…
14
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 Lets look at some ways in which the cut can be useful
15
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).
16
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).
17
formal models of computation 17 Avoiding unnecessary work with ! Another type of case: disjoint cases In Prolog: Lets 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 2. ?- 0 > 2. Backtrack!
18
formal models of computation 18 Avoiding unnecessary work with ! Another (more concrete) case: In Prolog: Lets 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 2. Backtrack!
19
formal models of computation 19 Avoiding unnecessary work with ! Another (more concrete) case: In Prolog: Lets 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. no Fail!!
20
formal models of computation 20 Avoiding unnecessary work with ! The values of Y are mutually exclusive –There is no point in trying different clauses!! –Lets 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 2. ?- 0 > 2. Backtrack! ?- range(1,Y), Y > 2. no Fail!!
21
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). 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.
22
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). Nos = [5,4,3,2,1] ? ?- countDown(5,Nos). Nos = [5,4,3,2,1] ? ; no ?-
23
formal models of computation 23 When to use cuts (!) Cuts arent always necessary – dont 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…
24
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(_,_).
25
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
26
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
27
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!
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.