Announcements: - Demo sessions scheduled for Upson B7 Wed and Thu 7 & 7:30pm PC section -- look for David and/or Melissa. They'll show how to set up and get Noodlle running. - Problem set 1 is available on-line. Direct questions to cornell.class.cs212 (preferably.) - Much information online, but we haven't explained it all yet -- be patient Today: - more on methods - the substitution model Syntax: e ::= p | c p ::= | | | #t | #f includes 1, -31, 0.0, 3.1415 includes "foo", "cs212 is a great course" includes x, abcd, a-long-variable-name, +, +1 c ::= ( e* ) where e* is separated by spaces (OPERATOR operand1 ... operandn) Example: (* (+ 3 4) (+ 1 2)) -> Inner-most parenthesis first, usually left-to-right order Special Form 1: define (define (x ) (+ 9 4)) In general: (define (var ) exp) Intuitively, evaluates exp until we have a value (e.g., an integer) and then binds the result to the variable var. Special Form 2: method (method ((x ) (y )) (+ (* x x) y)) Methods are values, just as integers or strings are values. Thus, just as we can bind an integer or string to a variable, or pass an integer or string as an argument to a method, we can also bind a method to a variable or pass the method as an argument to another method. Special Form 3: if (if test consequent alternate) (define (abs ) (method ((x )) (if (< x 0) (- 0 x) x))) (define (fact ) (method ((x )) (if (<= x 1) 1 (* x (fact (- x 1)))))) If test evaluates to #t then evaluate consequent and return its value as the result of the entire expression, otherwise, return the value of alternate. --------------------------------------------------------------- values: integers, strings, #t, #f, and methods (including builtin methods) non-values: variables, compound expressions (except for methods) To distinguish values and non-values, we'll draw boxes around the values. Every well-formed expression evaluates to a value. When is an expression well-formed? We'll see... Evaluation of an expression E proceeds (informally) as follows: 1. if E is already a value, then E evaluates to {E}. 2. if E is a (non-special) combination of the form (E1 E2 ... En) then we evaluate E1 yielding value V1, evaluate E2 yielding value V2, ... , and evaluate value En yielding value En. If V1 is a method (method ((x1 ) ... (xn )) E'), then substitute Vi for xi within E' yielding E'' and then evaluate E''. For example: To evaluate E = ((method ((x ) (y )) y) 3 4) we must first evaluate the subexpressions (method ...), 3, and 4 to values. Fortunately, they're already values so this part is done. Now, we must substitute 3 for x and 4 for y in the expression "y" and evaluate the resulting expression: [3/x,4/y]y -> 4 The resulting expression (after the substitution) is 4. To evaluate it, we use rule 1 since it's already a value and we're done. What about primitive operations like +? For example, how does: (+ 1 2) evalaute? There are special rules to deal with evaluation of primitive operations like + that are particular to the primitives. However, the behavior is typically intuitive. In this case: (+ 1 2) -> 3 In general, (+ V1 V2) -> V where V is the sum of the integers V1 and V2. We have similar rules for other primitives including *, -, /, etc. What about other special forms? In particular, what about if? (if E1 E2 E3) is different than other expressions: we don't eagerly evaluate E2 and E2. Instead, we only evaluate E1. If it evaluates to the value {#t} then we evaluate E2 and otherwise E3. What about define? (define (x ) E) evaluate E to a value V and then "bind" V to x. Informally, this means that whenever we see x, we'll substitute V for it. For instance: (define (x ) (+ 3 4)) (+ x 2) evaluates as follows: First evaluate (+ 3 4). This evaluates to the value 7. We bind 7 to x, meaning we substitute 7 for x in all remaining expressions. Then we evaluate (+ 7 2) which yields 9. The same is true for methods: (define (square ) (method ((x )) (* x x))) (square (+ 1 1)) Evaluates as follows: the (method ...) is already a value so we substitute it for "square" yielding the expression: ((method ((x )) (* x x)) (+ 1 1)) Now we must evaluate all the sub-expressions of this combination until they're values. The method is already a value, but (+ 1 1) is not. This evaluates to 2 and we're left to evaluate: ((method ((x )) (* x x)) 2) To do so, we invoke rule 2 which says to substitute 2 fo x in the body of the method and evaluate the resulting expression: [2/x](* x x) = (* 2 2) which evaluates to 4. Now let's see a more complicated trace: (define (fact ) (method ((x )) (if (<= x 1) 1 (* x (fact (- x 1)))))) (fact 3) We begin by substituting the method for the variable fact: ((method ((x )) (if (<= x 1) 1 (* x (fact (- x 1))))) 3) Now we invoke rule 2 and substitute 3 for x yielding: (if (<= 3 1) 1 (* 3 (fact (- 3 1)))) Now we use the if rule and evaluate (<= 3 1) which yields #f so we continue evaluating (* 3 (fact (- 3 1))) Again, we substitute the definition of the method for fact yielding: (* 3 ((method ((x )) (if (<= x 1) 1 (* x (fact (- x 1))))) (- 3 1))) (* 3 ((method ((x )) (if (<= x 1) 1 (* x (fact (- x 1))))) 2)) (* 3 (if (<= 2 1) 1 (* 2 (fact (- 2 1))))) (* 3 (if #f 1 (* 2 (fact (- 2 1))))) (* 3 (* 2 (fact (- 2 1)))) (* 3 (* 2 ((method ((x )) (if (<= x 1) 1 (* x (fact (- x 1))))) (- 2 1)))) (* 3 (* 2 ((method ((x )) (if (<= x 1) 1 (* x (fact (- x 1))))) 1))) (* 3 (* 2 (if (<= 1 1) 1 (* 1 (fact (- 1 1)))))) (* 3 (* 2 (if #t 1 (* 1 (fact (- 1 1)))))) (* 3 (* 2 1)) (* 3 2) 6