user=> (eval '(+ 1 2))
3
user=> (eval '('+ 1 2))
2
user=> (eval (list '+ '1 '2))
3
user=> (eval (list + '1 '2))
3
这是否意味着 '+ 有时与 + 相同?
是否有任何规则来确定应考虑什么情况?
(list '+ '1 '2)
生成符号+
和数字的列表1
和2
。评估它将查找由符号命名的函数并使用参数和+
调用它。1
2
(list + '1 '2)
生成由符号+
和数字命名的函数列表1
和2
。评估它将调用已经直接成为列表元素的函数,并使用列表其余部分的参数。
顺便说一句,从来不需要引用数字。他们在自我评估。
编辑:
您没有在标题中提到的更有趣的案例是该(eval '('+ 1 2))
案例发生了什么?为什么这不会3
像其他所有最终都+
使用参数1
和调用命名的函数的情况一样返回2
?
答案是不同的,因为这一次,您引用了该符号+
两次。因此,符号不会解析为它命名的函数,而是将符号本身作为函数调用。
现在当一个符号被作为函数调用时会发生什么行为?它将其第一个参数视为一个映射,并将其自身视为该映射中的一个键。但是这个数字1
显然不是一个映射,所以它现在通常会返回nil
表示没有找到匹配的键。
但是,在这种情况下,对 symbol 的调用有第二+
个参数,当没有找到匹配的键时,第二个参数被用作返回的默认值。所以这就是表单(eval '('+ 1 2))
返回的原因2
。
编辑 2:
好的,现在最后回答这个问题:为什么引用一个列表与调用list
具有相同参数的函数会产生不同的结果。这是因为引号'
会停止对其使用的列表的所有评估,但该list
函数会在生成将其作为元素的列表之前评估其参数。
当您打印各种表格而不是直接将它们发送到时,这些差异都变得非常清楚eval
:
user> '(+ 1 2)
(+ 1 2)
user> '('+ 1 2)
((quote +) 1 2)
user> (list '+ '1 '2)
(+ 1 2)
user> (list + '1 '2)
(#<core$_PLUS_ clojure.core$_PLUS_@165c64> 1 2)
您会看到第一种和第三种情况产生的结果相同,因为在这两种情况下您都引用+
过一次。在第二种情况下,+
它被引用了两次,而在最后一种情况下,它根本没有被引用。
编辑 3:
最后一个添加有望进一步澄清事情:除非它的第一个元素命名宏或特殊运算符,eval
否则将递归地评估列表的所有元素(包括第一个),然后通过调用其(评估的)第一个参数作为以列表的其余部分作为参数的函数。
如果第一个元素是'+
or (quote +)
(这是写同一事物的两种不同方式,带引号的符号+
),它将评估为符号+
并将其作为函数调用。
如果第一个元素是+
,那将评估为实际的加法函数 ( clojure.core$_PLUS_
)。如果它已经是那个函数,它将保持不变(基本上除了列表和符号之外的所有类型都是自我评估的)。在这两种情况下,eval
都会调用该函数。