关于使用简写 #(..) 的匿名函数,我有些不理解
以下作品:
REPL> ((fn [s] s) "Eh")
"Eh"
但这不会:
REPL> (#(%) "Eh")
这有效:
REPL> (#(str %) "Eh")
"Eh"
我不明白的是为什么(#(%) "Eh")不起作用,同时我不需要在((fn [s] s) "Eh")中使用str
它们都是匿名函数,并且都采用一个参数。为什么速记符号需要一个函数而其他符号不需要?
关于使用简写 #(..) 的匿名函数,我有些不理解
以下作品:
REPL> ((fn [s] s) "Eh")
"Eh"
但这不会:
REPL> (#(%) "Eh")
这有效:
REPL> (#(str %) "Eh")
"Eh"
我不明白的是为什么(#(%) "Eh")不起作用,同时我不需要在((fn [s] s) "Eh")中使用str
它们都是匿名函数,并且都采用一个参数。为什么速记符号需要一个函数而其他符号不需要?
#(...)
是简写
(fn [arg1 arg2 ...] (...))
(其中 argN 的数量取决于您体内有多少 %N)。所以当你写:
#(%)
它被翻译成:
(fn [arg1] (arg1))
请注意,这与您的第一个匿名函数不同,它类似于:
(fn [arg1] arg1)
您的版本将 arg1 作为值返回,扩展速记的版本尝试将其作为函数调用。由于字符串不是有效的函数,您会收到错误消息。
由于简写在主体周围提供了一组括号,因此它只能用于执行单个函数调用或特殊形式。
正如其他答案已经很好地指出的那样,#(%)
您发布的内容实际上扩展为类似于(fn [arg1] (arg1))
,这与(fn [arg1] arg1)
.
@John Flatness 指出您可以只使用identity
,但如果您正在寻找一种identity
使用#(...)
调度宏编写的方法,您可以这样做:
#(-> %)
通过将#(...)
dispatch 宏与->
threading 宏结合起来,它会扩展为类似 的东西(fn [arg1] (-> arg1))
,然后再扩展为(fn [arg1] arg1)
,这正是您想要的。我还发现->
and#(...)
宏组合有助于编写返回向量的简单函数,例如:
#(-> [%2 %1])
当你使用 时#(...)
,你可以想象你在写(fn [args] (...))
,包括你在英镑之后开始的括号。
因此,您的非工作示例转换为:
((fn [s] (s)) "Eh")
这显然不起作用,因为您试图调用字符串“Eh”。您的示例str
有效,因为现在您的功能是(str s)
而不是(s)
. (identity s)
将更接近于您的第一个示例,因为它不会强制 str.
如果您考虑一下,这是有道理的,因为除了这个完全最小的示例之外,每个匿名函数都会调用something,因此需要另一个嵌套的括号集来实际进行调用有点愚蠢。
如果您不确定匿名函数将转换为什么,您可以使用该macroexpand
过程来获取表示。请记住在将表达式传递给macroexpand 之前引用它。在这种情况下,我们可以这样做:
(macroexpand '#(%))
# => (fn* [p1__281#] (p1__281#))
这可能会打印出p1__281#
代表变量的不同名称%
。
您也可以macroexpand
完全调用。
(macroexpand '(#(%) "Eh"))
# => ((fn* [p1__331#] (p1__331#)) "Eh")
通过用短名称替换神秘的变量名称,转换为更易于阅读。我们得到了接受的答案所报告的内容。
# => ((fn* [s] (s)) "Eh")
资源: