41

我了解 Clojure 中的 `-> theading 宏将所有提供的函数应用于给定参数。但是,它似乎不适用于匿名函数。例如:

user> (-> 4 inc inc dec)
5

但:

user> (-> 4 #(+ % 1) #(- % 1) #(+ % 1))

返回错误:

clojure.lang.Symbol cannot be cast to clojure.lang.IPersistentVector
[Thrown class java.lang.ClassCastException]

如果有人知道解决方法会有所帮助。谢谢!

4

3 回答 3

52

您可以在 Clojure 宏中使用匿名函数。您遇到了问题,因为您缺少一些括号。:) 您的示例在下面进行了编辑。

(-> 4 (#(+ % 1)) (#(- % 1)) (#(+ % 1)))
于 2012-05-24T15:18:36.703 回答
32

(这是基于我在评论中发布的问题的答案)。

->宏接受每个参数,如有必要将其设为列表(将“原始”函数应用于无参数 - 转换为myfunc(myfunc),然后将第一个参数插入到->每个列表中作为第二个参数。

so(-> foo myfunc)变成(-> foo (myfunc)),(myfunc foo)大致上。

这在文档中->都有描述。

匿名函数的问题在于它们是由阅读器宏生成的,如此处所述(向下滚动)。这意味着#(...)将(正常宏扩展之前)转换为(fn [...] ...). 这很好,但至关重要的是,它已经是一个 list

所以宏认为匿名函数已经被应用,而实际上它正在遇到函数定义(两者都是列表)。并添加“额外”括号 - 如上所述在另一个答案中 - 将匿名函数应用于无参数。

这种不直观的行为的原因是宏使用的 dwim(do-what-i-mean,不是 dwim-witted,虽然......)启发式->,添加以允许您提供“裸”功能而不是要求通过将它们包含在列表中来将它们应用于无参数,这只是一种启发式方法——它只是测试一个列表——并且被阅读器宏创建的函数定义混淆了。

[在我的坏脾气看来,->实现得很差,应该拒绝所有“裸”功能,而只接受功能应用;然后它会显得更加一致。如果不是,那么至少文档可以更清晰,解释将事物放入列表背后的激励语义。]

于 2012-05-25T16:29:49.527 回答
4

您的具体情况可以简单地使用:

(-> 4 (+ 1) (- 1) (+ 1))

其中线程优先宏->负责将上一步的结果作为“当前”函数的第一个参数插入。

混淆源于这样一个事实,->即不是函数而是宏,并且在这种情况下,参数的处理方式非常不同,正如其他答案所解释的那样。

于 2016-01-24T18:41:53.133 回答