2012-11-03 112 views
76

有件事情我不明白如何使用短符号#(..)匿名函数速记

以下作品匿名函数:

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

但这并不:

REPL> (#(%) "Eh") 

这工作:

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

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

他们都是匿名函数,他们都采取,在这里,一个参数。为什么简写符号需要一个函数,而另一个符号不需要?

回答

115
#(...) 

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

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

#(%) 

它翻译成:

(fn [arg1] (arg1)) 

注意,这是你的第一个匿名函数,这就好比是不同的:

(fn [arg1] arg1) 

你的版本返回Arg1为一个值,扩展速记产生的版本会尝试将其称为函数。你得到一个错误,因为一个字符串不是一个有效的函数。

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

18

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

所以,你不工作的例子转换为:

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

这是因为你试图呼叫字符串“嗯”显然是行不通的。你的例子str的作品,因为现在你的功能是(str s)而不是(s)(identity s)会更接近你的第一个例子,因为它不会强迫str。

这是有道理的,如果你仔细想想,因为比这完全小例子,其他的,每一个匿名函数会调用东西,所以这会是一个有点傻,要求另一组嵌套括号的实际上使一个电话。

56

至于其他的答案已经非常漂亮指出的那样,你居然张贴#(%)扩展到像(fn [arg1] (arg1)),这一点也不一样(fn [arg1] arg1)

@约翰平整度指出,你可以使用identity,但如果你正在寻找一种方式使用#(...)调度宏写identity,你可以做这样的:

​​

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

#(-> [%2 %1])