Clojure: анонимная функция

В Clojure, когда и почему вы хотите использовать анонимную функцию? Например.

((fn add-five [x] (+ x 5)) 3))

В ClojureDocs, один комментарий к примеру говорит, что он полезен в трассировке стека. Можно ли привести пример этого?

Ответ 1

Существует две причины назвать анонимные функции (или по крайней мере две причины, по которым я это сделал). Во-первых, если дать ему имя, то он скажет более позднему читателю (возможно, самому себе через 6 месяцев), что, как предполагается, должно выполнять анонимная функция.

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

user=> (filter (fn [x] (/ 100 x)) [100 50 0])
ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:158)
user=> (pst *e)
ArithmeticException Divide by zero
    clojure.lang.Numbers.divide (Numbers.java:158)
    clojure.lang.Numbers.divide (Numbers.java:3784)
    user/eval8/fn--9 (NO_SOURCE_FILE:3)
    clojure.core/filter/fn--6908 (core.clj:2790)
    ...
nil

user=> (filter (fn hundred-div [x] (/ 100 x)) [100 50 0])
ArithmeticException Divide by zero  clojure.lang.Numbers.divide (Numbers.java:158)
user=> (pst *e)
ArithmeticException Divide by zero
    clojure.lang.Numbers.divide (Numbers.java:158)
    clojure.lang.Numbers.divide (Numbers.java:3784)
    user/eval14/hundred-div--15 (NO_SOURCE_FILE:5)        ;; <---
    clojure.core/filter/fn--6908 (core.clj:2790)
    ...

Ответ 2

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

Например:

(fn factorial[n]
  (if (<= n 1)
    1
    (* n  (factorial (- n 1)))))

Хотя рекурсия, подобная этой в Clojure, немного опасна, поскольку она потенциально может привести к переполнению стека.

Ответ 3

Именованные анонимные функции могут быть полезны, когда они ссылаются на себя, а также печатаются с их именем:

user=> ((fn [] (throw (Exception. "unnamed"))))

Exception unnamed  user/eval805/fn--806 (NO_SOURCE_FILE:1)
user=> ((fn myfn [] (throw (Exception. "named"))))

Exception named  user/eval809/myfn--810 (NO_SOURCE_FILE:1)