解构列表中的函数

Destructuring a function in a list

我在 Clojure 中有以下两个测试用例,一个解构向量,一个解构列表:

user=>  ((fn [[a op b]] (op a b)) [1 + 2])
3
user=>  ((fn [[a op b]] (op a b)) '(1 + 2))
2

为什么符号 + 在第一个测试用例而不是第二个测试用例中被评估?引号是递归的,所以在第二个测试用例中 op 真的是 'op?

首先,我们可以使用简单的 println 简单地查看表单是否以预期的方式被解构:

(def fn1 
(fn [[a op b]] 
 (println "a>" a) 
 (println "b>" b) 
 (println "op>" op) 
 (op a b)))


 (fn1 [1 + 2])
 ; a> 1
 ; b> 2
 ; op> #object[clojure.core$_PLUS_ 0x5b95b23d clojure.core$_PLUS_@5b95b23d]

 (fn1 '(1 + 2))
 ; a> 1
 ; b> 2
 ; op> +

所以在第一种情况下,括号计算 clojure.core 的函数 PLUS,它给出预期的 (+ 1 2) => 3。

在第二种情况下,您可以将要评估的表单序列想象成一个隐式序列,因此对其调用 eval 将给出: (评估('+ 1 2)) ; 2个 这只是对序列中每个形式求值的结果,因此只有 returns 最后一个值,这里是 2。 你可以把它想象成: (做 '+ 1个 2)

正如评论中所暗示的,do is not call。

'+ 是实现 IFn 的 clojure.lang.Symbol。因此,当调用 eval 时,它将调用 java 函数 invoke,带有两个参数 1 和 2.

 (invoke 1 2) ; is called on +'

查看 Symbol 的 2 个参数调用方法:

https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Symbol.java#L129

我们注意到它会在第一个参数中搜索符号+',这应该是一个地图。

(def a {'+ 0})
(apply '+ [a 2])
; 0

当找不到item(此处为符号)时,invoke函数returns默认值,invoke函数的第二个参数:

(def b { })
(apply '+ [b 2])
; symbol + is not found in b so returning 2
; 2

您是正确的,单引号 '(相当于函数 quote)将 return 符号列表,抑制计算。但是,由于符号不能以数字开头,因此 PersistentList '(1 + 2) 将包含 java.lang.Longclojure.lang.Symboljava.lang.Long.

当您调用 Symbol + 给出的函数时(这是可能的,因为 Symbol 扩展了 AFn,后者本身实现了 IFnand overrides the invoke function),这将被执行:

public Object invoke(Object obj, Object notFound) {
    return RT.get(obj, this, notFound);
}

因为你执行了(op a b),或('+ 1 2)obj1notFound2this 是调用 clojure.lang.RTget 方法中的 Symbol +,其中 looks like this:

static public Object get(Object coll, Object key, Object notFound){
    if(coll instanceof ILookup)
        return ((ILookup) coll).valAt(key, notFound);
    return getFrom(coll, key, notFound);
}

同样,collLong1,key是Symbol+,notFound是Long2.

因此,调用函数时,会在集合中查找它。由于 1 不是集合,值 notFound2 正在 returned。

作为 ('+ 5 10) 示例的可视化,在调试器中: