First of all, the basic call that does the trick is ... drum roll ... -
(apply (symbol-function (car expr)) (cdr expr))
this assuming - for a moment - that all the arguments in the expression are already numbers.
This one line replaces all four cases in your code which are all exact copies of one another, up to the operation to be performed.
Now, to make sure we have numbers, we just need to call same eval
on each of them. If they were numbers, they will stay as is, and if not - they'll get evaluated.
Let's just call our new function calc
, for "calculate", instead:
(defun calc (expr) ; our "eval"
(cond
((numberp expr) expr)
(T (call (car expr)
(mapcar #'calc (cdr expr))))))
(defun call (op args) ; our "apply"
(apply (symbol-function op)
args))
That's all. If you consider this cheating, you can call the operation by hand, but still you don't need to copy the same block of code four times for that. :)
If you indeed write the call
yourself to call the operation by hand, do note that the default value for (*)
is 1, not 0; and that there's no default value for (-)
and (/)
in Common Lisp (as tested in CLisp). Also, (/ 2)
should return 1/2
.