Сокращение анонимной функции

85

Я кое-что не понимаю в анонимных функциях, использующих короткую запись # (..)

Следующие работы:

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

Но это не так:

REPL>  (#(%) "Eh")

Это работает:

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

Я не понимаю, почему (# (%) "Eh") не работает, и в то же время мне не нужно использовать str в ((fn [s] s) "Eh")

Это обе анонимные функции, и обе они принимают один параметр. Почему для сокращенной записи нужна функция, а для другой - нет?

Седрик Мартин
источник

Ответы:

126
#(...)

сокращение для

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

(где количество argN зависит от того, сколько% N у вас в теле). Итак, когда вы пишете:

#(%)

это переведено на:

(fn [arg1] (arg1))

Обратите внимание, что это отличается от вашей первой анонимной функции, которая выглядит так:

(fn [arg1] arg1)

Ваша версия возвращает arg1 как значение, версия, полученная в результате расширения сокращения, пытается вызвать его как функцию. Вы получаете сообщение об ошибке, потому что строка не является допустимой функцией.

Поскольку сокращение содержит набор круглых скобок вокруг тела, его можно использовать только для выполнения одного вызова функции или специальной формы.

Barmar
источник
64

Как уже было очень хорошо указано в других ответах, опубликованный #(%)вами фактически расширяется до чего-то вроде (fn [arg1] (arg1)), что совсем не то же самое, что (fn [arg1] arg1).

@John Flatness указал, что вы можете просто использовать identity, но если вы ищете способ писать identityс использованием #(...)макроса отправки, вы можете сделать это следующим образом:

#(-> %)

Комбинируя #(...)макрос диспетчеризации с макросом ->потоковой передачи, он расширяется до чего-то вроде (fn [arg1] (-> arg1)), которое снова расширяется до (fn [arg1] arg1), чего вы просто хотели. Я также считаю, что комбинация макросов ->и #(...)полезна для написания простых функций, возвращающих векторы, например:

#(-> [%2 %1])
ДаоВэнь
источник
20

Когда вы используете #(...), вы можете представить, что вместо этого пишете (fn [args] (...)), включая скобки, которые вы начали сразу после фунта.

Итак, ваш нерабочий пример преобразуется в:

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

что, очевидно, не работает, потому что вы пытаетесь вызвать строку «Eh». Ваш пример с strработает, потому что теперь ваша функция (str s)вместо (s).(identity s)будет более близким аналогом к вашему первому примеру, так как он не будет приводить к str.

Это имеет смысл, если вы подумаете об этом, поскольку, кроме этого минимального примера, каждая анонимная функция будет что-то вызывать , поэтому было бы немного глупо требовать другой вложенный набор скобок для фактического вызова.

Джон Флэтнесс
источник