匿名功能简写

时间:2012-11-03 01:00:12

标签: function clojure anonymous-function

对于使用短符号#(..)

的匿名函数,我有些不明白

以下作品:

REPL>  ((fn [s] s) "Eh")
"Eh"

但这不是:

REPL>  (#(%) "Eh")

这有效:

REPL> (#(str %) "Eh")
"Eh"

我不明白为什么(#(%)“Eh”)不起作用,同时我不需要使用 str ((fn [s] s)“Eh”)

它们都是匿名函数,它们都带有一个参数。为什么简写符号需要一个函数而另一个符号不需要?

4 个答案:

答案 0 :(得分:123)

#(...)

的简写
(fn [arg1 arg2 ...] (...))

(其中argN的数量取决于你体内有多少%N)。所以当你写:

#(%)

它被翻译为:

(fn [arg1] (arg1))

请注意,这与您的第一个匿名函数不同,如:

(fn [arg1] arg1)

您的版本将arg1作为值返回,展开速记所产生的版本会尝试将其作为函数调用。您收到错误,因为字符串不是有效函数。

由于速记在身体周围提供一组括号,因此它只能用于执行单个函数调用或特殊形式。

答案 1 :(得分:62)

正如其他答案已经非常好地指出的那样,您发布的#(%)实际上会扩展为(fn [arg1] (arg1)),这与(fn [arg1] arg1)完全不同。

@John Flatness指出你可以使用identity,但是如果你正在寻找一种使用identity调度宏来编写#(...)的方法,你可以这样做这样:

#(-> %)

通过将#(...)调度宏与-> threading macro相结合,它会扩展为(fn [arg1] (-> arg1)),再次扩展为(fn [arg1] arg1),这只是您想要的。我还发现->#(...)宏组合有助于编写返回向量的简单函数,例如:

#(-> [%2 %1])

答案 2 :(得分:20)

当您使用#(...)时,您可以想象您正在编写(fn [args] (...))包括您在磅之后开始的括号。

因此,您的非工作示例将转换为:

((fn [s] (s)) "Eh")

这显然不起作用,因为你正试图调用字符串“Eh”。 str的示例有效,因为现在您的函数是(str s)而不是(s)(identity s)将与你的第一个例子更接近,因为它不会强迫str。

如果你考虑它是有道理的,因为除了这个完全最小的例子,每个匿名函数都会调用某些东西,所以要求另一个嵌套的集合是有点愚蠢的parens实际上打电话。

答案 3 :(得分:0)

如果您不确定您的匿名函数被转换成什么,您可以使用 macroexpand 过程来获取表示。请记住在将表达式传递给 macroexpand 之前引用它。在这种情况下,我们可以这样做:

(macroexpand '#(%))
# => (fn* [p1__281#] (p1__281#))

这可能会为 p1__281# 打印不同的名称,这些名称代表变量 %

您也可以macroexpand完整调用。

(macroexpand '(#(%) "Eh"))
# => ((fn* [p1__331#] (p1__331#)) "Eh")

通过用短名称替换神秘的变量名称,转换为更易读。我们得到了接受的答案所报告的内容。

# => ((fn* [s] (s)) "Eh")

资源: