Sample Midterm 1

Date: Monday, October 27th

 
Name:

Administrative

This is a sample midterm that was given a year ago.

Question Grade  
(1) Simple Scheme Programming /10
(2) Higher-Order Functions /14
(3) BNF Grammar /16
(4) Fill in the blanks /15
(5) Structural Recursion with ASTs /20
(6) Language Extension /20
(7) Language Features /18
(8) Schlac /12
Total /125
 


 

Question 1: Simple Scheme Programming

 10 pts 

In this question you're dealing with sets of integers, represented as ordered lists of unique numbers (in increasing order). Your mission, should you accept it, is to write a function called merge-sets, that consumes two such sets and produces the union set. The resulting set should also be ordered, and have no repeating items. This makes it possible to implement a linear-time merge. Here are a few examples that describe how this function works:
(test (merge-sets '() '()) => '()) (test (merge-sets '(1 2 3) '(1 2 3)) => '(1 2 3)) (test (merge-sets '(1 3 5) '(2 3 4)) => '(1 2 3 4 5))
Implement this function. Remember to write a proper contract and purpose statement.
(As you can probably guess, it's possible to write a tail recursive version of this function, but you don't need to do that. Just write simple, clear code.)


 

Question 2: Higher-Order Functions

 14 pts 

MzScheme has two very useful list functions: andmap and ormap. The first one, andmap, applies a function over each item in a given list, and only if all results are true (non-#f values), then it returns a true value (which is actually the last result) (in case of an empty input list it just returns #t). ormap is symmetric — it applies a function over each item in the given list, and returns the first true value (which, again, is not necessarily #t), and returns #f if the list is empty.
We want to add another function in the same spirit: xormap. This function will apply the given function to each item in the input list, and if this results in exactly one true result, then this result is returned. Here are some examples to clarify:
(test (xormap even? '()) => #f) (test (xormap even? '(1 3 5)) => #f) (test (xormap even? '(1 2 3 4 5)) => #f) (test (xormap even? '(1 3 4 5 1)) => #t) (test (xormap even? '(4)) => #t) (define (even-num x) (and (even? x) x)) (test (xormap even-num '(1 3 4 5 1)) => 4)
To make things a bit easier, implement a function called countmap, which simply returns the number of items that made the given function return a true value. For example:
(test (countmap even? '()) => 0) (test (countmap even? '(1 3 5)) => 0) (test (countmap even? '(1 2 3 4 5)) => 2) (test (countmap even? '(1 3 4 5 1)) => 1) (test (countmap even? '(4)) => 1) (test (countmap even-num '(1 3 4 5 1)) => 1)
Begin with a definition of countmap, remember the contract and purpose statement. Then define xormap using countmap and ormap. If you do everything right, xormap will be a one-liner.


 

Question 3: BNF Grammar

 16 pts 

Does the addition of the ellipsis notation (or Kleene star) make the BNF grammar language more expressive? In other words — can you specify more languages using this notation, ones that you couldn't specify before?
The answer to this question should be formal. It should be one of these two options:


 

Question 4: Fill in the blanks

 15 pts 

For each one of the following expressions, write a definition for blank in Scheme that makes the expression evaluate to 660. For example, a correct answer for this expression:
(* (blank) 2)
is:
(define blank (lambda () 330))
If you think that it is impossible to write such a definition, explain why this is the case. (Remember: this is Scheme, not Schlac.)

Do this for these expressions:
  1. (/ (blank) 0)
  2. (blank (/ 1 0))
  3. (car (map (blank) (list (blank))))
  4. (and (blank) blank 'blank)
  5. (let ([blank +]) (blank (blank))) ; careful!


 

Question 5: Structural Recursion with ASTs

 20 pts 

Say that you graduated and found a job in Hackers Inc, a software company that specializes in dynamic web adaptations of fuzzy models of database-backed semantic information dynamic queries, based on dynamically configurable stochastic agents. Or something. Whatever the sequence of buzzwords is, it boils down to running lots of machine generated code, and your language of choice is, of course, the ever so popular Flang.
An important issue that you have is that it turns out that such code as many instances of functions that are immediately applied. For example:
{with {x 1} {call {fun {y} {* y 2}} x}}
Your ultimate goal is to find such function applications in the code (at compile time, not run-time), and “pre-apply” them — essentially inline them in the code, before we get to evaluating it. For example, the above code will be transformed to
{with {x 1} {* x 2}}

We do this in three steps, and you only need to implement the first two:
  1. First, you need to write a find-immediate-application function that consumes a FLANG value, looks for such an application expression, and returns the first one it finds. (Reminder: the Flang implementation is attached to the end of the exam for reference.)
  2. The next step is to perform the inlining of such an expression. Implement an inline-application function that performs that inlining. Note that this part works on a single application, one that was found by the previous function. To simplify this part, you can assume that you have the subst function from the substitution-based Flang implementation, the one that has this signature:
    ;; subst : FLANG Symbol FLANG -> FLANG ;; substitutes the second argument with the third argument in the ;; first argument, as per the rules of substitution; the resulting ;; expression contains no free instances of the second argument (define (subst expr from to) (cases expr ...))
  3. The last step is to combine these two together, basically running a loop that finds an immediate application and inlines it, and continues while such expressions still exist in the code. As mentioned above, you do not need to implement this code.
Write the two definitions, and don't forget the function headers.


 


 

Question 6: Language Extension

 20 pts 

Say that you want to extend the environment-based Flang evaluator so it has recursive environments. Unlike in class, you want to avoid changing the ENV type to have all values in boxes. Note that the cycle of pointers that we have seen occur with recursive function bindings has two arrows: one from the environment to the closure, and one from the closure back to the same environment. In class we made the first pointer go indirectly from the environment through a mutable box to the closure. Now we want to do the same for the second pointer: make it go from the closure through a mutable box to the environment. Assume that the Flang BNF is extended with a rec syntax, that the type definition is extended with a matching Rec variant, and that the parser is adapted:
; <FLANG> ::= ... ; | { with { <id> <FLANG> } <FLANG> } ; | { rec { <id> <FLANG> } <FLANG> } ; | ... (define-type FLANG ... [With (name symbol?) (named FLANG?) (body FLANG?)] [Rec (name symbol?) (named FLANG?) (body FLANG?)] ...)
Your job is to implement the rest of the changes. First, you will need to change the FunV variant, and the piece of eval that uses it. (You only need to write new code, no need to copy everything.) Then, you need to write an additional case in eval that deals with Rec syntax objects: it should call out to an extend-rec with the same interface we had in class, but this extend-rec will have a very different body. For reference, this is the class definition:
;; extend-rec : Symbol FLANG ENV -> ENV (define (extend-rec id expr rest-env) (let* ([new-cell (box (NumV 42))] [new-env (Extend id new-cell rest-env)] [value (eval expr new-env)]) (set-box! new-cell value) new-env))
Note that you need to assume that the expression is always a Fun expression, or your code will break. (Warning: the code that you need to write is really very different from this class version, don't try to “adapt” it.)


 

Question 7: Language Features

 18 pts 

  1. The alternative implementation of the recursive rec binder in the previous question has certain advantages and disadvantages. Mention at least one of each. (Be brief, and don't write “just in case” text — wrong and/or redundant arguments will be penalized.)




  2. In the question where you inlined immediate application forms there are two serious problems. A hint for the first problem (which can itself have two bad outcomes in such code) is in this expression:
    {call {fun {x} {* x 2}} {/ y 3}}
    Describe this problem.




  3. The second problem in this inlining has a hint in this monster code, which is the result of manually inlining immediate applications:
    (define foo((lambda(f)((lambda(x)(x x))(lambda(x)(f(x x)))))(lambda(f )(lambda(x)(((x(lambda(x)(lambda(x y) y))(lambda(x y)x))(x(lambda(x)( ...)))))))))
    To see the hint, look for an un-inlined immediate application, and that should lead you to the problem.




  4. Suggest a solution to the first problem (hint: with), which will solve the second one too. (Note: this one can be difficult.)






 

Question 8: Schlac

 12 pts 

When viewed as an integer Church Numeral, what is the meaning of the fixpoint of the sub1 function that we have seen in class? In other words, what is the integer value encoded by (Y sub1)? Your answer should be formal, showing what the meaning is given only facts that you know from class.


 

The Flang Implementation

The following is the Flang implementation that uses environments. Use it as a reference for related questions.

This is for reference only, tear this part off, do not hand it in it with the rest of the exam. Do not write solutions on these pages.

(define-type FLANG
  [Num  (n Number)]
  [Add  (lhs FLANG) (rhs FLANG)]
  [Sub  (lhs FLANG) (rhs FLANG)]
  [Mul  (lhs FLANG) (rhs FLANG)]
  [Div  (lhs FLANG) (rhs FLANG)]
  [Id   (name Symbol)]
  [With (name Symbol) (named FLANG) (body FLANG)]
  [Fun  (name Symbol) (body FLANG)]
  [Call (fun-expr FLANG) (arg-expr FLANG)])

(: parse-sexpr : Sexpr -> FLANG)
;; to convert s-expressions into FLANGs
(define (parse-sexpr sexpr)
  (match sexpr
    [(number: n)    (Num n)]
    [(symbol: name) (Id name)]
    [(cons 'with more)
     (match sexpr
       [(list 'with (list (symbol: name) named) body)
        (With name (parse-sexpr named) (parse-sexpr body))]
       [else (error 'parse-sexpr "bad `with' syntax in ~s" sexpr)])]
    [(cons 'fun more)
     (match sexpr
       [(list 'fun (list (symbol: name)) body)
        (Fun name (parse-sexpr body))]
       [else (error 'parse-sexpr "bad `fun' syntax in ~s" sexpr)])]
    [(list '+ lhs rhs) (Add (parse-sexpr lhs) (parse-sexpr rhs))]
    [(list '- lhs rhs) (Sub (parse-sexpr lhs) (parse-sexpr rhs))]
    [(list '* lhs rhs) (Mul (parse-sexpr lhs) (parse-sexpr rhs))]
    [(list '/ lhs rhs) (Div (parse-sexpr lhs) (parse-sexpr rhs))]
    [(list 'call fun arg) (Call (parse-sexpr fun) (parse-sexpr arg))]
    [else (error 'parse-sexpr "bad syntax in ~s" sexpr)]))

(: parse : String -> FLANG)
;; parses a string containing an FLANG expression to a FLANG AST
(define (parse str)
  (parse-sexpr (string->sexpr str)))

;; Types for environments, values, and a lookup function

(define-type ENV
  [EmptyEnv]
  [Extend (id Symbol) (v VAL) (rest-env ENV)])

(define-type VAL
  [NumV (n Number)]
  [FunV (name Symbol) (body FLANG) (env ENV)])

(: lookup : Symbol ENV -> VAL)
(define (lookup name env)
  (cases env
    [(EmptyEnv) (error 'lookup "no binding for ~s" name)]
    [(Extend id val rest-env)
     (if (eq? id name) val (lookup name rest-env))]))

(: arith-op : (Number Number -> Number) VAL VAL -> VAL)
;; gets a Scheme numeric binary operator, and uses it within a NumV
;; wrapper
(define (arith-op op val1 val2)
  (: NumV->number : VAL -> Number)
  (define (NumV->number v)
    (cases v
      [(NumV n) n]
      [else (error 'arith-op "expects a number, got: ~s" v)]))
  (NumV (op (NumV->number val1) (NumV->number val2))))

(: eval : FLANG ENV -> VAL)
;; evaluates FLANG expressions by reducing them to values
(define (eval expr env)
  (cases expr
    [(Num n) (NumV n)]
    [(Add l r) (arith-op + (eval l env) (eval r env))]
    [(Sub l r) (arith-op - (eval l env) (eval r env))]
    [(Mul l r) (arith-op * (eval l env) (eval r env))]
    [(Div l r) (arith-op / (eval l env) (eval r env))]
    [(With bound-id named-expr bound-body)
     (eval bound-body
           (Extend bound-id (eval named-expr env) env))]
    [(Id name) (lookup name env)]
    [(Fun bound-id bound-body)
     (FunV bound-id bound-body env)]
    [(Call fun-expr arg-expr)
     (let ([fval (eval fun-expr env)])
       (cases fval
         [(FunV bound-id bound-body f-env)
          (eval bound-body
                (Extend bound-id (eval arg-expr env) f-env))]
         [else (error 'eval "`call' expects a function, got: ~s"
                            fval)]))]))

(: run : String -> Number)
;; evaluate a FLANG program contained in a string
(define (run str)
  (let ([result (eval (parse str) (EmptyEnv))])
    (cases result
      [(NumV n) n]
      [else (error 'run
                   "evaluation returned a non-number: ~s" result)])))