85

关于使用简写 #(..) 的匿名函数,我有些不理解

以下作品:

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

但这不会:

REPL>  (#(%) "Eh")

这有效:

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

我不明白的是为什么(#(%) "Eh")不起作用,同时我不需要在((fn [s] s) "Eh")中使用str

它们都是匿名函数,并且都采用一个参数。为什么速记符号需要一个函数而其他符号不需要?

4

4 回答 4

126
#(...)

是简写

(fn [arg1 arg2 ...] (...))

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

#(%)

它被翻译成:

(fn [arg1] (arg1))

请注意,这与您的第一个匿名函数不同,它类似于:

(fn [arg1] arg1)

您的版本将 arg1 作为值返回,扩展速记的版本尝试将其作为函数调用。由于字符串不是有效的函数,您会收到错误消息。

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

于 2012-11-03T01:33:10.913 回答
65

正如其他答案已经很好地指出的那样,#(%)您发布的内容实际上扩展为类似于(fn [arg1] (arg1)),这与(fn [arg1] arg1).

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

#(-> %)

通过将#(...)dispatch 宏与->threading 宏结合起来,它会扩展为类似 的东西(fn [arg1] (-> arg1)),然后再扩展为(fn [arg1] arg1),这正是您想要的。我还发现->and#(...)宏组合有助于编写返回向量的简单函数,例如:

#(-> [%2 %1])
于 2012-11-03T05:30:46.007 回答
21

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

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

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

这显然不起作用,因为您试图调用字符串“Eh”。您的示例str有效,因为现在您的功能是(str s)而不是(s). (identity s)将更接近于您的第一个示例,因为它不会强制 str.

如果您考虑一下,这是有道理的,因为除了这个完全最小的示例之外,每个匿名函数都会调用something,因此需要另一个嵌套的括号集来实际进行调用有点愚蠢。

于 2012-11-03T01:33:01.710 回答
0

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

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

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

您也可以macroexpand完全调用。

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

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

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

资源:

于 2021-04-30T15:20:19.280 回答