12/31/21 COSC-3308-01, Lecture 5 1 Programming Language Concepts, COSC-3308- 01 Lecture 5 Iteration, Recursion, Exceptions, Type Notation, and Design Methodology
Jan 12, 2016
04/21/23 COSC-3308-01, Lecture 5 1
Programming Language Concepts, COSC-3308-01Lecture 5
Iteration, Recursion, Exceptions, Type Notation, and Design Methodology
04/21/23 COSC-3308-01, Lecture 5 2
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
04/21/23 COSC-3308-01, Lecture 5 3
Overview
Last Call Optimization Recursion versus Iteration Tupled Recursion Exceptions Type Notation
Constructing programs by following the type Design methodology (standalone applications)
04/21/23 COSC-3308-01, Lecture 5 4
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
04/21/23 COSC-3308-01, Lecture 5 5
Power Function: Inductive Definition We know (from school or university)
1 , if n is zero
xn =
x xn-1 , n>0
04/21/23 COSC-3308-01, Lecture 5 6
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
04/21/23 COSC-3308-01, Lecture 5 7
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
04/21/23 COSC-3308-01, Lecture 5 8
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
04/21/23 COSC-3308-01, Lecture 5 9
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.
04/21/23 COSC-3308-01, Lecture 5 10
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
04/21/23 COSC-3308-01, Lecture 5 11
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
{PowerAcc X N Acc} = X^N * Acc{PowerAcc X N-1 X*Acc} = X^(N-1)*(X*Acc)
04/21/23 COSC-3308-01, Lecture 5 12
Pow: Complete Picture (PowA)declarelocal fun {PowAcc X N Acc} if N==0 then Acc else {PowAcc X N-1 X*Acc} end endin fun {PowA X N} {PowAcc X N 1} endend X and N are integers, because they are operands in
operations with 1.
04/21/23 COSC-3308-01, Lecture 5 13
Pow: Complete Picture (PowA)declarePowAlocal PowAccin 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} endend
04/21/23 COSC-3308-01, Lecture 5 14
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
04/21/23 COSC-3308-01, Lecture 5 15
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)
04/21/23 COSC-3308-01, Lecture 5 16
Reverse
Reversing a list
How to reverse the elements of a list
{Reverse [a b c d]}
returns
[d c b a]
04/21/23 COSC-3308-01, Lecture 5 17
Reversing a List Reverse of nil is nil
Reverse of X|Xr is Z, wherereverse 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
04/21/23 COSC-3308-01, Lecture 5 18
Question
What is correct
{Append {Reverse Xr} X}
or
{Append {Reverse Xr} [X]}
04/21/23 COSC-3308-01, Lecture 5 19
Naive Reverse Function
fun {NRev Xs}
case Xs of
nil then nil
[] X|Xr then {Append {NRev Xr} [X]}
end
end
04/21/23 COSC-3308-01, Lecture 5 20
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} endend
04/21/23 COSC-3308-01, Lecture 5 21
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!
2
)1( nn
04/21/23 COSC-3308-01, Lecture 5 22
Doing Better for Reverse
Use an accumulator to capture currently reversed list
Some abbreviations {IR Xs} for {IterRev Xs} Xs ++ Ys for {Append Xs Ys}
04/21/23 COSC-3308-01, Lecture 5 23
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]
04/21/23 COSC-3308-01, Lecture 5 24
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}
04/21/23 COSC-3308-01, Lecture 5 25
Why is Iteration Possible?
{Append {Append RL [a]} [b]}
= {Append RL {Append [a] [b]}}
Associative Property
{Append {Append RL [a]} Acc}
= {Append RL {Append [a] Acc}}
= {Append RL a|Acc}
More Generally
04/21/23 COSC-3308-01, Lecture 5 26
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
04/21/23 COSC-3308-01, Lecture 5 27
IterRev Properly Embedded
local fun {IterRev Xs Ys} case Xs of nil then Ys [] X|Xr then {IterRev Xr X|Ys} end endin fun {Rev Xs} {IterRev Xs nil} endend
04/21/23 COSC-3308-01, Lecture 5 28
State Invariant for IterRev
Unroll the iteration a number of times, we get:
{IterRev [x1 … xn] W}
=
{IterRev [xi+1 … xn] [xi … x1]++W}
04/21/23 COSC-3308-01, Lecture 5 29
Reasoning for IterRev and Rev Correctness:
{Rev Xs} is {IterRev Xs nil} Using the state invariant, we have:
{IterRev [x1 … xn] nil}=
= {IterRev nil [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
04/21/23 COSC-3308-01, Lecture 5 30
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
04/21/23 COSC-3308-01, Lecture 5 31
Tupled Recursion
Functions with multiple results
04/21/23 COSC-3308-01, Lecture 5 32
Computing Averagefun {SumList Ls} case Ls of nil then 0 [] X|Xs then X+{SumList Xs} endend
fun {Length Ls} case Ls of nil then 0 [] X|Xs then 1+{Length Xs} endend
fun {Average Ls} {SumList Ls} div {Length Ls} end
What is the Problem?
04/21/23 COSC-3308-01, Lecture 5 33
Problem?
Traverse the same list multiple times.
Solution: compute multiple results in a single traversal!
04/21/23 COSC-3308-01, Lecture 5 34
Tupling - Computing Two Resultsfun {CPair Ls} {Sum Ls}#{Length Ls} end
fun {CPair Ls} case Ls of nil then 0#0 [] X|Xs then case {CPair Xs}
of S#L then (X+S)#(1+L) end end
end
04/21/23 COSC-3308-01, Lecture 5 35
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.
04/21/23 COSC-3308-01, Lecture 5 36
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.
04/21/23 COSC-3308-01, Lecture 5 37
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.
04/21/23 COSC-3308-01, Lecture 5 38
Exceptions handling
04/21/23 COSC-3308-01, Lecture 5 39
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 endendtry {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
04/21/23 COSC-3308-01, Lecture 5 40
Exceptions (Example)
04/21/23 COSC-3308-01, Lecture 5 41
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>1 end: if <s> does not raise an exception, then execute <s>. if <s> raises an exception, i.e., by executing a raise
statement, 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>.
04/21/23 COSC-3308-01, Lecture 5 42
Exceptions. Full Syntax A try statement can specify a finally clause which is
always executed, whether the statement raises an exception or not.
try <s>1 finally <s>2 end is equivalent to: try <s>1 catch X then <s>2
raise X end end <s>2
where an identifier X is chosen that is not free in <s>2
04/21/23 COSC-3308-01, Lecture 5 43
Exceptions. Full Syntax (Example 1)declare One Two
fun {One} 1 end
fun {Two} 2 end
try
{Browse a}
{One}={Two}
finally
{Browse b}
end
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}
04/21/23 COSC-3308-01, Lecture 5 44
How we catch that exception anyway?declare One Twofun {One} 1 endfun {Two} 2 endtry try {Browse a} {One}={Two} catch X then {Browse b} raise X end endcatch X then {Browse 'We caught the failure'}end{Browse b}General idea: transform sequence of catch-s into nested catch-s.
04/21/23 COSC-3308-01, Lecture 5 45
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 try statements!
04/21/23 COSC-3308-01, Lecture 5 46
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.
04/21/23 COSC-3308-01, Lecture 5 47
System Exceptions (Example)
functorimport Browserdefine fun {One} 1 end fun {Two} 2 end try {One}={Two} catch failure(...) then {Browser.browse 'We caught the failure'} endend
04/21/23 COSC-3308-01, Lecture 5 48
Type Notation
Constructing programs by following the type
04/21/23 COSC-3308-01, Lecture 5 49
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
04/21/23 COSC-3308-01, Lecture 5 50
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.
04/21/23 COSC-3308-01, Lecture 5 51
Constructing Programs by Following the Type Programs so far take lists, hence they have a
form that corresponds to the list type:
List T ::= nil T '|' List T
case Xs of nil then expr1 % base case[] X|Xr then expr2 % recursive callend
04/21/23 COSC-3308-01, Lecture 5 52
Constructing Programs by Following the Type This helps us when the type gets complicated Nested lists are lists whose elements can be lists Exercise: “Find the number of elements of a nested list” Example: Xs= [[1 2] 4 nil [[5] 10]] {NoElements Xs} = ? {NoElements Xs} = 5
declareXs1=[[1 2] 4 nil]{Browse Xs1}Xs2=[[1 2] 4]|nil{Browse Xs2}
[[1 2] 4 nil]
[[[1 2] 4]]
04/21/23 COSC-3308-01, Lecture 5 53
Constructing Programs by Following the Type Nested lists NList T ::= nil
NList T '|' NList T T '|' NList T (T is neither nil nor a cons)
case Xs of nil then expr1 % base case[] X|Xr andthen {IsList X} then expr2 % recursive calls for X and Xr[] X|Xr then
expr3 % recursive call for Xr end
04/21/23 COSC-3308-01, Lecture 5 54
Constructing Programs by Following the Type
Nested lists Examples:
[[1 2] 3] is a nested list. {NoElements [[1 2] 3]}={NoElements [1 2]} +
{NoElements [3]}=… [1 2 3] is not a nested list (so, the number of elements
coincides with standard Oz function Length). {NoElements [1 2 3]}=1+{NoElements [2 3]}=…
fun {IsList L}L == nil orelse {Label L}=='|' andthen {Width L}==2
end
04/21/23 COSC-3308-01, Lecture 5 55
Constructing Programs by Following the Type Nested lists: fun {NoElements NList T}:Int fun {NoElements Xs}
case Xs of nil then 0 % base case[] X|Xr andthen {IsList X} then
{NoElements X} + {NoElements Xr} [] X|Xr then 1 + {NoElements Xr}end
end Question: What happens if the two case patterns X|Xr are switched?
04/21/23 COSC-3308-01, Lecture 5 56
Summary so far
Type Notation Constructing programs by following the type
04/21/23 COSC-3308-01, Lecture 5 57
Design methodology
Standalone applications
04/21/23 COSC-3308-01, Lecture 5 58
Design methodology
“Programming in the large” Written by more than one person, over a long
period of time “Programming in the small”
Written by one person, over a short period of time
04/21/23 COSC-3308-01, Lecture 5 59
Design methodology. Recommendations Informal specification: inputs, outputs, relation
between them Examples: mention particular cases Exploration: determine the programming technique;
split the problem into smaller problems Structure and coding: determine the program’s
structure; group related operations into one module Testing and reasoning: test cases/formal semantics Judging the quality: Is the design correct, efficient,
maintainable, extensible, simple?
04/21/23 COSC-3308-01, Lecture 5 60
Software components Split the program into logical units (also called
modules, components) A logical unit has:
An interface = the visible part of the logical unit. It is a record that groups together related languages entities: procedures, classes, objects, etc.
An implementation = a set of languages entities that are accessible by the interface operations but hidden from the outside.
04/21/23 COSC-3308-01, Lecture 5 61
Modules and module specifications A module specification (software
component, functor) is a template that creates a module (component instance) each time it is instantiated.
In Oz, a functor is a function whose arguments are the modules it needs and whose result is a new module. Actually, the functor takes module interfaces as
arguments, creates a new module, and returns that module’s interface!
04/21/23 COSC-3308-01, Lecture 5 62
Modules and module specifications A software component is a unit of
independent deployment, and has no persistent state.
Functors are a kind of software component. A module is a component instance; it is the
result of installing a functor in a particular module environment.
The module environment consists of a set of modules, each of which may have an execution state.
04/21/23 COSC-3308-01, Lecture 5 63
Functors
A functor has three parts: an import part = what other modules it needs an export part = the module interface a define part = the module implementation
including initialization code. Functors in the Mozart system are
compilation units. That is, the system has support for handling functors in files, both as: source code (i.e., human-readable text, .oz) object code (i.e., compiled form, .ozf).
04/21/23 COSC-3308-01, Lecture 5 64
Standalone applications (1) It can be run without the interactive interface. It has a main functor, evaluated when the
program starts. It imports the modules it needs, which causes
other functors to be evaluated. Evaluating (or “installing”) a functor creates a
new module: The modules it needs are identified. The initialization code is executed. The module is loaded the first time it is needed during
execution.
04/21/23 COSC-3308-01, Lecture 5 65
Standalone applications (2)
This technique is called dynamic linking, as opposed to static linking, in which the modules are already loaded when execution starts.
At any time, the set of currently installed modules is called the module environment.
Any functor can be compiled to make a standalone program.
So, no export part is necessary and the initialization part defines the program’s effect.
04/21/23 COSC-3308-01, Lecture 5 66
Functors. Example (GenericFunctor.oz)functorexport generic:Genericdefine fun {Generic Op InitVal N} if N == 0 then InitVal else {Op N {Generic Op InitVal (N-1)}} end endend The compiled functor GenericFunctor.ozf is created:
ozc –c GenericFunctor.oz
04/21/23 COSC-3308-01, Lecture 5 67
Functors. Example (GenericFact.oz)functorimport GenericFunctor Browserdefine fun {Mul X Y} X*Y end fun {FactUsingGeneric N} {GenericFunctor.generic Mul 1 N} end {Browser.browse {FactUsingGeneric 5}}end The executable functor GenericFact.exe is created:
ozc –x GenericFact.oz
04/21/23 COSC-3308-01, Lecture 5 68
Functors. Interactive Exampledeclare
[GF]={Module.link ['GenericFunctor.ozf']}fun {Add X Y} X+Y endfun {GenGaussSum N} {GF.generic Add 0 N} end{Browse {GenGaussSum 5}}
The function Module.link is defined in the system module Module.
It takes a list of functors, load them from the file system, links them together (i.e., evaluates them together, so that each module sees its
imported modules), and returns a corresponding list of modules.
04/21/23 COSC-3308-01, Lecture 5 69
Summary
Last Call Optimization Recursion versus Iteration Tupled Recursion Exceptions Type Notation
Constructing programs by following the type Design methodology (standalone
applications)
04/21/23 COSC-3308-01, Lecture 5 70
Reading suggestions
From [van Roy,Haridi; 2004] Chapter 2, Sections 2.4, 2.5, 2.6, 2.7 Exercises 2.9.4-2.9.12 Chapter 3, Sections 3.2-3.9 Exercises 3.10.6-3.10.17
04/21/23 COSC-3308-01, Lecture 5 71
Coming up next
Higher-Order Programming and Abstract Data Types Chapter 3, Sections 3.2-3.9
04/21/23 COSC-3308-01, Lecture 5 72
Thank you for your attention!
Questions?