Функции высшего порядка: 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 заменяет большинство циклов.