Lisp Arith eval(通用口齿不清:温和的介绍)

Lisp Arith eval(通用口齿不清:温和的介绍),lisp,common-lisp,Lisp,Common Lisp,我正在学习,想解决所有的练习 有时我有不同的解决办法。这让我很困惑,我不容易理解这本书的标准答案 例如,使用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

我的解决办法是:

(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))))))
在这种情况下我能做什么?

让我们从“
eval
funcall
是非常不同的野兽”开始

eval
函数接受一个S表达式,并在“空词汇环境”(本质上,只有动态变量可见)中对其求值。
funcall
函数采用函数指示符(函数对象或具有函数绑定的符号)和0个或多个参数。按照normal with函数,参数是在当前词汇环境中计算的

在一般情况下,我建议不要使用
eval
,除非您绝对需要,
funcall
apply
几乎总是解决此类问题的正确工具。

让我们从“
eval
funcall
是非常不同的两种工具”开始

eval
函数接受一个S表达式,并在“空词汇环境”(本质上,只有动态变量可见)中对其求值。
funcall
函数采用函数指示符(函数对象或具有函数绑定的符号)和0个或多个参数。按照normal with函数,参数是在当前词汇环境中计算的

在一般情况下,我建议不要使用
eval
,除非您绝对需要,
funcall
apply
几乎总是解决此类问题的正确工具。

您的解决方案是a)不正确,b)使用错误的方法

正确性

您的函数支持像
(1+(2+3))
这样的表达式,但不支持
((1+2)+3)

当您编写这样一个解决方案时,您需要考虑什么是可能的算术表达式,以及您的代码是否能够计算出一个解决方案。此外,考虑有用的测试用例并运行它们也是一个好主意:

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

有一种更好的方法:

  • 由于表达式中的运算符符号已经是有效的Lisp函数,因此可以调用该函数并提供正确的参数。我们可以利用内置的求值功能,通过递归调用
    arith eval
    来计算参数。这就是书中的解决方案
仍然可能有一个解决方案,
eval
有意义:

  • 将整个表达式从中缀转换为前缀一次,然后调用
    eval
    (此处
    eval
    可能有意义)
类似于
(eval(中缀到前缀表达式))

现在必须将函数
中缀写入前缀

您的解决方案是a)不正确,b)使用错误的方法

正确性

您的函数支持像
(1+(2+3))
这样的表达式,但不支持
((1+2)+3)

当您编写这样一个解决方案时,您需要考虑什么是可能的算术表达式,以及您的代码是否能够计算出一个解决方案。此外,考虑有用的测试用例并运行它们也是一个好主意:

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

有一种更好的方法:

  • 由于表达式中的运算符符号已经是有效的Lisp函数,因此可以调用该函数并提供正确的参数。我们可以利用内置的求值功能,通过递归调用
    arith eval
    来计算参数。这就是书中的解决方案
仍然可能有一个解决方案,
eval
有意义:

  • 将整个表达式从中缀转换为前缀一次,然后调用
    eval
    (此处
    eval
    可能有意义)
类似于
(eval(中缀到前缀表达式))


现在需要编写函数
中缀到前缀

,谢谢你的回答,我很高兴。但我真的很想知道人们是如何根据这本书的教程得到标准答案的。这对我来说毫无逻辑。@programer5566从未读过那本书,我想我是从温斯顿和霍恩(亚马逊链接:)的“Lisp”开始的,作为一个教程。这可能值得一读,对于一个标准文档来说,它的可读性令人惊讶。谢谢梵蒂娜,你是如此的友好和友好。我会找到这本书的,我很高兴见到你。谢谢你的回答,我很高兴。但我真的很想知道人们是如何根据这本书的教程得到标准答案的。这对我来说毫无逻辑。@programer5566从未读过那本书,我想我是从温斯顿和霍恩(亚马逊链接:)的“Lisp”开始的,作为一个教程。这可能值得一读,对于一个标准文档来说,它的可读性令人惊讶。谢谢梵蒂娜,你是如此的友好和友好。我会找到这本书的,很高兴见到你。谢谢乔斯威格先生!我不知道如何对你的回答表示感谢。我只是试着理解什么是测试用例,现在就努力学习谢谢Joswig先生!我不知道如何对你的回答表示感谢。我只是试着理解什么是测试用例,现在就努力学习