720 likes | 863 Views
Programming Language Concepts, COSC-3308-01 Lecture 5 . Iteration, Recursion, Exceptions, Type Notation, and Design Methodology. Reminder of the Last Lecture. Computing with procedures lexical scoping closures procedures as values procedure call Introduction of
E N D
Programming Language Concepts, COSC-3308-01Lecture 5 Iteration, Recursion, Exceptions, Type Notation, and Design Methodology COSC-3308-01, Lecture 5
Reminder of the Last Lecture • Computing with procedures • lexical scoping • closures • procedures as values • procedure call • Introduction of • properties of the abstract machine • last call optimization • full syntax to kernel syntax COSC-3308-01, Lecture 5
Overview • Last Call Optimization • Recursion versus Iteration • Tupled Recursion • Exceptions • Type Notation • Constructing programs by following the type • Design methodology (standalone applications) COSC-3308-01, Lecture 5
Recursion: Summary • Iterative computations run in constant space • This concept is also called last call optimization • no space needed for last call in procedure body COSC-3308-01, Lecture 5
Power Function: Inductive Definition • We know (from school or university) 1 , if n is zero xn = x xn-1 , n>0 COSC-3308-01, Lecture 5
Recursive Function fun {Pow X N} if N==0 then 1 else X*{Pow X N-1} end end • Is this function using last call optimization (a.k.a. tail-recursive)? • not an iterative function • uses stack space in order of N COSC-3308-01, Lecture 5
How Does Pow Compute? • Consider {Pow 5 3}, schematically: {Pow 5 3} = 5*{Pow 5 2} = 5*(5*{Pow 5 1}) = 5*(5*(5*{Pow 5 0}))) = 5*(5*(5*1))) = 5*(5*5) = 5*25 = 125 COSC-3308-01, Lecture 5
Better Idea for Pow • Take advantage of fact that multiplication can be reordered (that is, a*(b*c)=c*(a*b)) {Pow 5 3}* 1 = {Pow 5 2}* (5*1) = {Pow 5 2}* 5 = {Pow 5 1}* (5*5) = {Pow 5 1}* 25 = {Pow 5 0}* (5*25) = {Pow 5 0}* 125 = 1*125 = 125 • Technique: accumulator for intermediate result COSC-3308-01, Lecture 5
Using Accumulators • Accumulator stores intermediate result • Finding an accumulator amounts to finding a state invariant • A state invariant is a property (predicate) that is valid on all states • State invariant must hold initially • A recursive call transforms one valid state into another • Final result must be obtainable from state invariant. COSC-3308-01, Lecture 5
So What Is the State for Pow {Pow 5 3}* 1 (5, 3, 1) {Pow 5 2}* (5*1) (5, 2, 5) {Pow 5 1}* (5*5) (5, 1, 25) {Pow 5 0}* (5*25) (5, 0, 125) • Technique: accumulator for intermediate result COSC-3308-01, Lecture 5
{PowerAcc X N Acc} = X^N * Acc {PowerAcc X N-1 X*Acc}= X^(N-1)*(X*Acc) The PowAcc Function fun {PowAcc X N Acc} if N==0 then Acc else {PowAcc X N-1 X*Acc} end end • Initial call is{PowAcc X N 1} {PowerAcc X N 1} = X^N COSC-3308-01, Lecture 5
Pow: Complete Picture (PowA) declare local fun {PowAcc X N Acc} if N==0 then Acc else {PowAcc X N-1 X*Acc} end end in fun {PowA X N} {PowAcc X N 1} end end • X and N are integers, because they are operands in operations with 1. COSC-3308-01, Lecture 5
Pow: Complete Picture (PowA) declare PowA local PowAcc in PowAcc = fun {$ X N Acc} if N==0 then Acc else {PowAcc X N-1 X*Acc} end end PowA = fun {$ X N} {PowAcc X N 1} end end COSC-3308-01, Lecture 5
Is PowA() a Correct Function? • Actually, … no, because it doesn’t cover the whole domain of integers … • the call {PowA 2 ~4} will lead to an infinite loop COSC-3308-01, Lecture 5
Characteristics of PowA • It has a tight scope, since PowAcc is visible only to PowA • no other program could accidently use PowAcc • PowAcc belongs to PowA • Tight scope is important to conserve namespace and avoid clash of identifiers. • Possible only very recently in… • C++ “namespace” (took some twenty years) • Java “inner classes” (took a major language revision) COSC-3308-01, Lecture 5
Reverse • Reversing a list • How to reverse the elements of a list {Reverse [a b c d]} returns [d c b a] COSC-3308-01, Lecture 5
Reversing a List • Reverse of nil is nil • Reverse of X|Xr is Z, where reverse of Xr is Yr, and append Yr and [X] to get Z {Rev [a b c d]}= {Rev a|[b c d]}={Append {Rev [b c d]} [a]} {Rev b|[c d]}={Append {Rev [c d]} [b]} {Rev c|[d]}={Append {Rev [d]} [c]} {Rev d|nil}={Append {Rev nil} [d]} =[d c b a] =[d c b a] =[d c b] =[d c] =[d] nil COSC-3308-01, Lecture 5
Question • What is correct {Append {Reverse Xr} X} or {Append {Reverse Xr} [X]} COSC-3308-01, Lecture 5
Naive Reverse Function fun {NRev Xs} case Xs of nil then nil [] X|Xr then {Append {NRev Xr} [X]} end end COSC-3308-01, Lecture 5
Question • What is the problem with the naive reverse? • Possible answers • not tail recursive • Append is costly: • there are O{|L1|} calls fun {Append L1 L2} case L1 of nil then L2 [] H|T then H|{Append T L2} end end COSC-3308-01, Lecture 5
Cost of Naive Reverse • Suppose a recursive call {NRev Xs} • where {Length Xs}=n • assume cost of {NRev Xs} is c(n) number of function calls • then c(0) = 0 c(n) = c({Append {NRev Xr} [X]}) + c(n-1) = (n-1) + c(n-1) = (n-1) + (n-2) + c(n-2) =…= n-1 + (n-2) + … + 1 • this yields: c(n) = • For a list of length n, NRev uses approx. O(n2)calls! COSC-3308-01, Lecture 5
Doing Better for Reverse • Use an accumulator to capture currently reversed list • Some abbreviations • {IR Xs} for {IterRev Xs} • Xs ++ Ysfor {Append Xs Ys} COSC-3308-01, Lecture 5
Computing NRev {NRev [a b c]} = {NRev [b c]}++[a] = ({NRev [c]}++[b])++[a] = (({NRev nil}++[c])++[b])++[a] = ((nil++[c])++[b])++[a] = ([c]++[b])++[a] = [c b]++[a] = [c b a] COSC-3308-01, Lecture 5
Computing IterRev (IR) {IR [a b c] nil} = {IR [b c] a|nil} = {IR [c] b|a|nil} = {IR nil c|b|a|nil} = [c b a] • The general pattern: {IR X|Xr Rs} {IR Xr X|Rs} COSC-3308-01, Lecture 5
Why is Iteration Possible? • Associative Property {Append {Append RL [a]} [b]} = {Append RL {Append [a] [b]}} • More Generally {Append {Append RL [a]} Acc} = {Append RL {Append [a] Acc}} = {Append RL a|Acc} COSC-3308-01, Lecture 5
IterRev Intermediate Step fun {IterRev Xs Ys} case Xs of nil then Ys [] X|Xr then {IterRev Xr X|Ys} end end • Is tail recursive now COSC-3308-01, Lecture 5
IterRev Properly Embedded local fun {IterRev Xs Ys} case Xs of nil then Ys [] X|Xr then {IterRev Xr X|Ys} end end in fun {Rev Xs} {IterRev Xs nil} end end COSC-3308-01, Lecture 5
State Invariant for IterRev • Unroll the iteration a number of times, we get: {IterRev [x1 … xn] W} = {IterRev [xi+1 … xn] [xi … x1]++W} COSC-3308-01, Lecture 5
Reasoning for IterRev and Rev • Correctness: {Rev Xs} is {IterRev Xs nil} • Using the state invariant, we have: {IterRev [x1 … xn] nil}= = {IterRevnil [xn … x1]} = [xn … x1] • Thus: {Rev [x1 … xn]}=[xn … x1] • Complexity: • The number of calls for {IterRev L nil}, where list L has N elements, is c(N)=N COSC-3308-01, Lecture 5
Summary So Far • Use accumulators • yields iterative computation • find state invariant • Loop = Tail Recursion and is a special case of general recursion. • Exploit both kinds of knowledge • on how programs execute (abstract machine) • on application/problem domain COSC-3308-01, Lecture 5
Tupled Recursion Functions with multiple results COSC-3308-01, Lecture 5
Computing Average fun {SumList Ls} case Ls of nil then 0 [] X|Xs then X+{SumList Xs} end end fun {Length Ls} case Ls of nil then 0 [] X|Xs then 1+{Length Xs} end end fun {Average Ls} {SumList Ls} div {Length Ls} end • What is the Problem? COSC-3308-01, Lecture 5
Problem? • Traverse the same list multiple times. • Solution: compute multiple results in a single traversal! COSC-3308-01, Lecture 5
Tupling - Computing Two Results fun {CPair Ls} {Sum Ls}#{Length Ls} end fun {CPair Ls} case Ls of nil then 0#0 [] X|Xs thencase {CPair Xs} of S#L then (X+S)#(1+L) end end end COSC-3308-01, Lecture 5
Using Tupled Recursion fun {Average Ls} {Sum Ls} div {Length Ls} end fun {Average Ls} case {CPair Ls} of S#L then S div L end end • Note: Division by zero should also be considered. COSC-3308-01, Lecture 5
Exceptions • An error is a difference between the actual behavior of a program and its desired behavior. • Type of errors: • Internal: invoking an operation with an illegal type or illegal value • External: opening a nonexisting file • We want detect these errors and handle them, without stoping the program execution. • The execution is transferred to the exception handler, and pass the exception handler a value that describes the error. COSC-3308-01, Lecture 5
Exceptions handling • An Oz program is made up of interacting “components” organized in hierarchical fashion. • The mechanism causes a “jump” from inside the component to its boundary. • This jump should be a single operation. • The mechanism should be able, in a single operation, to exit from arbitrarily many levels of nested contexts. • A context is an entry on the semantic stack, i.e., an instruction that has to be executed later. • Nested contexts are created by procedure calls and sequential compositions. COSC-3308-01, Lecture 5
Exceptions handling COSC-3308-01, Lecture 5
Exceptions (Example) fun {Eval E} if {IsNumber E} then E else case E of plus(X Y) then {Eval X}+{Eval Y} [] times(X Y) then {Eval X}*{Eval Y} else raise illFormedExpression(E) end end end end try {Browse {Eval plus(plus(5 5) 10)}} {Browse {Eval times(6 11)}} {Browse {Eval minus(7 10)}} catch illFormedExpression(E) then {Browse '*** Illegal expression '#E#' ***'} end COSC-3308-01, Lecture 5
Exceptions (Example) COSC-3308-01, Lecture 5
Exceptions. try and raise • try: creates an exception-catching context together with an exception handler. • raise: jumps to the boundary of the innermost exception-catching context and invokes the exception handler there. • try<s>catch <x>then <s>1end: • if <s>does not raise an exception, then execute <s>. • if <s>raises an exception, i.e., by executing a raisestatement, then the (still ongoing) execution of <s>is aborted. All information related to <s>is popped from the semantic stack. Control is transferred to <s>1, passing it a reference to the exception in <x>. COSC-3308-01, Lecture 5
Exceptions. Full Syntax • A trystatement can specify a finallyclause which is always executed, whether the statement raises an exception or not. • try <s>1finally<s>2end is equivalent to: • try <s>1 catchXthen <s>2 raise X end end <s>2 where an identifier X is chosen that is not free in <s>2 COSC-3308-01, Lecture 5
Exceptions. Full Syntax (Example 1) declare One Two fun {One} 1 end fun {Two} 2 end try {Browse a} {One}={Two} catch X then {Browse b} raise X end end {Browse b} declare One Two fun {One} 1 end fun {Two} 2 end try {Browse a} {One}={Two} finally {Browse b} end COSC-3308-01, Lecture 5
How we catch that exception anyway? declare One Two fun {One} 1 end fun {Two} 2 end try try {Browse a} {One}={Two} catch X then {Browse b} raise X end end catch X then {Browse 'We caught the failure'} end {Browse b} General idea: transform sequence of catch-s into nested catch-s. COSC-3308-01, Lecture 5
Exceptions. Full Syntax (Example 2) • It is possible to combine both catching the exception and executing a finally statement. • try {ProcessFile F} catch X then {Browse '*** Exception '#X# 'when processing file ***'} finally {CloseFile F} end • It is similar with two nested trystatements! COSC-3308-01, Lecture 5
System Exceptions • Raised by Mozart system • failure: attempt to perform an inconsistent bind operation in the store (called also “unification failure”); • error: run-time error inside a program, like type or domain errors; • system: run-time condition in the environment of the Mozart operation system process, like failure to open a connection between two Mozart processes. COSC-3308-01, Lecture 5
System Exceptions (Example) functor import Browser define fun {One} 1 end fun {Two} 2 end try {One}={Two} catch failure(...) then {Browser.browse 'We caught the failure'} end end COSC-3308-01, Lecture 5
Type Notation Constructing programs by following the type COSC-3308-01, Lecture 5
Dynamic Typing • Oz/Scheme uses dynamic typing, while Java uses static typing. • In dynamic typing, each value can be of arbitrary type that is only checked at runtime. • Advantage of dynamic types • no need to declare data types in advance • more flexible • Disadvantage • errors detected late at runtime • less readable code COSC-3308-01, Lecture 5
Type Notation • Every value has a type which can be captured by: e :: type • Type information helps program development/documentation. • Many functions are designed based on the type of the input arguments. COSC-3308-01, Lecture 5