Presentation is loading. Please wait.

Presentation is loading. Please wait.

CS 1321.

Similar presentations


Presentation on theme: "CS 1321."— Presentation transcript:

1 CS 1321

2 CS1321: Introduction to Programming
Georgia Institute of Technology College of Computing Lecture 26 November 27, 2001 Fall Semester

3 Today’s Menu Coke Machines

4 Our Adventures Thus Far
In our last episode, we introduced the concept of keeping state within functions.

5 Each time we call (create-adder), it returns to us a lambda body, a nameless function that we can use or bind back to a name and hold onto. This lambda body utilizes a variable called “value” that was defined inside the local scope of the function create-adder but outside the body of the lambda body returned.

6 Scheme’s Behavior Scheme has to return a function body whenever create-adder is called. That’s the whole purpose of create-adder. Yet the function returned by create-adder deals with that variable that technically exists outside the function body, yet not within the global scope. So Scheme decides to create a “copy” of that variable and give it to each function body to play with:

7 Copy of value = 0 first Copy of value = 0 second Copy of value = 0
(lambda (delta) (begin …)) first Copy of value = 0 (lambda (delta) (begin …)) second Copy of value = 0 (lambda (delta) (begin …)) third Copy of value = 0 (lambda (delta) (begin …))

8 Each of these calls to create-adder results in a completely independent function body with it’s own copy of the internal variable that it can act upon.

9 So what have we done? We’ve managed to bind together two concepts until this point we have had to consider as separate items in Scheme: A sense of “state” (a collection of values and variables that exist before a function is called, that may be altered by a function during the course of its execution and that continue to exist after the function ceases) Functionality that can act on a set values, variables and parameters.

10 Giving this idea a name:
In Scheme, this concept is called a lexical closure (or more simply, a closure). The idea of closures begins to map very closely to a much larger Computer Science concept: Object Oriented Programming. Let’s talk a little bit about Object Oriented Programming.

11 What’s an Object? You are.
Seriously. You are an object. So is the chair you’re sitting on, the screen you’re looking at, the books you lug along with you every day, the paper you write on, the pencils you write with. Objects, in their purest sense, originated as an attempt to model reality. In the real world data and functionality do not exist as separate entities floating in space. In the real world, data is contained within objects, and can only be access or modified via behaviors.

12 Ah….the king of caffeinated beverages.
Have you ever thought about how much money is plunked into vending machines every day to buy these things? It’s a hefty hunk of change. So why don’t we get in on the action?

13 Example We wish to model a coke machine in Scheme, using this idea of lexical closures and object-oriented behavior. We’ll start, however, by attempting to model our coke machines without closures, and work our way forward from there.

14 Example Suppose we want to declare a variable that indicates how many cans are in the machine: (define number-of-cans 5)

15 Example We then add a function that dispenses a coke:
(define number-of-cans 5) (define (get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke"))))

16 Example We can now call "get-coke" five times, and on the sixth call, we get an error message. (define number-of-cans 5) (define (get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke")))) We now have a function which knows its history -- it has state.

17 Testing > (get-coke) "have a Coke" "sorry, out of Coke" >
We can verify this works.

18 Analysis However, "number-of-cans" is a free variable -- that is, it's initialized outside the lexical scope of the function in which it's referenced. We previously saw how this strategy caused problems. In short, global variables changed by functions are inherently problematic. Anyone can access the data--there’s no protection. Further, how can we model MORE THAN ONE machine? With only one global, we’re limited to the number of machines we can model…and we can’t exactly rake in the money with only one machine…

19 Analysis It would be best to have a variable local to the Coke machine. Keeping globals for each coke machine would be tedious, and prone to errors.

20 Solution? (define (get-coke) (local ((define number-of-cans 5)) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke"))))) Since we want a variable local to each function call, will let work?

21 Failure (define (get-coke) (local ((define number-of-cans 5)) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke"))))) Since the number-of-cans variable is defined at each invocations, every coke machine has 5 cans, at all times. We failed to save the ‘state’ of the coke machine.

22 Key Terminology Saving State

23 Analysis We need a variable that is:
It's local to the function, so nobody else can mess with it. The state is saved in this variable, even after the function has been exited, we call this function, the state variable contains exactly what was left at the last invocation. How can we do this?

24 The solution:

25 Strategy So, when lambda is called, Scheme takes in some code, “compiles” it, and returns an executable function. A compiled function (Some code) lambda

26 Strategy What if we had the code being processed by lambda reference a variable, perhaps defined by let? (local ((define num-cans 5)) A compiled function (Some code) lambda . . . ))))) In that case, Scheme would be forced to make a copy of the variable when it compiled the code.

27 Adding Lambda (define (get-coke) (local ((define number-of-cans 5)) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke"))))) Let’s convert this. We want the function below the local to be returned by a lambda function. If we use the part BELOW the “(local ((define number-of-cans 5))” statement, the function returned by lambda won’t have the code that resets the count to 5 each time. It will only have the code that uses the variable, AFTER it’s been set. This means it gets set ONCE, and not every time we call the function.

28 Testing We wanted: We observed: > (get-coke) > (get-coke)
(define (get-coke) (local ((define number-of-cans 5)) (lambda () (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke")))))) We wanted: We observed: > (get-coke) "have a Coke" > (get-coke) (lambda ()...)

29 Testing (alternative)
(define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) We wanted: We observed: > (get-coke) "have a Coke" > (get-coke) (lambda ()...)

30 Testing What went wrong? We wanted: We observed: > (get-coke)
(define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) We wanted: We observed: > (get-coke) "have a Coke" > (get-coke) (lambda ()...)

31 Testing Maybe it worked… (define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) As it turns out, this worked as it should. It’s just that: Instead of returning cokes, our function returns a function that returns cokes.

32 Testing (define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) Also, note the scope of the lambda return. We purposefully left out the local, so that the variable is KNOWN inside the lambda return, but not reset to 5 each time the lambda function is called.

33 Testing (define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) So this really worked right, and instead of data, we get a function back? Greeeeat…. How do we use it? Well, we could just call the function: > ( (get-coke) ) "have a Coke" How does this work?

34 Solution (define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) Hmmm…maybe it would be better to bind a name to this function body that’s returned: > (define cs-machine (get-coke)) > (cs-machine) "have a Coke"

35 Solution So this is why it’s called lexical closure.
We’ve created a function that is defined as only a portion of a previous function definition. (define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) Only part of the function is lambda returned. (define cs-machine (get-coke))

36 Solution Note that we do NOT include the part of the function that resets number-of-cans back to 5. That means when compiling the lambda, Scheme took the value 5, and will use that quantity, without resetting each time. (define (get-coke) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) (define cs-machine (get-coke))

37 Testing (define (get-coke) (local ((define number-of-cans 5))
(define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) > (cs-machine) "have a Coke" "sorry, out of Coke" > (define cs-machine (get-coke))

38 Improvements We really ought to name the function “make-coke-machine” instead of “get-coke”. (define (make-coke-machine) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke)

39 Improvements We can now make many coke machines, each
(define (make-coke-machine) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) We can now make many coke machines, each with their own inventory. (define machine1 (make-coke-machine)) (define machine2 (define machine3

40 Testing We find that each machine does in fact have its own
> (define cs-machine (make-coke-machine)) > (define ee-machine (make-coke-machine)) > (ee-machine) "have a Coke" "sorry, out of Coke" > (cs-machine) > We find that each machine does in fact have its own unique copy the can number variable

41 More Refinements We can now specify which machine to apply
(define (get-coke machine) (apply machine empty)) (define cs-machine (make-coke-machine)) (define ee-machine (make-coke-machine)) (define student-center-machine (make-coke-machine)) > (get-coke cs-machine) "have a Coke” > (get-coke student-center-machine) “have a Coke” We can now specify which machine to apply our purchase to.

42 It Doesn’t Stop... > (define ee-machine (make-coke-machine)) > (get-coke ee-machine) "have a Coke" "sorry, out of Coke” “have a Coke” The problem we now see is that every time we want to restock the coke machine, we have to reset the coke machine completely.

43 Solution The problem can be solved by actually passing
(define (make-coke-machine) (local ((define number-of-cans 5)) (define (inner-get-coke) (cond ((> number-of-cans 0) (begin (set! number-of-cans (- number-of-cans 1)) (display "have a Coke"))) (else (display "sorry, out of Coke“))))) inner-get-coke) The problem can be solved by actually passing in a parameter to our lambda function. Previously, we just left it with no parameters.

44 Our final version (define (make-coke-machine-v2)
(local ((define number-of-cans 0)) (define (inner-machine dowhat) (cond ((equal? dowhat 'load) (begin (set! number-of-cans 20) (display number-of-cans) (display " frosty cans waiting for you") (newline))) ((and (equal? dowhat 'buy) (> number-of-cans 0)) (set! number-of-cans (- number-of-cans 1)) (display "have a Coke") (newline) (display "cans remaining: ") ((equal? dowhat 'buy) (display "sorry, out of Coke")) (else (display "please use correct change“))))) inner-machine)) Our final version

45 Please exhale.

46 Adding complexity Our last example allowed us to model a very simple coke machine. But this model is very limited and doesn’t relate to reality at all. Let’s take a look at what we created versus what we wanted…

47 Old Coke Machine Had an internal state variable representing the number of cokes inside the machine Could vend a single coke at a time, subtracting a single coke from our internal state variable Could reset the number of cokes in the coke machine to a constant value of 20.

48 Real Coke Machines Real coke machines have varying capacities
Real coke machines can vend a single coke at a time Real coke machines can be reset to their maximum capacity Real coke machines can tell a customer that it’s empty before he or she attempts to buy a coke Real coke machines hold money. Real coke machines associate a cost with each coke. Real coke machines can take money in exchange for a coke Real coke machines can make change if the customer puts in too much money.

49 This could get ugly Whew! That’s a lot of functionality for our coke machines. Why don’t we break it down into the individual components before we create our object out of them…

50 New Coke Machine: The State Variables
Real coke machines have varying capacities Real coke machines hold money. Real coke machines associate a cost with each coke. (define cans 0) (define MAX-CANS 0) (define COST .65) (define cash 0) Even though max-cans is effectively going to be a constant, we’re still going to start it off at zero. This will allow us to create coke machines with varying capacities.

51 New Coke Machine: Initialization
We need to be able to specify the maximum number of cans our coke machine can hold, and initialize the number of cans we currently hold to that value: (define (init-machine in-max) (begin (set! MAX-CANS in-max) (set! cans MAX-CANS)))

52 New Coke Machine: reload
If we run out of cokes in our coke machine, we need to be able to reload our coke machine. Let’s simplify the problem by only reloading to our maximum capacity (define (reload) (set! cans MAX-CANS))

53 New Coke Machine: is-empty?
Now we need a function that returns whether or not a coke machine is empty or not. (define (is-empty?) (= cans 0))

54 New Coke Machine: vend Finally, something complex!
Logically, we need to be able to vend a single coke if the machine is NOT empty. We also need to take in an idea of cash. If the machine takes in AT LEAST the cost of a can of coke, and if one is available, we need to “vend” a coke, add money to the amount of cash in the machine, and return the appropriate change. If the user inserts the incorrect amount of change, we need to handle that as well:

55 New Coke Machine: vend (define (vend in-money)
(cond ((is-empty?) (display "No cokes left!")) (else (cond ((>= in-money COST) (begin (display "Have a Coke") (set! cash (+ cash COST)) (set! cans (- cans 1)) (- in-money COST))) (display "Add more money"))))))

56 New Coke Machine: how-many
For testing purposes, let’s put this function in: (define (how-many) cans)

57 Before we go on… Scheme is not exactly the most beautiful language to emulate this Object Oriented behavior in. The next function we’re about to see is ugly and confusing at first. So before we lose ourselves in those particulars, let’s examine what we’ve set ourselves up for:

58 OBJECT init-machine (in-max) reload Constants Variables cans MAX-CANS
COST Variables cans cash how-many is-empty? vend (in-money) Our goal is to create an object that represents a coke machine. This object will have values contained within it and behaviors that act on those values.

59 New Coke Machine: service-manager
As we stated earlier, Object Oriented Behavior isn’t very elegant in scheme. We’re stuck with the idea of closures as one of best models. Closures work on the idea of returning a lambda body (nameless function) that has within it a copy of the data we want to act on, and the behavior we wish to enact. Our adder example from last lecture was pretty straight-forward. It’s when we want to create multiple behaviors as we have done that things get complicated. Let’s see how it looks and then work through some examples…

60 New Coke Machine: service-manager
(define (service-manager service) (cond ((symbol=? service 'is-empty?) is-empty?) ((symbol=? service 'how-many) how-many) ((symbol=? service 'reload) reload) ((symbol=? service 'vend) (lambda (n) (vend n))) (else (display "Invalid service"))))

61 make-coke-machine (define (make-coke-machine max)
(local ((define cans 0) (define MAX-CANS 0) (define COST .65) (define cash 0) (define (init-machine in-max) … (define (reload) …) (define (is-empty?) …) (define (how-many) …) (define (vend in-money) …) (define (service-manager service) …) ) …)) The definitions for these functions are on the previous slides. Let’s look at what the local actually executes…

62 make-coke-machine (define (make-coke-machine max) (local (…) (begin
(init-machine max) service-manager))) We begin by initializing our coke-machine “object”. We then return the service manager (the main functionality) as is typical in closures.

63 Let’s figure out how this works…
As we’ve seen in previous examples, closures return functionality, so we have to bind it up with a name… And now we have a coke machine for the CS Building.

64 The functionality that we returned from make-coke-machine was this:
(define (service-manager service) (cond ((symbol=? service 'is-empty?) is-empty?) ((symbol=? service 'how-many) how-many) ((symbol=? service 'reload) reload) ((symbol=? service 'vend) (lambda (n) (vend n))) (else (display "Invalid service")))) As we didn’t provide any contracts for our functions (shame on us!), we’re going to have to play around with this function to see what happens. It looks like this function takes in 4 different types of symbols. Let’s try ‘how-many

65 Uh-oh. More functionality
Uh-oh. More functionality. This returns us a function that takes in no parameters and does…something. Let’s find out what by wrapping another set of parentheses around the functionality… It seems to return the amount of cans contained in the cs-coke-machine. Let’s try another… ‘is-empty

66 Hmm…more functionality. Let’s try it out.
That sounds reasonable, as we just created a coke machine with 30 cans. Let’s try selling one…

67 Still more functionality
Still more functionality! This one seems to require a parameter though…We could fish around to determine what type of value this function expects, but let’s cheat and rely on our previous knowledge. We’re trying to vend a coke. We need to insert money! So it gave us a coke and returned correct change…

68 If we keep doing that… We can see our coke stock vanishing. Let’s reload the machine…

69 Well, that seems to work…

70 Patterns… So it would seem that we play with objects like this:
We create an instance of our “object” and bind it to a name. This instance of our object is in fact the service-manager for our closure. (define <object-name> (<object creator>)) We act on that service manager by passing it the name of the functionality we want to enact: (<object-name> ‘<name of functionality>) This returns to us the actual functionality itself, which we can then pass parameters to and use: ((<object-name> ‘<name of functionality>) <parameter>)

71 Far-fetched? Well, not terribly. In Java, the language you’ll be working with next semester, you can definitely draw parallels between what we just did and what you will do: CokeMachine CSCokeMachine = new CokeMachine(30); CSCokeMachine.vend(1.00); System.out.println( CSCokeMachine.howMany());

72 Far-fetched? Well, not terribly. In Java, the language you’ll be working with next semester, you can definitely draw parallels between what we just did and what you will do: CokeMachine CSCokeMachine = new CokeMachine(30); CSCokeMachine.vend(1.00); System.out.println( CSCokeMachine.howMany()); Here we bind a name to a coke machine object

73 Far-fetched? Well, not terribly. In Java, the language you’ll be working with next semester, you can definitely draw parallels between what we just did and what you will do: CokeMachine CSCokeMachine = new CokeMachine(30); CSCokeMachine.vend(1.00); System.out.println( CSCokeMachine.howMany()); Here we access functionality and pass a parameter via: <object name>.<functionality>(<parameters>)

74 Far-fetched? Well, not terribly. In Java, the language you’ll be working with next semester, you can definitely draw parallels between what we just did and what you will do: CokeMachine CSCokeMachine = new CokeMachine(30); CSCokeMachine.vend(1.00); System.out.println( CSCokeMachine.howMany()); And we do the same here: <object name>.<functionality>(<parameters>)

75 Alternatives? Is this the best way to model a coke machine? Certainly not. We’re missing a whole mess of functionality. But let’s talk about that next time.


Download ppt "CS 1321."

Similar presentations


Ads by Google