匿名函数速记

关于使用短符号 # (. .)的匿名函数,我有一些不明白的地方

以下作品:

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

但事实并非如此:

REPL>  (#(%) "Eh")

这种方法是有效的:

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

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

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

29624 次浏览

When you use #(...), you can imagine you're instead writing (fn [args] (...)), including the parentheses you started right after the pound.

So, your non-working example converts to:

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

which obviously doesn't work because the you're trying to call the string "Eh". Your example with str works because now your function is (str s) instead of (s). (identity s) would be the closer analogue to your first example, since it won't coerce to str.

It makes sense if you think about it, since other than this totally minimal example, every anonymous function is going to call something, so it'd be a little foolish to require another nested set of parens to actually make a call.

#(...)

is shorthand for

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

(where the number of argN depends on how many %N you have in the body). So when you write:

#(%)

it's translated to:

(fn [arg1] (arg1))

Notice that this is different from your first anonymous function, which is like:

(fn [arg1] arg1)

Your version returns arg1 as a value, the version that comes from expanding the shorthand tries to call it as a function. You get an error because a string is not a valid function.

Since the shorthand supplies a set of parentheses around the body, it can only be used to execute a single function call or special form.

As the other answers have already very nicely pointed out, the #(%) you posted actually expands to something like (fn [arg1] (arg1)), which is not at all the same as (fn [arg1] arg1).

@John Flatness pointed out that you can just use identity, but if you're looking for a way to write identity using the #(...) dispatch macro, you can do it like this:

#(-> %)

By combining the #(...) dispatch macro with the -> threading macro it gets expanded to something like (fn [arg1] (-> arg1)), which expands again to (fn [arg1] arg1), which is just want you wanted. I also find the -> and #(...) macro combo helpful for writing simple functions that return vectors, e.g.:

#(-> [%2 %1])

If you're in doubt what your anonymous function gets converted to, you can use the macroexpand procedure to get the representation. Remember to quote your expression before passing it to macroexpand. In this case we could do:

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

This might print different names for p1__281# which are representations of the variables %.

You can also macroexpand the full invocation.

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

Converted to more human readable by replacing the cryptic variable names by short names. We get what the accepted answers have reported.

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

Resources: