640 likes | 787 Views
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:
E N D
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