对于使用短符号#(..)
的匿名函数,我有些不明白以下作品:
REPL> ((fn [s] s) "Eh")
"Eh"
但这不是:
REPL> (#(%) "Eh")
这有效:
REPL> (#(str %) "Eh")
"Eh"
我不明白为什么(#(%)“Eh”)不起作用,同时我不需要使用 str 在((fn [s] s)“Eh”)
它们都是匿名函数,它们都带有一个参数。为什么简写符号需要一个函数而另一个符号不需要?
答案 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")
资源: