[2/17/99] =============================================================================== * Note - it was said on the web that we _encourage_ work in pairs. The newsgroup is a great tool for finding a partner if you don't have one yet. =============================================================================== * A bit more sophisticated example: -------------------- (define (plus x) (+ x 2)) -------------------- We saw that to generalize this, we replace 2 by a variable and add it to the argument list, but what if we think differently - we can say that there are many versions of similar functions: -------------------- (define (plus x) (+ x 1)) or (define plus (lambda (x) (+ x 1))) (define (plus x) (+ x 2)) or (define plus (lambda (x) (+ x 2))) (define (plus x) (+ x 3)) or (define plus (lambda (x) (+ x 3))) (define (plus x) (+ x 4)) or (define plus (lambda (x) (+ x 4))) ... -------------------- How would we write a function that will get y and return one of these functions, namely (lambda (x) (+ x y))? This is easy to write when thinking about it in this way: -------------------- (define plus (lambda (y) (lambda (x) (+ x y)))) -------------------- Spend time here clarifying the meaning of plus, (plus 1) and ((plus 1) 2). Generally, every function that we write like: -------------------- (define f (lambda (x y...) ...)) -------------------- Can be rewritten as: -------------------- (define f (lambda (x) (lambda (y) ...))) -------------------- This is called Currying. =============================================================================== * Some more special forms that can be useful - go over these really quick: -------------------- (when condition body...) (unless condition body...) -------------------- which is the same as -------------------- (if condition (begin body...)) (if (not condition) (begin body...)) -------------------- but these are a little more convenient when you a body made of several expressions. This makes sense since you only want to use these expressions for side effect since you don't know what will be the result of the expression when the condition is false (or true in the case of unless). A general convenient expression that is used as this construct in C/Jave languages: -------------------- if (cond-1) { body-1...; } else if (cond-2) { body-2...; } else if (cond-3) { ... ... } else if (cond-n) { body-n...; } else { body-else; } -------------------- is: -------------------- (cond (cond-1 body-1...) (cond-2 body-2...) ... (else body-else)) -------------------- What this will do is to go over the conditions one by one, as soon as we find one which is true (evaluates to something other than #f), we stop evaluating conditions and start evaluating the corresponding body expressions. The result value is the the result of evaluating the last one of these expressions. The else part is optinal - if it is not specified, and no condition is true, the result is unspecified. Another note - the "or" and "and" logical operators are working on a "short-circuit" principle - so they don't evaluate more than they need to. For this to work, they must also be defined as special forms. So for example, you could rewrite: -------------------- (when condition body...) -------------------- as: -------------------- (and condition (begin body...)) -------------------- This is especially convenient when you want to check several conditions, stopping as soon as you realize there's no point going further, for example: -------------------- (and (can-quit-now?) (ask-if-user-wants-to-quit?) (close-all-windows) (quit-application)) -------------------- This will avoid a user prompt in case you can't quit, and avoid quitting the application if you couldn't close all windows (assuming close-windows returns #f if it couldn't work). It might be preferable to use -------------------- (when (and (can-quit-now?) (ask-if-user-wants-to-quit?) (close-all-windows)) (quit-application)) -------------------- since that emphasizes the fact that (quit-application) is not part of the condition but the body of the conditional statement. The "and" (and "or") form can be used in this way relying on the fact that it will return the last non-#f result if all are non-#f (the first for "or"). The reason that this can be used, is that it is part of the RxRS specifications. Example: -------------------- (and (number? x) (not (zero? x)) (/ 1 x)) -------------------- returning 1/x or #f. =============================================================================== * Important note about Currying that is useful for PS#2: When we use the curried plus function to write: -------------------- (define plus3 (plus 3)) -------------------- then plus3 is actually an object that "remembers" the stored number 3. In fact, if we have any object that is the result of -------------------- (define something (plus N)) -------------------- we can retrieve the stored value by evaluating (something 0). This can be used in an even more direct way to get a procedural object that stores any Scheme value, not necessarily a number: -------------------- (define (put-in-box x) (lambda (y) x)) (define (look-in-box x) (x "anything")) -------------------- Now, what about that unused y argument to put-in-box? We could just remove it, or we can use it to select some value: -------------------- (define (put-in-box x) (lambda (y) (cond ((eq? y 1) x) ((eq? y 2) (/ (* x pi) 180)) (else (error "Didn't expect" y))))) (define (look-in-box1 x) (x 1)) (define (look-in-box2 x) (x 2)) -------------------- Now, how would this be modified so we get an implementation of a pair, and two selectors? =============================================================================== * Evaluation cheater. To avoid formal rules - explain what it actually does, in contrast to the other let forms. Important - cannot actually use other bindings in the same letrec, only functions so that their evaluation is delayed. Some examples of how to evaluate. ===============================================================================