Функции высшего порядка: map, filter, reduce

Осваиваем три главные функции высшего порядка, на которых держится обработка данных в Clojure.

Функция высшего порядка — функция, которая принимает другую функцию как аргумент или возвращает функцию.

map: преобразовать каждый элемент

map применяет функцию к каждому элементу коллекции и возвращает новую последовательность результатов. Цикл писать не нужно.

(map #(* % 2) [1 2 3 4])      ; => (2 4 6 8)
(map count ["abc" "de" "f"])  ; => (3 2 1)

; map по двум коллекциям сразу
(map + [1 2 3] [10 20 30])    ; => (11 22 33)

Вывод:

(2 4 6 8)
(3 2 1)
(11 22 33)

filter: отобрать подходящие

filter оставляет только те элементы, для которых функция-предикат возвращает истину:

(filter even? [1 2 3 4 5 6])   ; => (2 4 6)
(filter #(> % 3) [1 2 3 4 5])  ; => (4 5)

; противоположность - remove (убрать подходящие)
(remove even? [1 2 3 4])       ; => (1 3)

Вывод:

(2 4 6)
(4 5)
(1 3)

reduce: свернуть в одно значение

reduce «складывает» коллекцию в единственное значение, накапливая результат. Это самый мощный из трёх — через него выражаются и сумма, и максимум, и построение map.

; сумма всех чисел
(reduce + [1 2 3 4 5])         ; => 15

; с начальным значением 100
(reduce + 100 [1 2 3])         ; => 106

; найти максимум
(reduce max [3 7 2 9 4])       ; => 9

Вывод:

15
106
9

Комбинируем три кита

Сила в комбинации: отобрать, преобразовать, свернуть. Посчитаем сумму квадратов чётных чисел:

(reduce +
        (map #(* % %)
             (filter even? [1 2 3 4 5 6])))
; чётные: (2 4 6) -> квадраты: (4 16 36) -> сумма: 56

Вывод:

56

Как работает под капотом

map и filter возвращают ленивые последовательности — элементы вычисляются по запросу, а не сразу (об этом отдельный раздел). reduce же выполняется сразу и в одном проходе: он начинает с начального значения (или первого элемента) и последовательно применяет функцию к аккумулятору и очередному элементу. Именно поэтому через reduce можно выразить почти любую агрегацию.

Частые ошибки

  • Путать filter и map. filter отбирает (количество может уменьшиться), map преобразует (количество сохраняется).
  • Забыть начальное значение reduce на пустой коллекции. (reduce + []) вернёт 0, а (reduce max []) — ошибку без начального значения.
  • Ждать вектор от map. map возвращает ленивую последовательность; при необходимости оберните в vec.

Итоги

  • map преобразует каждый элемент, сохраняя их количество.
  • filter отбирает элементы по предикату; remove — противоположность.
  • reduce сворачивает коллекцию в одно значение, накапливая результат.
  • Комбинация filter → map → reduce заменяет большинство циклов.
Проверьте себя
1. Что делает функция map?
AОтбирает элементы по условию
BПрименяет функцию к каждому элементу, возвращая новую последовательность
CСворачивает коллекцию в одно значение
DСортирует коллекцию
2. Чему равно (reduce + [1 2 3 4 5])?
A5
B15
C[1 2 3 4 5]
D120
3. Что вернёт (filter even? [1 2 3 4 5 6])?
A(1 3 5)
B(2 4 6)
C(1 2 3 4 5 6)
D6