Sample Midterm 2

Date: Tuesday, February 16th

 
Name:

Administrative

This is a sample midterm that was given in the 2008 fall semester.

Note about Typed Scheme: make sure that you write proper type declarations for functions that you’re writing. Also, if you use internal definitions then write type declarations for them. Grading will not be strict about the syntax of type declarations, but we will be looking at the types as well as the definitions. Also, in some of the given examples there are type annotations that are missing for the code to work. This should not be a problem for understanding the questions.

Please write clearly, especially in “essay questions”.

(At the end of the exam there is a printout of the Flang implementation, please tear it off now. Do not hand in the exam with it.)

Question Grade  
(1) Simple Scheme Programming /9
(2) Higher-Order Functions /12
(3) BNF Grammar /15
(4) Fill in the blanks /15
(5) Language Extension /18
(6) Structural Recursion with ASTs /20
(7) Language Features /25
(8) The Y Combinator /10
Total /124
 


 

Question 1: Simple Scheme Programming

 9 pts 

Define an all-equal? function that consumes a list of values, and returns #t if the values are all equal?. Here are a few test cases that highlight cases that you need to take care of:
(test (all-equal? (list 1 2 3)) => #f) (test (all-equal? (list 1 1 1)) => #t) (test (all-equal? (list (list 1 2) (list 1 2))) => #t) (test (all-equal? (list "foo" "foo")) => #t) (test (all-equal? (list 'blah)) => #t) (test (all-equal? null) => #t)
Write the definition with a correct type declaration, and a purpose statement.
Hint: You can (and should) use andmap or ormap to make your code very simple.


 

Question 2: Higher-Order Functions

 12 pts 

As you know, the andmap function is expecting a predicate and a list of values, and returns #t if the predicate is true on all of the list items. For the purpose of this question, you can assume that it returns a boolean result:
(: andmap : (A -> Boolean) (Listof A) -> Boolean)
Write a function called andpreds which is a twist on andmap: it expects a list of predicate functions and a single value and returns #t if all of the functions are true for the value. Again, here are a few tests to demonstrate how the function should work:
(test (andpreds (list even?) 10) => #t) (test (andpreds (list integer? even?) 10) => #t) (test (andpreds (list string? even?) 10) => #f) (test (andpreds (list string?) null) => #f) (test (andpreds (list list?) (list 1 2)) => #t) (test (andpreds (list list? null?) null) => #t) (test (andpreds null 10) => #t)
Hint: using andmap will make it very easy; you can assume the above boolean value (that is, ignore the fact that it actually returns the last result if all are not #f).
Remember to write the type and purpose statement.


 

Question 3: BNF Grammar

 15 pts 

Several software packages accept a specification for “pages to print” as a comma separated sequence of integers and integer ranges (two integers separated by a dash). For example,
1 1,3,5 1-10,13,20-27
Write a BNF for such specification. Notes:


 

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.)
You do not need to write types for definitions, except when the question asks for it.

Do this for these expressions:
  1. (blank (random 1000)) ; random does the usual
  2. (blank + 60)
  3. (blank blank) ; easy, but write a type too
  4. (or ’blank (blank))
  5. (let ([blank 330]) blank)


 

Question 5: Language Extension

 18 pts 

(Use the Flang source at the end of the exam for this question.)

We wish to extend the Flang language with two-argument (binary) functions. We begin by extending the BNF with two new rules for binary functions and binary function applications:

<FLANG> ::= ... same as before ... | { fun2 { <id> <id> } <FLANG> } | { call2 <FLANG> <FLANG> <FLANG> } (define-type FLANG ... same as before ... [Fun2 (name1 Symbol) (name2 Symbol) (body FLANG)] [Call2 (fun-expr FLANG) (arg1-expr FLANG) (arg2-expr FLANG)])
You can assume that the parser was modified appropriately. Specifically, you should assume that the parser will throw an error if the two argument names in a fun2 expression are not different. In addition, the VAL type is extended with a new kind of closure type that holds two argument names:
(define-type VAL ... same as before ... [FunV2 (name1 Symbol) (name2 Symbol) (body FLANG) (env ENV)])

Begin by extending the formal evaluation rules with cases for the two new kinds of expressions fun2 and call2. You need to write only these two rules, since the rest of the rules are not modified. Complete the question with the extended eval function — again, you need only the two new cases branches for Fun2 and Call2 because the rest of the code is the same.



 

Question 6: Structural Recursion with ASTs

 20 pts 

A different approach for implementing the new fun2 and call2 expressions is one that we used previously: instead of extending the evaluator, we can translate the new expressions to other expression that eval already knows how to deal with. Specifically, we can convert fun2 expressions to curried fun expressions, and call2 to curried call application expressions. This can be achieved using the following two rewrite rules:
{fun2 {x y} E} -> {fun {x} {fun {y} E}} {call2 f x y} -> {call {call f x} y}
which you will implement in the form of a new preprocess function. This will be “injected” to the evaluator by changing run to be:
(: run : String -> Number) ;; evaluate a FLANG program contained in a string (define (run str) (let ([result (eval (preprocess (parse str)) (EmptyEnv))]) ... same as before ...))
First of all, the two formal evaluation rules should reflect this change, write the new rules. When you’re done, implement the preprocess function that implements these rewrites.


 

Question 7: Language Features

 25 pts 

Important: this question is hard, don’t get stuck on it before you finish the rest of the exam. Note that all answers should be quite short.

  1. Consider the above two implementations of fun2 and call2. The two implementations result in a different semantics for these expressions. Find an expression that can reveal the difference, by evaluating differently based on which implementation is used.
  2. The difference between the two binary function extensions is pointing at a trade-off: each implementation can claim a feature that the other lacks. Find such a feature for each side.
  3. In the first implementation of the binary functions it was important to assume that the two argument names are different. Explain why this assumption is important in that implementation, and what you can gain by removing the assumption from the second one.
  4. In a language implementation like the attached Flang code, do we really need a garbage collector? Explain.
  5. What if we were to change this implementation to use dynamic scope? — Would we need a garbage collector then? (Note: this is a hard question!)


 

Question 8: The Y Combinator

 10 pts 

Our version of the Y combinator is limited to one-argument functions. Is this a problem? Explain if so, or describe how it can be used for multiple arguments if not. Either way, your answer should be very short. (A correct answer can be one word!)


 

The Flang Implementation

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

Please tear this part off, do not hand it in it with the rest of the exam. Do not write solutions on these pages.

#lang pl

#|
The grammar:
  <FLANG> ::= <num>
            | { + <FLANG> <FLANG> }
            | { - <FLANG> <FLANG> }
            | { * <FLANG> <FLANG> }
            | { / <FLANG> <FLANG> }
            | { with { <id> <FLANG> } <FLANG> }
            | <id>
            | { fun { <id> } <FLANG> }
            | { call <FLANG> <FLANG> }

Evaluation rules:
  eval(N,env)                = N
  eval({+ E1 E2},env)        = eval(E1,env) + eval(E2,env)
  eval({- E1 E2},env)        = eval(E1,env) - eval(E2,env)
  eval({* E1 E2},env)        = eval(E1,env) * eval(E2,env)
  eval({/ E1 E2},env)        = eval(E1,env) / eval(E2,env)
  eval(x,env)                = lookup(x,env)
  eval({with {x E1} E2},env) = eval(E2,extend(x,eval(E1,env),env))
  eval({fun {x} E},env)      = <{fun {x} E},env>
  eval({call E1 E2},env1)
           = eval(Ef,extend(x,eval(E2,env1),env2))
                             if eval(E1,env1)=<{fun {x} Ef},env2>
           = error!          otherwise
|#

(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 a 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)])))