为什么Clojure允许(eval 3),虽然3没有引号



我正在学习Clojure,并试图通过类比Python的类似特性来理解reader、引用、eval和同象性。

在Python中,避免(或延迟)求值的一种方法是将表达式括在引号之间,例如:'3 + 4'。稍后可以使用eval对其进行评估,例如。eval('3 + 4')生成7。(如果你只需要引用Python的值,你可以使用repr函数而不是手动添加引号。)

在Lisp中,您使用quote'来引用,使用eval来求值。(eval '(+ 3 4))生成7

因此,在Python中,"引号"的内容由字符串表示,而在Lisp中,它由以quote为第一项的列表表示。

我的问题,最后:为什么Clojure允许(eval 3),虽然3没有引用?这仅仅是Lisp风格的问题(尽可能给出答案而不是错误),还是背后有其他原因?这种行为对Lisp来说是必要的吗?

简短的回答是数字(例如符号和字符串)对自己求值。引用指示lisp(读者)传递未求值的引用后面的内容。eval然后得到你写的列表,但没有引号,然后求值(在(eval '(+ 3 4))的情况下,eval将在两个参数上求值函数调用(+))。

最后一个表达式的情况如下:

  1. 当您按enter键时,将对表达式求值。它包含一个普通函数调用(eval)和一些参数。
  2. 计算参数。第一个参数包含一个引号,它告诉读者生成引号之后的内容(实际的(+ 3 4)列表)。
  3. 没有更多的参数,实际的函数调用被求值。这意味着使用列表 (+ 3 4)作为参数调用eval函数。
  4. eval函数再次执行相同的步骤,找到正常函数+和参数,并应用它,获得结果。

其他答案已经解释了机制,但我认为哲学要点在于lisp和python看待"代码"的不同方式。在python中,表示代码的唯一方式是字符串,因此尝试计算非字符串当然会失败。Lisp有更丰富的代码数据结构:列表、数字、符号等等。因此表达式(+ 1 2)是一个列表,包含一个符号和两个数字。在求值一个列表时,必须首先求值它的每个元素。

因此,在运行lisp代码的普通过程中,需要计算一个数字是非常自然的。为此,数字被定义为"自我评估",这意味着它们在评估后与之前是一样的:只是一个数字。eval函数对裸"代码片段"3应用的规则与编译器在编译大型表达式(例如(+ 5 3))的第三个元素时应用的规则相同。对于数字来说,这意味着别管它。

3的值应该是多少?Lisp将一个数字求值给它自己是最有意义的。我们想要求在代码中引用数字吗?那将不太方便,而且极有问题:

不是

(defun add-fourtytwo (n)
   (+ n 42))

我们必须写

(defun add-fourtytwo (n)
   (+ n '42))

代码中的每个数字都需要加引号。缺少引号会触发错误。这可不是什么好东西。

作为旁注,想象一下当您想在代码中使用eval时会发生什么。

(defun example ()
  (eval 3))
上面的

是错误的。数字需要加引号

 (defun example ()
   (eval '3))
上面的

可以,但是在运行时生成错误。Lisp计算'3为数字3。但是在这个数字上调用eval将是一个错误,因为它们需要被引用。

所以我们需要写:

(defun example ()
   (eval ''3))

这不是很有用…

在Lisp历史中,

数总是自求值的。但是在早期的Lisp实现中,其他一些数据对象,比如数组,并不是自求值的。同样,由于这是一个巨大的错误来源,像Common Lisp这样的Lisp方言已经定义了所有数据类型(除了列表和符号)都是自求值的。

要回答这个问题,我们需要看一下lisp中的eval定义。例如,在CLHS中有一个定义:

语法:eval form => result*

参数和值:表单-表单。
结果-表单评估产生的值。

其中form

  1. 任何要求值的对象。
  2. 符号、复合形式或自求值对象。
  3. (用于操作符,如在<<operator>> form'') a compound form having that operator as its first element.中)常数的形式。"

在您的案例中,编号"3"是self-evaluating objectSelf-evaluating objecta form that is neither a symbol nor a cons is defined to be a self-evaluating object。我相信对于clojure,我们可以用list代替cons

在clojure中,只有listseval解释为函数调用。其他数据结构和对象作为自求值对象求值。

'(+ 3 4) = (list '+ 3 4)'(由阅读器转换为quote函数)只是避免给定形式的求值。因此在表达式(eval '(+ 3 4))中,eval将列表数据结构('+ 3 4)作为参数。

相关内容

  • 没有找到相关文章

最新更新