Mapa (funkce vyššího řádu) - Map (higher-order function)

V mnoha programovacích jazycích je mapa název funkce vyššího řádu, která aplikuje danou funkci na každý prvek funktoru , např. Seznam , vrací seznam výsledků ve stejném pořadí. Je - li zvažován ve funkční formě, je často nazýván aplikovatelný na všechny .

Koncept mapy není omezen na seznamy: funguje pro sekvenční kontejnery , kontejnery podobné stromům nebo dokonce abstraktní kontejnery, jako jsou futures a sliby .

Příklady: mapování seznamu

Předpokládejme, že máme seznam celých čísel [1, 2, 3, 4, 5]a chtěli bychom vypočítat druhou mocninu každého celého čísla. Za tímto účelem nejprve definujeme funkci pro squarejedno číslo (zde zobrazené v Haskellu ):

square x = x * x

Potom můžeme zavolat

>>> map square [1, 2, 3, 4, 5]

což poskytuje [1, 4, 9, 16, 25], což ukazuje, že mapprošel celým seznamem a použil funkci squarena každý prvek.

Vizuální příklad

Níže vidíte pohled na každý krok procesu mapování pro seznam celých čísel, X = [0, 5, 8, 3, 2, 1]která chceme mapovat do nového seznamu X'podle funkce  :

použití kroků zpracování mapové funkce
Zobrazení kroků zpracování při použití funkce mapy v seznamu

Soubor mapje poskytován jako součást základní předehry společnosti Haskell (tj. „Standardní knihovna“) a je implementován jako:

map :: (a -> b) -> [a] -> [b]
map _ []       = []
map f (x : xs) = f x : map f xs

Zobecnění

V Haskellu je polymorfní funkce map :: (a -> b) -> [a] -> [b] zobecněna na polytypovou funkci fmap :: Functor f => (a -> b) -> f a -> f b , která platí pro jakýkoli typ patřící do třídy typů . Functor

Konstruktor typu seznamů []lze definovat jako instanci třídy Functortypu pomocí mapfunkce z předchozího příkladu:

instance Functor [] where
  fmap = map

Mezi další příklady Functorinstancí patří stromy:

-- a simple binary tree
data Tree a = Leaf a | Fork (Tree a) (Tree a)

instance Functor Tree where  
  fmap f (Leaf x) = Leaf (f x)
  fmap f (Fork l r) = Fork (fmap f l) (fmap f r)

Mapování výnosů stromu:

>>> fmap square (Fork (Fork (Leaf 1) (Leaf 2)) (Fork (Leaf 3) (Leaf 4)))
Fork (Fork (Leaf 1) (Leaf 4)) (Fork (Leaf 9) (Leaf 16))

Pro každý případ třídy Functortypu fmapje smluvně povinen dodržovat zákony funktorů:

fmap id       id              -- identity law
fmap (f . g)  fmap f . fmap g -- composition law

kde .označuje funkční složení v Haskellu.

Mimo jiné to umožňuje definovat elementární operace pro různé druhy kolekcí .

Navíc, pokud F a G jsou dva funktory, přirozená transformace je funkcí polymorfního typu, který respektuje fmap :

pro jakoukoli funkci .

Pokud je funkce h definována parametrickým polymorfismem jako v definici typu výše, je tato specifikace vždy splněna.

Optimalizace

Matematický základ map umožňuje řadu optimalizací . Zákon o složení zajišťuje, že obojí

  • (map f . map g) list a
  • map (f . g) list

vést ke stejnému výsledku; to je , . Druhá forma je však pro výpočet efektivnější než první forma, protože každá vyžaduje přestavbu celého seznamu od začátku. Proto se kompilátoři pokusí transformovat první formu na druhou; tento typ optimalizace je známý jako mapová fúze a je funkčním analogem smyčkové fúze . map

Mapové funkce mohou být a často jsou definovány jako záhyby, jako například foldr, což znamená, že lze provést fúzi skládání mapy : foldr f z . map gje ekvivalentní foldr (f . g) z.

Implementace mapy výše na jednotlivě propojených seznamech není rekurzivní , takže při volání s velkým seznamem může v zásobníku vytvořit mnoho snímků. Mnoho jazyků střídavě poskytuje funkci „zpětné mapy“, která je ekvivalentní obrácení mapovaného seznamu, ale je rekurzivní. Zde je implementace, která využívá funkci fold -left.

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

Protože obrácení jednotlivě propojeného seznamu je také rekurzivní ocas, reverzní a reverzní mapa mohou být složeny tak, aby prováděly normální mapu rekurzivním způsobem, ačkoli to vyžaduje provedení dvou průchodů přes seznam.

Porovnání jazyků

Funkce mapy pochází z funkčních programovacích jazyků.

Jazyk Lisp představil mapovou funkci nazvanou maplistv roce 1959, s mírně odlišnými verzemi se již objevily v roce 1958. Toto je původní definice pro maplistmapování funkce přes po sobě jdoucí odpočinkové seznamy:

maplist[x;f] = [null[x] -> NIL;T -> cons[f[x];maplist[cdr[x];f]]]

Funkce maplistje stále k dispozici v novějších lispech, jako je Common Lisp , i když by se upřednostňovaly funkce jako mapcarnebo obecnější map.

Vyrovnávání prvků seznamu pomocí maplistby bylo zapsáno v zápisu výrazu S takto:

(maplist (lambda (l) (sqr (car l))) '(1 2 3 4 5))

Pomocí funkce mapcarby výše uvedený příklad byl napsán takto:

(mapcar (function sqr) '(1 2 3 4 5))

Dnešní mapování funkce jsou podporovány (nebo může být definováno) v mnoha procesních , objektově orientované , a multi-paradigma jazycích také: V C ++ je standardní knihovny , se nazývá std::transform, v C # (3,0) je knihovna LINQ, že je poskytované jako metoda rozšíření nazvaná Select. Mapa je také často používanou operací v jazycích vysoké úrovně, jako je ColdFusion Markup Language (CFML), Perl , Python a Ruby ; operace se nazývá mapve všech čtyřech těchto jazycích. collectAlias mapje také v Ruby (od Smalltalk ). Common Lisp poskytuje řadu funkcí podobných mapám; nazývá se chování odpovídající zde popsanému chování mapcar( -carindikující přístup pomocí operace CAR ). Existují také jazyky se syntaktickými konstrukty poskytující stejné funkce jako funkce mapy.

Mapa je někdy zobecněna tak, aby přijímala dyadické (2 argumenty) funkce, které mohou použít uživatelem dodanou funkci na odpovídající prvky ze dvou seznamů. Některé jazyky k tomu používají speciální názvy, například map2 nebo zipWith . Jazyky, které používají explicitní variadic funkce může mít verze mapy s variabilním arity v rámci podpůrných jednotek s proměnlivým arita funkcí. Mapa se 2 nebo více seznamy narazí na problém zpracování, pokud jsou seznamy různé délky. Různé jazyky se v tomto liší. Někteří vyvolávají výjimku. Někteří se zastaví po délce nejkratšího seznamu a ignorují další položky v ostatních seznamech. Někteří pokračují v délce nejdelšího seznamu a u seznamů, které již skončily, předejte funkci zástupného symbolu nějakou funkci, která žádnou hodnotu neuvádí.

V jazycích, které podporují prvotřídní funkce a currying , maplze částečně aplikovat na zvednutí funkce, která funguje pouze na jedné hodnotě, na elementární ekvivalent, který funguje na celém kontejneru; například map squareje funkce Haskell, která čtverce každý prvek seznamu.

Mapa v různých jazycích
Jazyk Mapa Seznam 2 na mapě Mapa n seznamů Poznámky Zpracování seznamů různých délek
APL func list list1 func list2 func/ list1 list2 list3 list4 Díky schopnostem zpracování pole APL jsou operace jako mapa implicitní délka chyby, pokud délka seznamu není stejná nebo 1
Lisp (mapcar func list) (mapcar func list1 list2) (mapcar func list1 list2 ...) zastaví po délce nejkratšího seznamu
C ++ std::transform(begin, end, result, func) std::transform(begin1, end1, begin2, result, func) v záhlaví <algoritmus>
začátek , konec a výsledek jsou iterátory,
výsledek je zapsán od výsledku
C# ienum.Select(func)
nebo doložka
select
ienum1.Zip(ienum2, func) Selectje metoda rozšíření
ienum je IEnumerable
Zipje zaveden v .NET 4.0
Podobně ve všech jazycích .NET
zastaví po skončení nejkratšího seznamu
CFML obj.map(func) Kde objje pole nebo struktura. funcpřijímá jako argumenty hodnotu každé položky, její index nebo klíč a odkaz na původní objekt.
Clojure (map func list) (map func list1 list2) (map func list1 list2 ...) zastaví po skončení nejkratšího seznamu
D list.map!func zip(list1, list2).map!func zip(list1, list2, ...).map!func Určeno pro zip pomocí StoppingPolicy: nejkratší, nejdelší nebo requireSameLength
Erlang lists:map(Fun, List) lists:zipwith(Fun, List1, List2) zipwith3 také dostupný Seznamy musí mít stejnou délku
Elixír Enum.map(list, fun) Enum.zip(list1, list2) |> Enum.map(fun) List.zip([list1, list2, ...]) |> Enum.map(fun) zastaví po skončení nejkratšího seznamu
F# List.map func list List.map2 func list1 list2 Funkce existují pro jiné typy ( Sekvence a Pole ) Vyvolává výjimku
Báječný list.collect(func) [list1 list2].transpose().collect(func) [list1 list2 ...].transpose().collect(func)
Haskell map func list zipWith func list1 list2 zipWithn func list1 list2 ... nodpovídá počtu seznamů; předdefinováno až nazipWith7 zastaví po skončení nejkratšího seznamu
Haxe array.map(func)
list.map(func)
Lambda.map(iterable, func)
J. func list list1 func list2 func/ list1, list2, list3 ,: list4 Schopnosti zpracování pole J dělají operace jako mapa implicitní délka chyby, pokud se délka seznamu nerovná
Java 8+ stream.map(func)
JavaScript 1.6
ECMAScript 5
array#map(func) List1.map(function (elem1, i) {
return func(elem1, List2[i]); })
List1.map(function (elem1, i) {
return func(elem1, List2[i], List3[i], ...); })
Array#map předává 3 argumenty func : element, index elementu a pole. Nepoužité argumenty lze vynechat. Zastaví na konci List1 a v případě potřeby rozšíří kratší pole o nedefinované položky.
Julie map(func, list) map(func, list1, list2) map(func, list1, list2, ..., listN) CHYBA: Neshoda dimenzí
Logtalk map(Closure, List) map(Closure, List1, List2) map(Closure, List1, List2, List3, ...) (up to seven lists) Musí být zadán pouze argument Uzavření . Selhání
Mathematica func /@ list
Map[func, list]
MapThread[func, {list1, list2}] MapThread[func, {list1, list2, ...}] Seznamy musí mít stejnou délku
Maxima map(f, expr1, ..., exprn)
maplist(f, expr1, ..., exprn)
mapa vrací výraz, jehož vedoucí operátor je stejný jako u výrazů;
maplist vrátí seznam
OCaml List.map func list
Array.map func array
List.map2 func list1 list2 vyvolá výjimku Invalid_argument
PARI/GP apply(func, list) N/A
Perl map block list
map expr, list
V bloku nebo expr speciální proměnná $ _ uchovává postupně každou hodnotu ze seznamu. Pomocník List::MoreUtils::each_arraykombinuje více než jeden seznam, dokud není vyčerpán ten nejdelší, přičemž ostatní naplníundef.
PHP array_map(callable, array) array_map(callable, array1,array2) array_map(callable, array1,array2, ...) Počet parametrů pro splatných na požádání
by měl odpovídat počtu polí.
rozšiřuje kratší seznamy o NULL položky
Prolog maplist(Cont, List1, List2). maplist(Cont, List1, List2, List3). maplist(Cont, List1, ...). Argumenty seznamu jsou vstupní, výstupní nebo obojí. Subsumes also zipWith, unzip, all Tiché selhání (nejedná se o chybu)
Krajta map(func, list) map(func, list1, list2) map(func, list1, list2, ...) Vrátí seznam v Pythonu 2 a iterátor v Pythonu 3. zip()a map()(3.x) se zastaví po skončení nejkratšího seznamu, zatímco map()(2.x) a itertools.zip_longest()(3.x) rozšíří kratší seznamy o Nonepoložky
Rubín enum.collect {block}
enum.map {block}
enum1.zip(enum2).map {block} enum1.zip(enum2, ...).map {block}
[enum1, enum2, ...].transpose.map {block}
enum is an Enumeration zastaví se na konci objektu, na který je vyvolán (první seznam); pokud je jakýkoli jiný seznam kratší, je rozšířen o nulové položky
Rez list1.into_iter().map(func) list1.into_iter().zip(list2).map(func) že Iterator::mapi Iterator::zipmetody jak převzít vlastnictví původního iterátoru a vrátí nový; Iterator::zipmetoda interně volá IntoIterator::into_itermetodu nalist2 zastaví po skončení kratšího seznamu
S - R. lapply(list, func) mapply(func, list1, list2) mapply(func, list1, list2, ...) Kratší seznamy se cyklují
Scala list.map(func) (list1, list2).zipped.map(func) (list1, list2, list3).zipped.map(func) poznámka: více než 3 není možné. zastaví po skončení kratšího seznamu
Schéma (včetně lsti a rakety ) (map func list) (map func list1 list2) (map func list1 list2 ...) seznamy musí mít všechny stejnou délku (SRFI-1 se rozšiřuje o seznamy různé délky)
Pokec aCollection collect: aBlock aCollection1 with: aCollection2 collect: aBlock Selže
Standardní ML map func list ListPair.map func (list1, list2)
ListPair.mapEq func (list1, list2)
U mapy se 2 argumenty přebírá func argumenty v n-tici ListPair.mapzastaví po skončení nejkratšího seznamu, zatímco ListPair.mapEqvyvolá UnequalLengthsvýjimku
Rychlý sequence.map(func) zip(sequence1, sequence2).map(func) zastaví po skončení nejkratšího seznamu
XPath 3
XQuery 3
list ! block
for-each(list, func)
for-each-pair(list1, list2, func) V blockkontextu položka .obsahuje aktuální hodnotu zastaví po skončení nejkratšího seznamu

Viz také

Reference