当通过列表发送时,Clojure的func似乎没有按预期工作

时间:2011-12-19 06:41:46

标签: clojure

在Clojure REPL中,这个表达

( #(for [x %] (+ 100 (second x)))  ['(+ 38) '(+ 48)] )

按预期产生(138 148)

但是这个

( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] )

产生(38 48),这似乎真的很奇怪。

这两个表达式确实应该产生相同的结果! 我错过了什么? 将会欣赏任何解决这个谜团的想法。

顺便说一句,我尝试使用'apply(first x)'并将其余的args打包成一个列表,但似乎并不重要。同样出人意料的结果又回来了。

另外,为了验证+确实从输入中解析了+,我将以下内容提供给REPL

( #(for [x %] (resolve (first x) )) '((+ 38) (+ 48)) )

产生了

 (#'clojure.core/+ #'clojure.core/+)   as expected.

2 个答案:

答案 0 :(得分:10)

( #(for [x %] ((first x) 100 (second x))) ['(+ 38) '(+ 48)] )

在此,+是一个符号,而不是一个函数,因为它已在列表中引用。但是,符号被定义为在作为函数调用时进行映射查找(与关键字相同)。因此('+ 100 38)(get 100 '+ 38)相同。最后一个论点是“如果你在地图中找不到我想要的东西,那就回归”。由于100不是地图,+使用该参数作为返回值。

要让它做你想做的事,你有两个选择:

  1. 使用向量而不是引用列表可确保+得到适当解析。

    ( #(for [x %] ((first x) 100 (second x))) [[+ 38] [+ 48]] )
    
  2. 自行解决,以确保您使用+函数而不是+符号。

    ( #(for [x %] ((resolve (first x)) 100 (second x))) ['(+ 38) '(+ 48)] )
    

答案 1 :(得分:3)

当您引用列表时,例如'(+ 38),列表中的所有项都不会被评估。因此+只是一个符号,而不是对clojure.core中的加法函数的引用。

将此符号作为函数调用的结果有点令人困惑,特别是因为您碰巧使用两个参数调用它。原因已由@mange解释:调用符号作为函数尝试在第一个参数中查找符号,在查找失败时将(可选)第二个参数作为默认值返回:

('x)       ; throws ArityException
('x 1)     ;=> nil
('x 1 2)   ;=> 2
('x 1 2 3) ; throws ArityException

您有几种选择:

  1. 使用向量代替引用列表:[+ 38]。评估向量的所有元素(如在不带引号的列表中),但向量只是一个数据结构,而不是列表中函数调用的语法。
  2. 使用resolve功能查找符号引用的功能。请注意resolve在当前命名空间中查找符号。因此,如果引用列表的构造和函数的调用发生在不同的名称空间中,这可能会导致令人惊讶的结果(如果您碰巧对不同名称空间中的相同符号有两个不同的定义)。
  3. 在列表中使用语法引用而不是简单引用并取消引用该符号(对其进行评估):`(~+ 38)