user=> (eval '(+ 1 2))
3
user=> (eval '('+ 1 2))
2
user=> (eval (list '+ '1 '2))
3
user=> (eval (list + '1 '2))
3
这是否意味着'+有时与+相同?
是否有任何规则来确定应考虑哪种情况?
答案 0 :(得分:5)
(list '+ '1 '2)
会生成符号+
以及数字1
和2
的列表。评估它将查找符号+
命名的函数,并使用参数1
和2
调用它。
(list + '1 '2)
生成一个由符号+
和数字1
和2
命名的函数列表。评估它将使用列表其余部分的参数调用已经直接作为列表元素的函数。
顺便说一下,从来没有必要引用数字。他们是自我评估的。
修改强>:
您在标题中未提及的更有趣的案例是(eval '('+ 1 2))
案例中发生的情况?为什么这不会像其他情况一样返回3
,这些情况最终都会调用+
使用参数1
和2
命名的函数?
答案是它有所不同,因为这次你引用了符号+
两次。因此,符号不会被解析为它命名的函数,而是将符号本身称为函数。
现在将符号作为函数调用时的行为是什么?它将其第一个参数视为地图,并将自己视为该地图中的关键。但是数字1
显然不是地图,因此现在通常会返回nil
以表示找不到匹配的密钥。
在这种情况下,对符号+
的调用有第二个参数,第二个参数被用作默认值,当没有找到匹配的键时返回。这就是表单(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
将在通过调用评估列表本身之前递归计算列表的所有元素(包括第一个元素)将其(已评估的)第一个参数作为函数,将列表的其余部分作为参数。
如果第一个元素是'+
或(quote +)
(这是两种不同的方式来编写相同的内容,引用的符号+
),它将评估为符号{{1这将被称为函数。
如果第一个元素是+
,那么它将评估实际的加法函数(+
)。如果它已经是那个函数,它将保持不变(除了列表和符号之外基本上所有类型都是自我评估的)。在这两种情况下,clojure.core$_PLUS_
都会调用该函数。