Part 2: Higher Order Functions
my-cu
y: implement your own version of cu
y. Your version should behave identically to cu
y, but need only work on procedures with fixed arity. E.g.,
((my-cu
y even?) 2)
#t
(((my-cu
y cons) 1) 2)
'(1 . 2)
((my-cu
y cons 1) 2)
'(1 . 2)
Implement tail recursive versions of map and filter (found in "mp1.rkt" as my-map and my-filter).
make-object: creates and returns a rudimentary "object" as a closure over an associative list containing attributes. The returned closure (i.e., function) responds to three "messages": get, set, and update.
get takes an attribute key, whose value is returned (if it exists).
set takes an attribute key and value, which are used to update the associative list
update takes an attribute key and a function which is used to update the associative list by applying it to the cu
ent value
make-object takes one argument, the initial name of the object (associated with the attribute key 'name).
E.g.,
> (define obj (make-object "foo"))
> (obj 'get 'name)
"foo"
> (obj 'set 'name 'bar)
> (obj 'get 'name)
'ba
> (obj 'set 'x 42)
> (obj 'update 'x (lambda (n) (* n 100)))
> (obj 'get 'x)
4200
Part 3: Meta-circular Evaluator (10 points)
In this part you will implement your own limited version of eval. Your version will only understand a very limited subset of Racket, with expressions of the following kind:
expr ::= (lambda (var) expr) ; lambda (function) definition
| (expr expr) ; function application
| var ; variable reference
Note that a lambda may only have one parameter.
Your evaluation function will be passed s-expressions of the above form (the base form will always be a lambda), and should return co
esponding Racket values. Because your evaluator will use Racket features to represent and implement this subset of the Racket language, it is a so-called meta-circular evaluator.
To implement this, you will need to take apart the input to your evaluator and determine which form it is before building the co
esponding Racket form. In order to implement variables (which are just symbols in the input), you will also need to keep track of their bindings in an environment -- we recommend using the associative list which you implemented above.
Here's the evaluator in action:
> ((my-eval '(lambda (x) x)) 10)
10
> (((my-eval '(lambda (f) (lambda (x) (f (f x))))) sqr) 5)
625
We've stu
ed out portions of the evaluator for you as a guide. Feel free to delete/keep what you wish.
Part 4: Free Variables
Lastly, you will implement a function free, which will take the same types of expressions understood by your evaluator, but will instead return a list of all the free variables found in those expressions (order doesn't matter).
You will likely be able to reuse much of your code from part 3!
Some sample results:
(free 'a)
'(a)
(free '(x y))
'(x y)
(free '(lambda (x) x))
'()
(free '(lambda (x) (x y)))
'(y)
(free '(lambda (x) (w (lambda (w) (x y)))))
'(w y)
Testing
I've provided you with test suites for most of the exercises in the "mp1-tests.rkt" source file. If you open the file you'll find that there are a bunch of definitions that looks like this:
(test-case "test-make-object"
(define obj (make-object 'foo))
(obj 'set 'name 'bar)
(obj 'set 'x 42)
(obj 'update 'x (lambda (x) (* x 100)))
(obj 'set 'y 100)
(check-equal? (obj 'get 'name) 'bar)
(check-equal? (obj 'get 'x) 4200)
(check-equal? (obj 'get 'y) 100))
This defines a test case (here, for the make-object function). The check-equal? calls at the end of the test case are assertions. If they fail, they will produce out e
ors that looks like this:
--------------------
test-make-object
FAILURE
name: check-equal?
location: mps/mp1/mp1-test.rkt:14:2
actual: 'foo
expected: 'ba
--------------------
Code Skeleton File:
;;;;; Part 2: Higher Order Functions
(define (my-cu
y f . rest)
(f )
(define (my-map f lst)
(if (empty? lst)
'()
(cons (f (first lst)) (my-map f (rest lst)))))
(define (my-filter pred lst)
(if (empty? lst)
'()
(let ([x (first lst)])
XXXXXXXXXXif (pred x)
XXXXXXXXXXcons x (my-filter pred (rest lst)))
XXXXXXXXXXmy-filter pred (rest lst))))))
(define (make-object name)
void)
;;;;; Part 3: Meta-circular Evaluato
(define (my-eval rexp)
(let my-eval-env ([rexp rexp]
[env '()]) ; environment (assoc list)
(cond [(symbol? rexp) ; variable
XXXXXXXXXXvoid]
[(eq? (first rexp) 'lambda) ; lambda expression
XXXXXXXXXXvoid]
[else ; function application
XXXXXXXXXXvoid])))
;;;;; Part 4: Free Variables
(define (free sexp)
'())
Test File:
#lang racket
(require rackunit
"mp1.rkt")
(test-case "rotate"
XXXXXXXXXXcheck-equal? (rotate 3 ' XXXXXXXXXX))
' XXXXXXXXXX))
XXXXXXXXXXcheck-equal? (rotate 2 '((a) (b (c)) (d e)))
'((d e) (a) (b (c)))))
(test-case "lookup"
XXXXXXXXXXcheck-equal? (lookup 'a '((a . apple) (b . bee) (c . cat)))
'apple)
XXXXXXXXXXcheck-equal? (lookup 2 '((1 . "one") (2 "two" "three") (4 "four" "five")))
'("two" "three"))
XXXXXXXXXXcheck-equal? (lookup 'foo '((a . apple) (2 . "two")))
#f))
(test-case "update"
XXXXXXXXXXcheck-equal? (update 'a 'apple '((b . bee) (c . cat)))
'((b . bee) (c . cat) (a . apple)))
XXXXXXXXXXcheck-equal? (update 'a "auto" '((a . apple) (b . bee) (c . cat)))
'((a . "auto") (b . bee) (c . cat)))
XXXXXXXXXXcheck-equal? (update 1 (list XXXXXXXXXX) '())
' XXXXXXXXXX))))
(test-case "equal-shape?"
XXXXXXXXXXcheck-true (equal-shape? '(1 2 3) '(2 3 4)))
XXXXXXXXXXcheck-false (equal-shape? '(1 2 3) ' XXXXXXXXXX)))
XXXXXXXXXXcheck-true (equal-shape? '(1 (a b) ((#f)) "d" e)
'(2 ("a" "b" XXXXXXXXXXf g)))
XXXXXXXXXXcheck-false (equal-shape? '(a (b . c) d) '(a (b c) d))))
(test-case "my-map"
XXXXXXXXXXdefine lst1 (range 10))
XXXXXXXXXXdefine lst2 '(() (a b c) (a b) ()))
XXXXXXXXXXcheck-equal? (my-map (cu
y + 2) lst1) (map (cu
y + 2) lst1))
XXXXXXXXXXcheck-equal? (my-map length lst2) (map length lst2)))
(test-case "my-filter"
XXXXXXXXXXdefine lst1 (range 10))
XXXXXXXXXXdefine lst2 '(() (a b c) (a b) ()))
XXXXXXXXXXcheck-equal? (my-filter even? lst1) (filter even? lst1))
XXXXXXXXXXcheck-equal? (my-filter (compose not empty?) lst2) (filter (compose not empty?) lst2)))
(test-case "test-cu
ying"
XXXXXXXXXXdefine f1 (lambda (x) (+ x 100)))
XXXXXXXXXXdefine f2 (lambda (x y) (* x y)))
XXXXXXXXXXdefine f3 (lambda (x y z) (* x (+ y z))))
XXXXXXXXXXcheck-equal? ((my-cu
y (lambda () #t))) #t)
XXXXXXXXXXcheck-equal? (my-cu
y f XXXXXXXXXX)
XXXXXXXXXXcheck-equal? ((my-cu
y f XXXXXXXXXX)
XXXXXXXXXXcheck-equal? (my-cu
y f XXXXXXXXXX)
XXXXXXXXXXcheck-equal? ((my-cu
y f XXXXXXXXXX)