Abréviation de la fonction anonyme

85

Il y a quelque chose que je ne comprends pas à propos des fonctions anonymes utilisant la notation courte # (..)

Les travaux suivants:

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

Mais cela ne:

REPL>  (#(%) "Eh")

Cela marche:

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

Ce que je ne comprends pas, c'est pourquoi (# (%) "Eh") ne fonctionne pas et en même temps je n'ai pas besoin d'utiliser str in ((fn [s] s) "Eh")

Ce sont toutes deux des fonctions anonymes et elles prennent toutes les deux, ici, un paramètre. Pourquoi la notation abrégée a-t-elle besoin d'une fonction alors que l'autre notation n'en a pas?

Cédric Martin
la source

Réponses:

126
#(...)

est un raccourci pour

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

(où le nombre d'argN dépend du nombre de% N que vous avez dans le corps). Alors quand vous écrivez:

#(%)

il est traduit par:

(fn [arg1] (arg1))

Notez que ceci est différent de votre première fonction anonyme, qui est comme:

(fn [arg1] arg1)

Votre version renvoie arg1 en tant que valeur, la version qui provient de l'expansion du raccourci tente de l'appeler comme une fonction. Vous obtenez une erreur car une chaîne n'est pas une fonction valide.

Étant donné que le raccourci fournit un ensemble de parenthèses autour du corps, il ne peut être utilisé que pour exécuter un seul appel de fonction ou un formulaire spécial.

Barmar
la source
64

Comme les autres réponses l'ont déjà très bien souligné, le que #(%)vous avez posté s'étend en fait à quelque chose comme (fn [arg1] (arg1)), qui n'est pas du tout la même chose que (fn [arg1] arg1).

@John Flatness a souligné que vous pouvez simplement utiliser identity, mais si vous cherchez un moyen d'écrire identityà l'aide de la #(...)macro de répartition, vous pouvez le faire comme ceci:

#(-> %)

En combinant la #(...)macro de répartition avec la ->macro de threading, elle est étendue à quelque chose comme (fn [arg1] (-> arg1)), qui se développe à nouveau (fn [arg1] arg1), ce qui est juste ce que vous vouliez. Je trouve également le combo ->et #(...)macro utile pour écrire des fonctions simples qui renvoient des vecteurs, par exemple:

#(-> [%2 %1])
DaoWen
la source
20

Lorsque vous utilisez #(...), vous pouvez imaginer que vous écrivez à la place (fn [args] (...)), y compris les parenthèses que vous avez commencées juste après la livre.

Ainsi, votre exemple non fonctionnel se convertit en:

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

ce qui ne fonctionne évidemment pas parce que vous essayez d' appeler la chaîne "Eh". Votre exemple avec strfonctionne parce que maintenant votre fonction est à la (str s)place de (s).(identity s)serait l'analogue le plus proche de votre premier exemple, car il ne contraindra pas à str.

Cela a du sens si vous y réfléchissez, car à part cet exemple totalement minimal, chaque fonction anonyme va appeler quelque chose , donc ce serait un peu stupide d'exiger un autre ensemble imbriqué de parenthèses pour effectuer un appel.

John Flatness
la source