Usually a good plan is to write some unit tests first. Examples of what the function should return for certain outputs. Try to think of boundary or corner cases, too. For instance:
(require rackunit)
(check-equal? (my-eval '(1 + (3 * 4)))
13)
(check-equal? (my-eval '(20 + 20))
40)
(check-equal? (my-eval 1)
1)
Of course these will all fail initially. But your goal is to get them to pass.
Next, you don't need to use eval
, and shouldn't. You almost never want to use eval
in real life. (Plus, isn't the whole point of your exercise to implement (some of) what eval
implements?)
Finally, unless you have a class assignment that forbids it, I recommend using match
instead of car
, cadr
etc. With match
, it's simply:
(define (my-eval x)
(match x
[(list lhs '* rhs) (* (my-eval lhs) (my-eval rhs))]
[(list lhs '+ rhs) (+ (my-eval lhs) (my-eval rhs))]
[(list) (list)]
[_ x]))
You can also use quasi-quoting for the match
patterns, which I usually find cleaner. The equivalent, that way:
(define (my-eval x)
(match x
[`(,lhs * ,rhs) (* (my-eval lhs) (my-eval rhs))]
[`(,lhs + ,rhs) (+ (my-eval lhs) (my-eval rhs))]
[`() `()]
[_ x]))
Although some don't like the `
s and ,
s involved, I prefer those to all the list
s.