#| The exam will use only the following subset of Scheme: define lambda cond if and or ' cons car cdr null? pair? list? list append + - * / = < > >= <= eq? not symbol? number? vector make-vector vector-ref vector-set! It will also use the following in given programs, but you will not be asked to generate program using them: define-datatype cases The exam may: 1. Show you a brand-new datatype, and ask you to generate examples, and perhaps "prove" that an example is valid. E.g.: = wabe | (gyre . ) | ( . gimble) Or, equivalently: A slithy-tove is * 'wabe * (cons 'gyre st) * (cons st 'gimble) where st is a slithy-tove Which of the following expressions produce slithy-toves? * 1 - no * 'wabe - yes * '(gyre wabe . gimble) - yes Proving that '(gyre wabe . gimble) is a slity-tove: '(gyre wabe . gimble) = (cons 'gyre (cons 'wabe 'gimble)) ---- --------|----------- ---------------|----|----------- | | |- by case #1 | |- by case #3 |- by case #2 2. Ask you to write functions for the given datatype. Your answer MUST: a. Have a contract b. Show examples c. Implement the function in a way that follows the data definition. (The purpose will be given as part of the question.) 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 (cons 'gimble 'wabe)) = #f Note that every case of the data definition is covered by an example! c. The function implement must have the following 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)) ...])) 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. (Yes, Scheme would let you use "if" for the case split, but don't!) 3. 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?))) (define (f st) (cases slithy-tove st (wabe-tove () 0) (gyre-tove (inner-st) (+ 1 (f inner-st))) (gimble-tove (inner-st) (f inner-st)))) You should be able to predict the value of (f (gyre-tove (gyre-tove (gyre-tove (gimble-tove (wabe-tove)))))) ---------------------------------------------------------------------- So far, we have maintained a 1-to-1 correspondence between data descriptions of the form ; An X is ... and BNF descriptions of the form = ... But consider the following data definition: A slithy-tove2 is * 'wabe * (vector 'gyre st) * (list 'gimble st) where st is a slithy-tove2 This definition describes a very different collection of data than slithy-tove. Nevertheless, it has essentially the same expressive capability as slithy-tove. Similarly, given the "define-datatype" expression above, we could say A slithy-tove3 is * (wabe-tove) * (gyre-tove st) * (gimble-tove st) where st is a slithy-tove3 This is a third data representations of the same idea. In general, BNF definitions need not generate Scheme-friendly patterns of characters. For example, we might define a grammar for legal poems as = in the wabe | gyre ; | gimble ; Examples include poems such as in the wabe gyre ; in the wabe gimble ; gimble ; in the wabe Of course, since these poems contain ";", we can't turn them into Scheme data by sticking a quote at the beginning. Instead, we must choose a separate, data definition to implement poems. We might, for example, choose slithy-tove3 as the data collection to be used for all programs that process slithy-tove4 poems. [We could even use procedures to represent elements of slithy-tove4. Homework problem 3.4 asked you to do just that for the position-based environment datatype. We won't often choose such a reprsentation, though.] We will usually choose a "define-datatype" representation for data. We'll do that so often that we're going to define a convention for writing a BNF and giving it a Scheme reprsentation with "define-datatype", all at the same time. = in the wabe -------------- | wabe-tove () | -------------- | gyre ; ---------------- | gyre-tove (st) | ---------------- | gimble ; ------------------ | gimble-tove (st) | ------------------ When the BNF is annotated with boxes, as above, it means: 1. We will have a "define-datatype" expression defining a datatype with the same name as the left-hand side of the BNF production. In this case, it means we have a "slithy" datatype. 2. The datatype will have the same number of variants as the BNF --- three, in this case --- where each variant corresponds to a case in the BNF. 3. Each variant's name is given in the box. The field names are given in parentheses after the variant name. Thus, the "define-datatype" expression to go with the above BNF is (define-datatype slithy-tove slithy-tove? (wabe-tove ()) (gyre-tove (st slithy-tove?)) (gimble-tove (st slithy-tove?))) WRONG TERMINOLOGY (from lecture): I called the BNF form "abstract", because I thought of it as describing the essential data (poems about slithy toves). I called the the other form of data definitions "concrete", because it determines specific representations using Scheme data. RIGHT TERMINOLOGY (from book, and rest of the world): The BNF form is the CONCRETE one. It's concrete in the sense that the actual poem a poet would write takes the form defined by the BNF. As we invent various programming languages, the BNF defines the actual syntax that a programmer using our languages would employ. The "define-datatype" reprsentation of BNF elements is the ABSTRACT one. It's abstract because we ignore unimportant details like semi-colons and parentheses, and just incorporate the important details. |#