我正在学习Common Lisp: a gentle introduction,想要解决所有的练习。
有时我会有不同的解决方案。这让我感到困惑,我不能轻易理解本书的标准答案。
例如,使用arith-eval
:
我的解决方案是:
(defun arith-eval (x)
(cond
((atom x) x)
(t (eval (cons (cadr x)
(cons (car x)
(list (arith-eval (caddr x)))))))))
本书的解决方案:
(defun arith-eval (exp)
(cond ((numberp exp) exp)
(t (funcall (second exp)
(arith-eval (first exp))
(arith-eval (third exp))))))
在这种情况下我该怎么办?
答案 0 :(得分:7)
您的解决方案是a)不正确,b)使用错误的方法。
<强>正确性强>
您的函数支持(1 + (2 + 3))
之类的表达式,但不支持((1 + 2) + 3)
。
CL-USER 6 > (arith-eval '((3 * 5) + 1))
Error: Illegal car 3 in compound form (3 * 5).
当您编写这样的解决方案时,您需要考虑可能的算术表达式以及您的代码是否可以计算解决方案。另外,考虑有用的测试用例并运行它们是个好主意:
CL-USER 14 > (defparameter *test-cases*
'( ( ((1 + 2) + 3) . 6)
( (1 + 2 + 3) . 6)
( (1 + (2 + 3)) . 6)))
*TEST-CASES*
CL-USER 15 > (loop for (test . result) in *test-cases*
collect (list (ignore-errors (eql (arith-eval test)
result))
test))
((NIL ((1 + 2) + 3)) ; failed
(NIL (1 + 2 + 3)) ; failed, but probably not required
(T (1 + (2 + 3))))
<强>方法强>
您的代码创建一个Lisp表单,然后调用eval
,并以递归方式执行。
解决Lisp练习的第一条规则:不要使用EVAL
有一种更好的方法:
arith-eval
来计算参数。这是本书中的解决方案。仍然可能存在eval
有意义的解决方案:
eval
(此处eval
可能有意义。)类似于(eval (infix-to-prefix expression))
。
现在必须编写函数infix-to-prefix
。
答案 1 :(得分:3)
让我们从&#34开始; eval
和funcall
是非常不同的野兽&#34;。
eval
函数采用S表达式并在&#34; null词法环境中对其进行评估&#34; (基本上,只有动态变量可见)。 funcall
函数采用函数指示符(函数对象或具有函数绑定的符号)和0或更多参数。按照常规函数,参数在当前词法环境中进行评估。
在一般情况下,我建议不要使用eval
,除非您绝对需要,funcall
或apply
几乎总是适合此类问题的工具。