1. Trang chủ
  2. » Công Nghệ Thông Tin

How to Design Programs phần 9 doc

56 227 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 56
Dung lượng 2,71 MB

Nội dung

-450- The extension of the language with set!-expressions required another change to our rules. Now definitions that associate variables and values can change over the course of an evaluation. The informal rules we've used so far deal with changes to the definition of state variables, because they matter the most. But the rules are informal and imprecise, so a precise description of how the addition of set! changes the meaning of the language must be our primary concern. Let's recall how we determine the meaning of a program. A program consists of two parts: a collection of definitions and an expression. The goal is to evaluate the expression, which means to determine the expression's value. 72 In Beginning Student Scheme, the collection of values consists of all the constants plus lists. Only one list has a concise representation: the empty one. All other lists are written down as a series of cons tructed lists. The evaluation of an expression consists of a series of steps. At each step we use the laws of arithmetic and algebra to simplify a subexpression. This yields another expression. We also say that we REWRITE the first expression into the second. If the second expression is a value, we are finished. The introduction of set!-expressions into our programming language requires a few small adjustments and extensions to this process: 1. Instead of rewriting just an expression, we must now rewrite definitions and expressions. More precisely, each step changes the expression and possibly the definition of a state variable. To make these effects as obvious as possible, each stage in an evaluation displays the definitions of state variables and the current expression. 2. Furthermore, it is no longer possible to apply the laws of arithmetic and algebra whenever or wherever we want. Instead, we must determine the subexpression that we must evaluate if we wish to make progress. This rule still leaves us with choices. For example, when we rewrite an expression such as 3. (+ (* 3 3) (* 4 4)) we may choose to evaluate (* 3 3) and then (* 4 4) or vice versa. Fortunately, for such simple expressions, the choice doesn't affect the final outcome, so we don't have to supply a complete unambigous rule. In general, though, we rewrite subexpressions in a left-to-right and top-to-bottom order. At each stage in the evaluation, we best start by underlining the subexpression that must be evaluated next. 4. Suppose the underlined subexpression is a set!-expression. By the restrictions on set!- expressions, we know that there is a define for the left-hand side of the subexpression. That is, we face the following situation: 5. (define x aValue) 6. 7. (set! x anotherValue) 8. = (define x anotherValue) 9. 10. (void) The equation indicates that the program changes in two ways. First, the variable definition is modified. Second, the underlined set!-expression is replaced by (void), the invisible value. TEAMFLY TEAM FLY PRESENTS -451- 11. The next change concerns the replacement of variables in expressions with the value in their definition. Until now, we could replace a variable with its value wherever we thought it was convenient or necessary. Indeed, we just thought of the variable as a shorthand for the value. With set!-expressions in the language, this is no longer possible. After all, the evaluation of a set!-expression modifies the definition of a state variable, and if we replace a variable with its value at the wrong time, we get the wrong value. Suppoe that the underlined expression is a (state) variable. Then we know that we can't make any progress in our evaluation until we have replaced the variable with the current value in its definition. This suggests the following revised law for variable evaluation: (define x aValue) x = (define x aValue) aValue In short, substitute the value in a state variable definition for the state variable only when the value is needed for this particular occurrence of the state variable. 12. Last, but not least, we also need a rule for begin-expressions. The simplest one says to drop the first subexpression if it is a value: 13. (begin v exp-1 exp-n) 14. = (begin exp-1 exp-n) That means we also need a rule for dropping begin completely: (begin exp) = exp In addition, we use a rule for dropping several values at once: (begin v-1 v-m exp-1 exp-n) = (begin exp-1 exp-n) But this is only a convenience. Although the laws are more complicated than those of Beginning Student Scheme, they are still manageable. Let's consider some examples. The first one demonstrates how the order of evaluation of subexpressions makes a difference: (define x 5) (+ (begin (set! x 11) x) x) = (define x 11) (+ (begin (void) x ) x) = = (define x 11) (+ 11 x ) TEAMFLY TEAM FLY PRESENTS -452- = (define x 11) (+ 11 11) The program consists of one definition and one addition, which is to be evaluated. One of the addition's arguments is a set!-expression that mutates x ; the other is just x . By evaluating the subexpressions of the addition from left to right, the mutation takes place before we replace the second subexpression with its value. As a result, the outcome is 22 . If we had evaluated the addition from right to left, the result would have been 16 . To avoid such problems, we use the fixed ordering but give ourselves more freedom when no state variables are involved. The second example illustrates how a set!-expression that occurs in a local-expression actually affects a top-level definition: (define (make-counter x0) (local ((define counter x0) (define (increment) (begin (set! counter (+ counter 1)) counter))) increment)) ((make-counter 0) ) The program again consists of a single definition and an expression that is to be evaluated. The latter, however, is an application nested in an application. The inner application is underlined, because we must evaluate it to make progress. Here are the first few steps: = (define (make-counter x0) (local ((define counter x0) (define (increment) (begin (set! counter (+ counter 1)) counter))) increment)) ((local ((define counter 0) (define (increment) (begin (set! counter (+ counter 1)) counter))) increment)) = (define (make-counter x0) (local ((define counter x0) (define (increment) (begin (set! counter (+ counter 1)) counter))) increment)) (define counter1 0) (define (increment1) (begin (set! counter1 (+ counter1 1)) counter1)) (increment1) The evaluation of the local-expression created additional top-level expressions. One of them introduces a state variable; the others define functions. TEAMFLY TEAM FLY PRESENTS -453- The second part of the evaluation determines what (increment1) accomplishes: (define counter1 0) (increment1) = (define counter1 0) (begin (set! counter1 (+ counter1 1)) counter1) = (define counter1 0) (begin (set! counter1 (+ 0 1) ) counter1) = (define counter1 0) (begin (set! counter1 1) counter1) = (define counter1 1) (begin (void) counter1 ) = (define counter1 1) 1 During the evaluation, we replace counter1 with its value twice. First, the second step replaces counter1 with 0 , its value at that point. Second, we substitute 1 for counter1 during the last step, which is its new value. Exercise 38.4.1. Underline the subexpression that must be evaluated next in the following expressions: 1. (define x 11) (begin (set! x (* x x)) x) 2. (define x 11) (begin (set! x (cond [(zero? 0) 22] [else (/ 1 x)])) 'done) 3. (define (run x) (run x)) (run 10) 4. (define (f x) (* pi x x)) (define a1 (f 10)) (begin (set! a1 (- a1 (f 5))) 'done) 5. (define (f x) TEAMFLY TEAM FLY PRESENTS -454- (set! state (- 1 state))) (define state 1) (f (f (f))) Explain why the expression must be evaluated. Exercise 38.4.2. Confirm that the underlined expressions must be evaluated next: 1. (define x 0) (define y 1) (begin (set! x 3) (set! y 4) (+ (* x x) (* y y))) 2. (define x 0) (set! x (cond [(zero? x ) 1] [else 0])) 3. (define (f x) (cond [(zero? x) 1] [else 0])) (begin (set! f 11) f) Rewrite the three programs to show the next state. Exercise 38.4.3. Evaluate the following programs: 1. (define x 0) (define (bump delta) (begin (set! x (+ x delta)) x)) (+ (bump 2) (bump 3)) 2. (define x 10) (set! x (cond [(zeor? x) 13] [else (/ 1 x)])) 3. (define (make-box x) (local ((define contents x) (define (new y) (set! contents y)) (define (peek) contents)) (list new peek))) (define B (make-box 55)) (define C (make-box 'a)) (begin ((first B) 33) ((second C))) TEAMFLY TEAM FLY PRESENTS -455- Underline for each step the subexpression that must be evaluated next. Show only those steps that involve a local-expression or a set!-expression. In principle, we could work with the rules we just discussed. They cover the common cases, and they explain the behavior of the programs we have encountered. They do not explain, however, how an assignment works when the left-hand side refers to a define d function. Consider the following example, for which the rules still work: (define (f x) x) (begin (set! f 10) f) = (define f 10) (begin (void) f) Here f is a state variable. The set!-expression changes the definition so f stands for a number. The next step in an evaluation substitutes 10 for the occurrence of f . Under ordinary circumstances, an assignment would replace a function definition with a different function definition. Take a look at this program: (define (f x) x) (define g f) (+ (begin (set! f (lambda (x) 22)) 5) (g 1)) The purpose of the underlined set!-expression is to modify the definition of f so that it becomes a function that always produces 22 . But g stands for f initially. Since f is a the name of a function, we can think of (define g f) as a value definition. The problem is that our current rules change the definition of f and, by implication, the definition of g , because it stands for f : = (define f (lambda (x) 22)) (define g f) (+ (begin (void) 5) (g 1)) = (define f (lambda (x) 22)) (define g f) (+ 5 (g 1) ) = (define f (lambda (x) 22)) (define g f) (+ 5 22) Scheme, however, does not behave this way. A set!-expression can modify only one definition at a time. Here it modifies two: f's, which is intended, and g's, which happens through the indirection from g to f. In short, our rules do not explain the behavior of all programs with set!- expressions; we need better rules if we wish to understand Scheme fully. <vdf> = (define <var> <val> ) TEAMFLY TEAM FLY PRESENTS -456- | (define-struct <var> (<var> <var>)) <val> = <con> | <lst> | <prm> | <fun> | <void> <lst> = empty | (cons <val> <lst> ) <fun> = (lambda ( <var> <var> ) <exp> ) Figure 112: Advanced Student Scheme: The values The problem concerns the definitions of functions, which suggests that we take a second look at the representation of functions and function definitions. So far, we used the names of functions as values. As we have just seen, this choice may cause trouble in case the state variable is a function. The solution is to use a concrete representation of functions. Fortunately, we already have one in Scheme: lambda-expressions. Furthermore, we rewrite function definitions so that they turn into variable definitions with a lambda-expression on the right-hand side: (define (f x) x) = (define f (lambda (x) x)) Even recursive definitions are evaluated in this manner: (define (g x) (cond [(zero? x) 1] [else (g (sub1 x))])) = (define g (lambda (x) (cond [(zero? x) 1] [else (g (sub1 x))]))) All other rules, including the rule for replacing variables with their values, remain the same. Figure 112 specifies the set of values, 73 as a subset of the set of expressions, and the set of value definitions, as a subset of the definitions. Using these definitions and the modified rules, we can take a second look at at the above example: (define (f x) x) (define g f) (+ (begin (set! f (lambda (x) 22)) 5) (g 1)) = (define f (lambda (x) x)) (define g f ) (+ (begin (set! f (lambda (x) 22)) 5) (g 1)) = (define f (lambda (x) x)) (define g (lambda (x) x)) (+ (begin (set! f (lambda (x) 22)) 5) (g 1)) TEAMFLY TEAM FLY PRESENTS -457- = (define f (lambda (x) 22)) (define g (lambda (x) x)) (+ (begin (void) 5) (g 1)) = (define f (lambda (x) 22)) (define g (lambda (x) x)) (+ 5 (g 1) ) = (define f (lambda (x) 22)) (define g (lambda (x) x)) (+ 5 1) The key difference is that the definition of g directly associates the variable with a function representation, not just a name for a function. The following program shows the effects of set!-expressions on functions with an extreme example: (define (f x) (cond [(zero? x) 'done] [else (f (sub1 x))])) (define g f) (begin (set! f (lambda (x) 'ouch)) (symbol=? (g 1) 'ouch)) The function f is recursive on natural numbers and always produces 'done . Initially, g is defined to be f . The final begin-expression first modifies f and then uses g . At first, we must rewrite the function definitions according to our modified rules: = (define f (lambda (x) (cond [(zero? x) 'done] [else (f (sub1 x))]))) (define g f ) (begin (set! f (lambda (x) 'ouch)) (symbol=? (g 1) 'ouch)) = (define f (lambda (x) (cond [(zero? x) 'done] [else (f (sub1 x))]))) (define g (lambda (x) (cond [(zero? x) 'done] [else (f (sub1 x))]))) (begin (set! f (lambda (x) 'ouch)) (set! f (lambda (x) 'ouch)) TEAMFLY TEAM FLY PRESENTS -458- (symbol=? (g 1) 'ouch)) Rewriting the definition of f is straightforward. The major change concerns the definition of g . Instead of f it now contains a copy of the value for which f currently stands. This value contains a reference to f , but that is not unusual. Next, the set!-expression modifies the definition of f : = (define f (lambda (x) 'ouch)) (define g (lambda (x) (cond [(zero? x) 'done] [else (f (sub1 x))]))) (begin (void) (symbol=? (g 1) 'ouch)) No other definition, however, is affected. In particular, the definition of g remains the same, though the f inside of g 's value now refers to a new value. But we have seen this phenomenon before. The next two steps follow the basic rules of intermezzo 1: = (define f (lambda (x) 'ouch)) (define g (lambda (x) (cond [(zero? x) 'done] [else (f (sub1 x))]))) (begin (void) (symbol=? (f 0) 'ouch)) = (define f (lambda (x) 'ouch)) (define g (lambda (x) (cond [(zero? x) 'done] [else (f (sub1 x))]))) (begin (void) (symbol=? 'ouch 'ouch)) That is, the application of g eventually applies f to 0, which yields 'ouch . Hence the final result is true. TEAMFLY TEAM FLY PRESENTS -459- Exercise 38.4.4. Validate that the following program evaluates to true : (define (make-box x) (local ((define contents x) (define (new y) (set! contents y)) (define (peek) contents)) (list new peek))) (define B (make-box 55)) (define C B) (and (begin ((first B) 33) true) (= (second C) 33) (begin (set! B (make-box 44)) (= (second C) 33))) Underline for each step the subexpression that must be evaluated next. Show only those steps that involve a local-expression or a set!-expression. While we decided to rewrite function definitions so that their right-hand side are always lambda- expressions, we stuck with a function application rule that assumes function definitions in the style of Beginning Student Scheme. More concretely, if the definition context contains a definition such as (define f (lambda (x y) (+ x y))) and the expression is (* (f 1 2) 5) then the next step in the evaluation is (* (+ 1 2) 5) For other occasions, however, we just replace variables with the values in the respective definitions. If we followed that rule, we would rewrite (* (f 1 2) 5) to (* ((lambda (x y) (+ x y)) 1 2) 5) At first glance, this exploration route ends here, because there are no laws for this application. We can reconcile the two ideas with a new law, suggested by the last expression: ((lambda (x-1 x-n) exp) TEAMFLY TEAM FLY PRESENTS [...]... discussion 74 Logic is to computing what mathematics is to physics -463TEAM FLY PRESENTS Part VIII Changing Compound Values Y L F M A E T -464TEAM FLY PRESENTS Section 39 Encapsulation When we design a program to control a traffic light, we probably don't want to control just one traffic light, but several Similarly, when we design a program to manage names and phone numbers, we might wish to manage several... TL-color ;; to keep track of the current color of the traffic light (define current-color 'red) A E T ;; init-traffic-light : -> true ;; to (re)set current-color to red and to (re)create the view (define (init-traffic-light) ) ;; next : -> true ;; effect: to change current-color from 'green to 'yellow, ;; 'yellow to 'red, and 'red to 'green (define (next) ) ;; next-color : TL-color -> TL-color ;; to compute... An implementation of posns with mutators Together, sections 39 and 40.1 suggest that structures are mutable That is, we should be able to change the values of some field in a structure After all, we introduced the service managers in section 39 to hide state variables, not just ordinary variable definitions -478TEAM FLY PRESENTS Figure 117 shows how a small change to the definitions of figure 116 turns... thus give ourselves the power to create as many versions as necessary We learn how to encapsulate state variables in the first subsection, and practice it in the second one Y L F M 39. 1 Abstracting with State Variables Suppose we wish to turn the program in figure 100 (page 45) into a program for managing (simulated) traffic lights An operator of the simulation should be able to control each traffic light... TL-color ;; to keep track of the current color of the traffic light (define current-color 'red) A E T ;; init-traffic-light : -> true ;; to (re)set current-color to red and to (re)create the view (define (init-traffic-light) (begin (set! current-color 'red) (draw-light current-color x-posn))) ;; next : -> true ;; effect: to change current-color from 'green to 'yellow, ;; 'yellow to 'red, and 'red to 'green... Student Scheme turns a class of syntactic errors of Beginning Student Scheme into run-time errors It also introduces a new form of logical error Consider the following program: ;; how- many-in-list : (listof X) -> N ;; to count how many items alist contains (define (how- many-in-list alist) (cond [empty? (alist)] [else (+ (how- many-in-list (rest alist)) 1)])) In Beginning Student Scheme or Intermediate... all the same services In particular, the definition provides a constructor that consumes two values and constructs a compound value, and two selectors for extracting the values that went into the construction of a compound value To understand why f-make-posn is a constructor and why f-posn-x and f-posn-y are selectors, we can discuss how they work, and we can confirm that they validate the expected equations... DISTANCE-BETWEEN-BULBS)) Develop the necessary definitons separate from the rest of the traffic light program, then create a single function definition using local Solution Now suppose we wish to provide the additional service of resetting an individual traffic light That is, in addition to switching from the current color to the next, an operator should be able to set a traffic light to red The function for doing so... numbers to the address book, or we retrieve numbers as desired: > > > > > ((friends 'add) 'Bill 2) ((friends 'add) 'Sally 3) ((friends 'add) 'Dave 4) ((business 'add) 'Emil 5) ((business 'add) 'Faye 18) In this case, we added three entries to the address book named friends and two to the one called business Y L F M An addition to, say, friends works in two steps The first step is to apply friends to 'add... function add -to- address-book The second step is to apply this resulting function to a name and a number In a similar vein, looking up a phone number also works in two steps The application of, say, friends to 'search yields a function that consumes a name This function is then applied to a symbol: > ((friends 'search) 'Bill) 2 > ((business 'search) 'Bill) false A E T The two applications show that the . Section 39 Encapsulation When we design a program to control a traffic light, we probably don't want to control just one traffic light, but several. Similarly, when we design a program to. Student Scheme into run-time errors. It also introduces a new form of logical error. Consider the following program: ;; how- many-in-list : (listof X) -> N ;; to count how many items alist. final outcome, so we don't have to supply a complete unambigous rule. In general, though, we rewrite subexpressions in a left -to- right and top -to- bottom order. At each stage in the evaluation,

Ngày đăng: 12/08/2014, 19:20