匿名函数速记


85

对于使用缩写符号#(..)的匿名函数,我有些不了解。

以下作品:

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

但这不是:

REPL>  (#(%) "Eh")

这有效:

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

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

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

Answers:


126
#(...)

是的简写

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

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

#(%)

它被翻译为:

(fn [arg1] (arg1))

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

(fn [arg1] arg1)

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

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


64

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

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

#(-> %)

通过将#(...)调度宏与->线程宏结合使用,它可以扩展为(fn [arg1] (-> arg1)),然后再次扩展为(fn [arg1] arg1),这正是您想要的。我还发现->#(...)宏组合对于编写返回向量的简单函数很有帮助,例如:

#(-> [%2 %1])

20

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

因此,您的无效示例将转换为:

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

这显然不起作用,因为您正在尝试将字符串称为“ Eh”。你有例子str的作品,因为现在你的功能是(str s)代替(s)(identity s)将与您的第一个示例更接近,因为它不会强制转换为str。

如果考虑一下,这是有道理的,因为除了这个完全简单的示例之外,每个匿名函数都将调用某事,因此,需要另一套嵌套的paren进行实际调用会有些愚蠢。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.