解构列表中的函数
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.Long
、clojure.lang.Symbol
和 java.lang.Long
.
当您调用 Symbol
+
给出的函数时(这是可能的,因为 Symbol
扩展了 AFn
,后者本身实现了 IFn
,and overrides the invoke
function),这将被执行:
public Object invoke(Object obj, Object notFound) {
return RT.get(obj, this, notFound);
}
因为你执行了(op a b)
,或('+ 1 2)
,obj
是1
,notFound
是2
,this
是调用 clojure.lang.RT
的 get
方法中的 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);
}
同样,coll
是Long
1,key是Symbol
+
,notFound是Long
2.
因此,调用函数时,会在集合中查找它。由于 1
不是集合,值 notFound
或 2
正在 returned。
作为 ('+ 5 10)
示例的可视化,在调试器中:
我在 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。
正如评论中所暗示的,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.Long
、clojure.lang.Symbol
和 java.lang.Long
.
当您调用 Symbol
+
给出的函数时(这是可能的,因为 Symbol
扩展了 AFn
,后者本身实现了 IFn
,and overrides the invoke
function),这将被执行:
public Object invoke(Object obj, Object notFound) {
return RT.get(obj, this, notFound);
}
因为你执行了(op a b)
,或('+ 1 2)
,obj
是1
,notFound
是2
,this
是调用 clojure.lang.RT
的 get
方法中的 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);
}
同样,coll
是Long
1,key是Symbol
+
,notFound是Long
2.
因此,调用函数时,会在集合中查找它。由于 1
不是集合,值 notFound
或 2
正在 returned。
作为 ('+ 5 10)
示例的可视化,在调试器中: