Map (программирование)

Перейти к навигацииПерейти к поиску

map — функция высшего порядка, используемая во многих языках программирования, которая применяет какую-либо функцию к каждому элементу списка своих аргументов, выдавая список результатов как возвращаемое значение. При рассмотрении в функциональной форме она часто называется «применить-ко-всем».

Например, если определить функцию square следующим образом:

square x = x * x

то вызов map square [1,2,3,4,5] вернёт список [1,4,9,16,25], так как map применит функцию square к каждому элементу, собирая результаты в том же порядке.

Сравнение языков

Функция map произошла из языков функционального программирования, но поддерживается (или определена) во многих процедурных, объектно-ориентированных и мультипарадигменных языках, например: в стандартной библиотеке шаблонов C++ она называется transform, в C# (3.0) она представляется методом Select. Функция также часто используется в высокоуровневых языках, таких как Perl, Python и Ruby; во всех трёх языках функция называется map. В Ruby для map также существует псевдоним collect. В Common Lisp существует целое семейство функций, подобных map, например: mapcar, соответствующее описанному здесь поведению (суффикс car означает доступ к первому элементу списка). Также существуют языки с синтаксическими конструкциями, предоставляющими функциональность, аналогичную функции map.

Иногда встречается обобщённая версия map, принимающая функцию двух аргументов, 2 списка и применяющая её к соответствующим элементам списков. В некоторых языках для них заданы специальные имена вроде map2 или zipWith. Функция map с двумя или более списками порождает проблему работы со списками различной длины. Различные языки ведут себя по-разному: некоторые выбрасывают исключение, другие останавливаются по достижению конца короткого списка и игнорируют остальные элементы более длинных списков, третьи идут до самого длинного, возвращая некоторое специальное значение для списков, чьи значения уже закончились.

В языках, поддерживающих функции первого класса, map можно использовать с каррированием для получения функции, проводящей заранее заданное преобразование над списком. Например, map square в языке Haskell — функция, возвращающая список, каждый элемент которого равен соответствующему элементу списка-аргумента, возведённому в квадрат.

map в различных языках

Map в различных языках
ЯзыкMapMap 2 списковMap n списковПримечанияПоведение при списках различной длины
Haskellmap func listzipWith func list1 list2zipWithn func list1 list2n соответствует числу списков; определено вплоть до zipWith7останавливается после конца самого короткого списка
HaxeLambda.map(iterable, func)
Jfunc listlist func listfunc/ list1 , list2 , list3 ,: list4Возможности обработки массивов в языке позволяют неявно вызывать такие функции, как map Списки должны быть одинаковой длины
(length error if lists not equal)
OCamlList.map func list
Array.map func array
List.map2 func list1 list2выбрасывает исключение Invalid_argument
Standard MLmap func listListPair.map func (list1, list2)
ListPair.mapEq func (list1, list2)
Для map двух списков, func получает элементы в виде кортежа. ListPair.map останавливается по достижению конца самого короткого списка, ListPair.mapEq выбрасывает исключение UnequalLengths
Pythonmap(func, list) map(func, list1, list2) map(func, list1, list2, …) zip() and map() (версии 3.x) останавливаются по достижению конца самого короткого списка, map() (2.x) и itertools.zip_longest() (3.x) расширяют короткие списки значениями None
Rubyenum.collect {block}
enum.map {block}
enum1.zip(enum2).map {block} enum1.zip(enum2, …).map {block}
[enum1, enum2, …].transpose.map {block}
enum — это перечислениеостанавливается по достижению конца списка, на котором функция вызвана (первый список); если какой-либо другой список короче, он расширяется значениями nil
C++std::transform(begin, end, result, func) std::transform(begin1, end1, begin2, result, func) в заголовке <algorithm>
begin, end, & result итераторы
результат будет записан в начало result
Perlmap block list
map expr, list
В block или expr специальное значение $_ содержит каждое значение из списка. N/A
C# 3.0 ienum.Select(func)
C# 4.0 ienum.Select(func) ienum1.Zip(ienum2, func) останавливается по достижению конца самого короткого списка
JavaScript 1.6 array.map(func) - - map представлен только как метод массива, поэтому возможно применение только к одному списку (массиву)
Common Lisp(mapcar func list) (mapcar func list1 list2) (mapcar func list1 list2 …) Останавливается по достижению конца самого короткого списка
Scheme, Clojure(map func list) (map func list1 list2) (map func list1 list2 …) Останавливается по достижению конца самого короткого списка
SmalltalkaCollection collect: aBlockaCollection1 with: aCollection2 collect: aBlockПадает
Erlanglists: map(Fun, List) lists: zipwith(Fun, List1, List2) также доступен zipwith3Списки должны быть одинаковой длины
PHParray_map(callback, array) array_map(callback, array1,array2) array_map(callback, array1,array2, …) Число аргументов callback
обязано совпадать с числом массивов.
расширят короткие списки значениями NULL
Mathematicafunc /@ list
Map[func, list]
MapThread[func, {list1, list2}] MapThread[func, {list1, list2, …}] Списки должны быть одинаковой длины
MATLABarrayfun(func, list) arrayfun(func, list1, list2) arrayfun(func, list1, …, listn) cellfun для cell-списков
Maximamap(f, expr1, …, exprn)
maplist(f, expr1, …, exprn)
S/Rlapply(list,func) mapply(func,list1,list2) mapply(func,list1,list2,…) Короткие списки циклически повторяются
Scalalist.map(func) (list1, list2).zipped.map(func) (list1, list2,"list3").zipped.map(func) не более 3 списков. останавливается по достижению конца самого короткого
Java 8list.stream().map(func) нет функций первого класса; используется интерфейс java.lang.Function<T, R>

Оптимизации

Математическая основа операции map позволяет проводить множество оптимизаций. (map f . map g) xs (где «.» — оператор композиции функций) эквивалентно map (f . g) xs ; то есть: . Эта оптимизация избавляет от необходимости в двукратном вызове map за счёт совмещения применения функций f и g[1].

Функцию map можно определить, используя свёртку списка. В частности, id xs = map id xs = foldr (:) [] xs = xs. Сочетание же fold и map можно оптимизировать: foldr f z . map g эквивалентно foldr (f . g) z, таким образом map g xs = (id . map g) xs = foldr ((:) . g) [] xs. Такая реализация map при использовании с односвязными списками в не-ленивых языках не поддаётся оптимизации хвостовой рекурсии напрямую (хотя подлежит оптимизации «по модулю cons»[англ.]), поэтому она может привести к переполнению стека в случае применения к большим спискам. Во многих языках существует альтернативная «инвертированная функция map», эквивалентная функции map в случае инвертированного списка, но зато с возможностью оптимизации хвостовой рекурсии. Реализация, использующая левую свёртку:

 revMap f xs = foldl (\ys x -> f x : ys) [] xs

Примечания

  1. «Map fusion: Making Haskell 225 % faster». Дата обращения: 17 июля 2011. Архивировано 6 августа 2013 года.

См. также