Sample Midterm 1Date: Monday, October 27th | |
| |
AdministrativeThis is a sample midterm that was given a year ago.
| |
| |
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:
- The ellipsis notation makes BNFs more expressive, for example,
here is a language using ellipsis that cannot be expressed
without it. (If you think that this is the right answer, you
need exactly one example, no additional examples, and no verbal
explanations are necessary.)
- The ellipsis notation does not make BNFs more expressive. Here
is an exact description for taking a BNF that uses ellipsis, and
converting it to one that does not have them. (If you think that
this is the solution, then it's enough to assume a BNF that has
exactly one use of ellipsis and show how to eliminate it.)
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:
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:
- (/ (blank) 0)
- (blank (/ 1 0))
- (car (map (blank) (list (blank))))
- (and (blank) blank 'blank)
- (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
We do this in three steps, and you only need to implement the first two:
- 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.)
- 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
...)) |
- 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 |
- 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.)
- 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.
- 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.
- 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)])))