What we've studied so far ------------------------- Foundations: * Grammars as set definitions - membership proofs - concrete and abstrct syntax via grammars * Evaluation through reduction steps * A small dialect of Scheme (as an archetypal functional language) - define, cond, let, let*, letrec, lambda, function call, define-datatype, cases, begin, numbers, booleans, symbols, pairs (and lists), vectors * Processing members of sets - especially members of recursively-defined sets - contracts, examples, templates - interpreter as a prominent example Language constructs: * Lexical scope - free, binding, and bound occurrences - environments - let, letrec, let* (let ([x 7] [y x]) : << free x ...) ; << let bindings start here (let* ([x 7] [y x]) ; << bound x, x binding starts here ...) ; << y binding start here ;; v-- x, y binding start here (letrec ([x (lambda (z) y)] ;; bound y [y (lambda (z) x)]) ;; bound x ...) * Functions and closures - first-order (at top-level) versus higher-order (at expressions) - evaluation of `proc' and function calls Exam Content ------------ In-class, open-book, open-notes. The exam might... ... show you a brand-new datatype, and ask you to generate examples, and perhaps prove that an example is valid. E.g.: = 'wabe | (cons 'gyre ) | (list 'gimble ) Or, equivalently: A slithy-tove is one of * 'wabe * (cons 'gyre st) * (cons st 'gimble st) where st is a slithy-tove 'wabe (list 'wabe 'gimble 'wabe) Which of the following expressions produce slithy-toves? * 1 - no * 'wabe - yes * (cons 'gyre (list 'wabe 'gimble 'wabe)) - yes Prove that (cons 'gyre (list 'wabe 'gimble 'wabe)) is a slity-tove: 'wabe in 'wabe in ----------------------------------------------- (list 'wabe 'gimble 'wabe) in --------------------------------------------------------- (cons 'gyre (list 'wabe 'gimble 'wabe)) in Another example, this time with more than one datatype: = 1 | (list ) = 0 | Which of the following expressions produce pings? 1 - yes 2 - no (list 0 0) - yes (list 1 (list 0 0)) -yes Prove that the last example above is a ping: 0 in 0 in ------------------------ 1 in (list 0 0) in ------------ -------------------- 1 in (list 0 0) in ---------------------------------- (list 1 (list 0 0)) in ... ask you to write functions for a given datatype. !!! Your answer MUST implement the function in a way that !!! !!! follows the data definition. !!! You may be asked to write examples, too. For example, suppose you are asked to implement "slithy-tove-gyres?" which takes a slity-tove and returns #t if it contains 'gyre, #f otherwise. a. The contract is: slithy-tove-gyres? : slithy-tove -> bool b. Examples: (slithy-tove-gyres? 'wabe) = #f (slithy-tove-gyres? (cons 'gyre 'wabe)) = #t (slithy-tove-gyres? (list 'wabe 'gimble 'wabe)) = #f Note that every case of the data definition is covered by an example! The following is a BAD EXAMPLE and would be rejected: (slithy-tove-gyres? 'gyre) = #t because 'gyre is not a slithy-tove. c. The implementation must have the following template shape: (define (slithy-tove-gyres st) (cond [(eq? st 'wabe) ...] [(eq? (car st) 'gyre) ... (slithy-tove-gyres (cdr st))...] [else ... (slithy-tove-gyres (car st)) ... (slithy-tove-gyres (caddr st)) ...])) Of course, your answer may choose a different name for the function argument; the test to find the "wabe" case could use "symbol?"; and "else" could be replaced with a specific test. But there is NO CHOICE in the overall form of the "cond", or the placement of recursive calls. (define (slithy-tove-gyres? st) (cond [(eq? st 'wabe) #f] [(eq? (car st) 'gyre) #t] [else (or (slithy-tove-gyres? (car st)) (slithy-tove-gyres? (caddr st)))])) Example #2: Implement `ping-sum', which takes a ping and adds up its numbers. ; ping-sum : ping -> num ; (ping-sum 1) = 1 ; (ping-sum (list 1 (list 1 1))) = 3 (define (ping-sum p) (cond [(and (number? p) (= p 1)) 1] [else (+ (pong-sum (car p)) (pong-sum (cadr p)))])) ; pong-sum : pong -> num ; (pong-sum 0) = 0 ; (pong-sum 1) = 1 (define (pong-sum p) (cond [(and (number? p) (= p 0)) 0] [else (ping-sum p)])) An example *unacceptable* answer: (define (ping-sum p) [(number? p) p] [else (+ (ping-sum (car p)) (ping-sum (cadr p)))]) ... ask you to understand a program using "define-datatype" and "cases". For example, given: (define-datatype slithy-tove slithy-tove? (wabe-tove ()) (gyre-tove (st slithy-tove?)) (gimble-tove (st slithy-tove?) (another slithy-tove?))) (define (f st) (cases slithy-tove st (wabe-tove () 0) (gyre-tove (inner-st) (+ 1 (f inner-st))) (gimble-tove (inner-st another-st) (+ (f inner-st) (f another-st))))) You should be able to predict the value of (f (gyre-tove (gyre-tove (gyre-tove (gimble-tove (wabe-tove) (wabe-tove)))))) ... ask you about lexical scope. You might have to draw arrows to show bindings, or compute lexical addresses. Given an expression let x = 3 y = w in let x = 7 y = x in letrec f = proc(z)(f +(z,x)) in (f y) * Draw arrows from bound variable to binding occurrences: v---------. let x = 3 \ y = w \ in let x = 7 <-/---------------. ,---> y = x -' v------. \ / in letrec f = proc(z)(f +(z, x)) \ ,^-----------' `----------|-. in (f y) * List the free vars: w * List the bound vars: x y z f ... ask about environments and closures at points in an evaluation. Given the following expression for a call-by-value language (denoted values = expressed values): let y = 2 in let f = proc(x)+(x,y) in (f 3) * Describe the closure bound to "f" at the point where "(f 3)" is the current expression. Args: x Body: +(x,y) Environment: { y = 2 } * Describe the environment at the point during evaluation where "+(x,y)" is the current expression. Environment: { y = 2, x = 3} Given the following expression (denoted values = expression values): let g = let a = 10 in proc(z)a in let f = proc(w)(g w) in (f 1) * Describe the closure bound to "g" at the point where "(g w)" is the current expression. Args: z Body: a Environment: { a = 10 } * Describe the environment at the point during evaluation where "(f 1)" is the current expression. Environment: { f = }>, g = } ... ask you to "play interpreter", showing the recursive calls to an evaluation expression. Given the following expression: let y = 2 in let f = proc(x)+(x,y) in (f 3) Describe a trace of the evalaution in terms of arguments to an "eval-expression" interpreter function for every call. For literal, variable, and proc expressions, show the result. eval expr = let y = 2 in let f = proc(x)+(x,y) in (f 3) env = { } eval expr = 2 env = { } result = 2 eval expr = let f = proc(x)+(x,y) in (f 3) env = E1 = { y = 2 } eval expr = proc(x)+(x,y) env = E1 result = eval expr = (f 3) env = E2 = { f = , E1 } eval expr = f env = E2 result = eval expr = 3 env = E2 result = 3 eval expr = +(x, y) env = { x = 3 , E1 } eval expr = x env = {x = 3 , E1} result = 3 eval expr = y env = {x = 3 , E1} result = 2 A more subtle example for practice: let g = let a = 10 in proc(z)a in let f = proc(w)(g w) in (f 1)