defn, анонимные fn и форма #()
Учимся определять функции — главный строительный блок любой программы на Clojure.
Функция в Clojure — значение первого класса: её можно передавать в другие функции, возвращать и хранить в переменных, как любое число или строку.
Именованные функции через defn
Самый частый способ — defn: имя, вектор параметров, тело. Последнее выражение тела — возвращаемое значение (никакого return не нужно).
(defn квадрат [x]
(* x x))
(квадрат 5) ; => 25
(defn сумма [a b]
(+ a b))
(сумма 3 4) ; => 7Вывод:
25 7
Анонимные функции через fn
Иногда функция нужна на месте, без имени — например, чтобы передать её в map. Для этого есть fn:
((fn [x] (* x 10)) 4) ; => 40
; обычно анонимную функцию передают другой функции
(map (fn [x] (+ x 100)) [1 2 3]) ; => (101 102 103)Вывод:
40 (101 102 103)
Краткая форма #()
Для коротких анонимных функций есть ещё компактнее запись — #(...). Внутри % означает первый аргумент, %1, %2 — первый, второй и т.д.
; #(* % %) то же, что (fn [x] (* x x))
(map #(* % %) [1 2 3 4]) ; => (1 4 9 16)
; два аргумента: %1 и %2
(#(+ %1 %2) 10 20) ; => 30
; сравнение: оставить элементы меньше 5
(filter #(< % 5) [3 7 2 9 4]) ; => (3 2 4)Вывод:
(1 4 9 16) 30 (3 2 4)
Несколько арностей
Одна функция может принимать разное число аргументов — это называется арностью:
(defn привет
([] (привет "мир"))
([имя] (str "Привет, " имя)))
(привет) ; => "Привет, мир"
(привет "Аня") ; => "Привет, Аня"Вывод:
"Привет, мир" "Привет, Аня"
Как работает под капотом
Каждая функция Clojure компилируется в отдельный класс JVM, реализующий интерфейс IFn с методом invoke. Вызов (f x) — это вызов invoke у объекта функции. Поэтому функции и есть полноценные объекты-значения: их можно хранить и передавать. Форма #(...) — синтаксический сахар, который reader разворачивает в обычный fn с автоматически сгенерированными параметрами.
Частые ошибки
- Вкладывать #() в #(). Внутри краткой формы нельзя вложить другую
#()— символы%запутаются. Используйтеfn. - Писать
return. Его нет: возвращается последнее выражение тела. - Забыть экранировать сравнения. В коде
(filter #(< % 5) ...)символ «меньше» — это оператор, аргумент подставляется через%.
Итоги
defnопределяет именованную функцию; возвращается последнее выражение тела.fn— анонимная функция;#(...)— её краткая форма с%,%1,%2.- Функция может иметь несколько арностей (наборов параметров).
- Функции — значения первого класса, их можно передавать и возвращать.