使用线程宏时,命名函数和匿名函数之间的差异很大

时间:2014-10-05 17:39:14

标签: macros clojure anonymous-function

关于Clojure中的线程宏,我必须缺少一些东西。

我有一个地图,其值也是地图,我想在另一个查找的结果中查找。让地图变得简单{:a {:b 2}} - 首先我要查找密钥:a,然后查找{:b 2},然后查找b,结果为{ {1}}。第二次查找的关键字必须是函数的结果。

2

好的,让我们通过线程宏使其更具可读性。

((fn [x] (get x :b)) ({:a {:b 2} } :a ))
=> 2

即。将(-> {:a {:b 2} } :a (fn [x] (get x :b))) 作为函数应用于地图,然后应用另一个函数。嗯,这不起作用: :a

奇怪的是,如果将匿名函数提取到命名函数,那么它可以正常工作:

CompilerException java.lang.IllegalArgumentException: Parameter declaration :a should be a vector

甚至:

(defn f [x] (get x :b))
(-> {:a {:b 2} } :a f)
=> 2

为什么命名和匿名函数的工作方式有区别?

2 个答案:

答案 0 :(得分:4)

线程宏通过递归地将前一个表单作为第一个参数插入每个子表单来查看并更改系列中每个子表单,然后再计算该表单。

你从:

开始
(-> {:a {:b 2} } :a (fn [x] (get x :b)))

这变为:

(-> (:a {:a {:b 2}}) (fn [x] (get x :b)))

这变为:

(fn (:a {:b {:b 2}}) [x] (get x :b)))

这显然不是你想要的。

但是,让我们看看如果你在匿名函数周围添加额外的parens会发生什么:

(-> {:a {:b 2}} :a ((fn [x] (get x :b))))

(-> (:a {:a {:b 2}}) ((fn [x] (get x :b))))

(-> ((fn [x] (get x :b)) (:a {:a {:b 2}})))

((fn [x] (get x :b)) (:a {:a {:b 2}}))

->表单的最后一次递归宏展开时,我们现在留下了有效的代码,可以满足您的需要。

答案 1 :(得分:3)

为了补充noisesmith的响应,在这种特殊情况下,您不需要线程宏。从嵌套地图获取值的惯用方法是get-in。 e.g:

(get-in {:a {:b 2}} [:a :b])

=>

2