ITB001 Problem Solving and Programming Lecture 9
ITB001 Problem Solving and Programming Lecture 9. Faculty of Information Technology Queensland University of Technology. Aims of this lecture. In this lecture we allow for problems whose solution requires a ‘memory’ of past events To do this we need some new programming concepts:
ITB001 Problem Solving and Programming Lecture 9
E N D
Presentation Transcript
ITB001Problem Solving and ProgrammingLecture 9 Faculty of Information Technology Queensland University of Technology
Aims of this lecture • In this lecture we allow for problems whose solution requires a ‘memory’ of past events • To do this we need some new programming concepts: • Introducing scoped variables • Assignment of values to variables • Evaluating statements in a particular sequence • We thus extend our current functional programming paradigm to imperative programming
References • Concrete Abstractions: An Introduction to Computer Science Using Scheme, p. 61, p. 362 • The concepts in this lecture are not covered in detail in this textbook • Structure and Interpretation of Computer Programs, Section 3.1
The need for memory • So far all of our procedures have been ‘pure’ functions • For particular arguments they always produce the same result • Often, however, we need to perform calculations that rely on remembering previously-computed values • In this case the result produced by a procedure may be different at different times
Motivational example:Maintaining a bank account • The ability to withdraw money from a bank account, that initially contains $100, depends on the past history of deposits and withdrawals: • (withdraw 40) returns balance 60 • (withdraw 40) returns balance 20 • (withdraw 40) returns message "Insufficient funds" • Procedure withdraw must return different results at different times, even when given the same argument
Functional versusimperative programming • In practice this is supported through: • Declaration of variables whose values can change • An assignment statement which changes the value bound to a variable • Explicit control over the sequence in which assignments are performed • Programs which direct the computer to change the values bound to variables are known as ‘imperative’
Introducing scoped variables • To allow programs to have memory, we need a place to store values • This is done by introducing mutable variables • The scope, or part of the program from which the variable can be seen, should normally be kept as small as possible to aid program maintenance
Introducing scoped variables • In Scheme we have already seen two particular types of ‘variables’, i.e., symbolic names whose values are not fixed: • Using define we have bound names to values (including procedures) in the global environment • Procedure parameters are also variables (whose scope is limited to the procedure body) • More generally, we can introduce locally-scopedvariables using ‘let’ expressions
Example: Avoiding duplicatedcalculation using a ‘let’ variable • Imagine you are asked to write a procedure to evaluate the following function: f(x) (x2 5x)2 10x(x 5) 24 • Factorising the expression to extract the common term allows us to reduce the number of arithmetic operations involved: f(x) y2 10y 24 where yx2 5x
Example: Avoiding duplicatedcalculation using a ‘let’ variable • In Scheme we can express this idea by declaring common term y as a local variable which is bound to the intermediate value: (define [f x] (let [[y (- (sqr x) (* 5 x))]] (+ (sqr y) (* 10 y) 24)))
‘Let’ expressions • In general, the let construct allows a number of local variables to be associated with an expression (let [[v1e1] [v2e2] … [vnen]] e) • A let expression returns the value of expression e with occurrences of each variable vi bound to corresponding value ei
Examples of local variable scoping • Assume below that x has been defined to be 5: (define x 5) • The following expression returns 12: (+ (let [[x 3]] (+ x 4)) ; equals 3 4 x) ; equals 7 5 • The following expression returns 21: (let [[x 3] [y (+ x 2)]] ; equals 5 2 (* x y)) ; equals 3 7
Nested ‘let’ expressions • The preceding example shows that the values of variables in let expressions are evaluated simultaneously in the let expression’s envrionment • To declare dependent let variables, we must use appropriate nesting • The following expression returns 15: (let [[x 3]] (let [[y (+ x 2)]] ; equals 3 2 (* x y))) ; equals 3 5
Exercise 9-1: Practicewith let expressions • Rewrite the following expression, using let to extract the common term and improve the structure of the code (do not perform any algebraic simplifications) (+ (- (* 3 a) b) (+ (* 3 a) b)) • Check your answer in DrScheme by: • Defining values for numbers a and b • Typing in the expression above and your (hopefully equivalent) version using let • Making sure that both expressions return the same value
Exercise 9-2: More practicewith let expressions • Desk check the value of the following expression, then confirm your answer by typing the expression into DrScheme (let [[x 9]] • (* x (let [[x (/ x 3)]] • (+ x x))))
The need for sequencing • In functional programming the order of expression evaluation is usually unimportant • The order in which we evaluate the arguments during procedure application usually doesn’t matter • (Nevertheless, some special Scheme constructs do assume a particular evaluation order, either for efficiency, e.g., and, or conciseness of expression, e.g., cond)
The need for sequencing • In imperative programming, however, the sequence in which operations are performed becomes significant • The values of variables can change over time • The current value of a variable may depend on its previous value or previous values of other variables • Therefore, we need a language construct for explicitly specifying the order of evaluation of expressions
‘Begin’ expressions • The following construct evaluates expressions e1 to en, in that order, and returns the value of expression en: • (begin e1e2…en) • Example: • (begin (+ 2 3) • (- 10 8) • (* 2 3)) returns 6 • Some pre-defined Scheme procedures, including let expressions and procedure definitions, include an implicit ‘begin’
What’s the point? • Since the results returned by expressions e1 to en 1 in (begin e1e2…en) aren’t returned, what’s the point of evaluating them? • To be useful, these expressions must have some side effect • That is, they must do something other than returning a value
Side-effecting procedures • We have already seen an example of a pre-defined side-effecting procedure: • The define procedure binds a name to a value in the current environment (and returns nothing) • Another such procedure we will see later is display, which has a side effect on your computer’s screen • The set! procedure introduced below has a side effect on variables • Importantly, side-effecting procedures do not return values (called returning ‘void’ in Scheme)
Assignment • We now introduce the ability to change the value bound to an existing variable • The assignment statement changes the value bound to a ‘target’ variable v to be that of an expression e: • (set! ve) • The set! statement does not return a result • We use set! for its side effect on v, not its returned value • The ‘!’ at the end of the procedure’s name is merely a convention used to remind us that it has a side effect
Assignment • The target variable of a set! statement can be any of: • A ‘global’ variable introduced using define • A ‘local’ variable introduced using let • A procedure parameter • In each case, the set! statement must be evaluated within the variable’s scope
(define x 2) (begin (set! x (* x 2)) (set! x (+ x 3)) x) returns …? (define x 2) (begin (set! x (+ x 3)) (set! x (* x 2)) x) returns …? Exercise 9-3: Understanding assignment to a global variable • Desk check the following two examples, then check your answers with DrScheme
Exercise 9-4: Understandingassignment to a local variable • Confirm that the following sequence of expressions swaps the values of x and y: • (define x 1) • (define y 2) • (let [[temp x]] • (set! x y) • (set! y temp) • "done") returns "done" • x returns …? • y returns …?
Exercise 9-5: Understandingvariables local to a procedure • Now imagine that you try to capture the calculation from Exercise 9-4 as a procedure — what happens and why? • (define x 1) (define y 2) • (define [swap a b] • (let [[temp a]] • (set! a b) • (set! b temp) • "done")) • (swap x y) returns "done" • x returns …? y returns …?
Using a global variable tomaintain a bank account • We now return to our motivational example of maintaining a bank balance • We first define the opening balance as a global variable: • (define balance 100) • Variable balance is now bound to number 100
Using a global variable tomaintain a bank account • The withdrawal procedure then updates the balance using assignment: • (define [withdraw amount] • (if (>= balance amount) • (begin • (set! balance (- balance amount)) • balance) ; returned value • "Insufficient funds"))
Using a global variable tomaintain a bank account • Example: • (define balance 100) • (withdraw 55) returns 45 • balance returns 45 • (withdraw 60) returns "Insufficient funds" • balance returns 45 • (withdraw 10) returns 35 • balance returns 35
Exercise 9-6: A cinema usher • When allocating cinema seats we need to ensure that no seat is booked twice • For instance, a cinema usher could keep track of the number of seats allocated on a blackboard in the foyer • Use a global variable to define a parameterless procedure usher which always returns a previously unused ticket, up to a fixed number after which it always returns string "Sorry, sold out!" • Hint: The global variable should keep track of the number of seats already allocated
Exercise 9-6: A cinema usher • Example, assuming that this theatre has only three seats: • (usher) returns 1 • (usher) returns 2 • (usher) returns 3 • (usher) returns "Sorry, sold out!" • (usher) returns "Sorry, sold out!"
Global versus local state variables • By defining variable balance at the ‘top level’ we have made it accessible to any procedure • Generally it is better to minimise the number of procedures that have access to a variable • This makes programs easier to understand and maintain • This can be done by using procedure parameters or ‘let’ variables as our memory
A bank account witha local variable • To safeguard our bank balance we can define the withdrawal procedure so that no other procedures can access the balance variable • As shown overleaf we can do this by putting the procedure that updates the balance within a let expression that introduces the variable • This procedure, within the scope of the local variable, is defined to be our new withdrawal procedure
A bank account witha local variable (define withdraw (let [[balance 100]] ; local variable (define [update-balance amount] (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) update-balance)) ; return the nested procedure
A bank account witha local variable • This new version of procedure withdraw works just like its predecessor except that variable balance is not accessible by any other procedures • The ‘let’ expression is evaluated when withdraw is defined and procedure update-balance is encapsulated within the scope of variable balance • (Aside: Although the procedure’s written form says that balance equals 100, this may not be true after a withdrawal has been performed!)
A bank account witha local variable • Example: • (withdraw 55) returns 45 • balance returns an error because this variable is not in scope • (withdraw 60) returns "Insufficient funds" • (withdraw 10) returns 35
Exercise 9-7: A cinema usherwho safeguards seat allocations • Tired of schoolchildren changing the seat numbers written on the blackboard, our cinema usher decides to instead write seat numbers on a notebook he keeps in his pocket • Redefine your usher procedure so that it has exactly the same behaviour as before but uses a local variable so that no other procedure can interfere with its operation
Multiple bank accounts • So far we have had a single ‘balance’ variable only (whether it was globally or locally visible) and a single ‘withdraw’ procedure • To support multiple bank accounts we need to make multiple procedures each containing their own local variables • In this case we will use the procedures’ parameters to hold the bank balance • Recall that procedure parameters are accessible within the procedure body only
Multiple bank accounts • The following higher-order procedure returns procedures for performing withdrawals: (define [make-withdraw balance] (define [update-balance amount] (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) update-balance)
Multiple bank accounts • We can use make-withdraw to create two withdrawal procedures with independent states: • (define i-pay (make-withdraw 100)) • (define you-pay (make-withdraw 50)) • (i-pay 75) returns balance 25 • (you-pay 40) returns balance 10 • (you-pay 20) returns message "Insufficient funds" • (i-pay 20) returns balance 5
Exercise 9-8: Cinema multiplex • The previous seat allocation procedure could keep track of seat allocations for one film only • Define a procedure make-usher with one parameter which is the maximum number of seats that may be allocated for a particular film in a multiplex • The make-usher procedure should return a parameterless procedure which allocates seats until this maximum is reached • This will be done independently of seat allocations by other such procedures
Exercise 9-8: Cinema multiplex • Hint: You will need to use a let expression to introduce a local variable to keep track of the last seat allocated, distinct from the parameter which specifies the maximum number of seats available
Exercise 9-8: Cinema multiplex • Example: • (define casablanca (make-usher 3)) • (define citizen-kane (make-usher 2)) • (casablanca) returns 1 • (casablanca) returns 2 • (citizen-kane) returns 1 • (casablanca) returns 3 • (casablanca) returns "Sorry, sold out!" • (citizen-kane) returns 2 • (citizen-kane) returns "Sorry, sold out!"
The need for a newcomputational model • Introducing assignment to our programming language means that our simple ‘substitution’ model of computational processes is no longer sufficient • The substitution model no longer works because it does not capture the ‘history’ of variable updates • To model computations involving assignment we need to keep track of which variables are accessible in the current environment and which values are currently bound to them
Referential transparency • ‘Pure’ functions have the desirable property of referential transparency • This means that they always return the same result given the same arguments • The previous ‘withdrawal’ example demonstrated that this is not necessarily true of procedures with memory