Hartă (funcție de ordin superior) - Map (higher-order function)
În multe limbaje de programare , harta este numele unei funcții de ordin superior care aplică o funcție dată fiecărui element al unui functor , de exemplu o listă , returnând o listă de rezultate în aceeași ordine. Este adesea numit aplicabil la toate atunci când este considerat sub formă funcțională .
Conceptul de hartă nu se limitează la liste: funcționează pentru containere secvențiale , containere asemănătoare copacilor sau chiar containere abstracte, cum ar fi futures și promisiuni .
Exemple: maparea unei liste
Să presupunem că avem o listă de numere întregi [1, 2, 3, 4, 5]și am dori să calculăm pătratul fiecărui număr întreg. Pentru a face acest lucru, mai întâi definim o funcție într- squareun singur număr (prezentat aici în Haskell ):
square x = x * x
După aceea, putem suna
>>> map square [1, 2, 3, 4, 5]
care cedează [1, 4, 9, 16, 25], demonstrând că mapa trecut prin întreaga listă și a aplicat funcția squarefiecărui element.
Exemplu vizual
Mai jos, puteți vedea o vedere a fiecărui pas al procesului de mapare pentru o listă de numere întregi pe X = [0, 5, 8, 3, 2, 1]care dorim să le mapăm într-o nouă listă în X'funcție de funcție :
Este mapfurnizat ca parte a preludiului de bază al lui Haskell (adică „biblioteca standard”) și este implementat ca:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x : xs) = f x : map f xs
Generalizare
În Haskell, funcția polimorfă map :: (a -> b) -> [a] -> [b] este generalizată la o funcție politipică fmap :: Functor f => (a -> b) -> f a -> f b , care se aplică oricărui tip care aparține clasei de tip .
Functor
Constructorul de tip al listelor []poate fi definit ca o instanță a Functorclasei de tip folosind mapfuncția din exemplul anterior:
instance Functor [] where
fmap = map
Alte exemple de Functorcazuri includ copaci:
-- 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)
Cartarea peste un copac produce:
>>> fmap square (Fork (Fork (Leaf 1) (Leaf 2)) (Fork (Leaf 3) (Leaf 4)))
Fork (Fork (Leaf 1) (Leaf 4)) (Fork (Leaf 9) (Leaf 16))
Pentru fiecare caz din Functorclasa de tip, fmapeste obligat contractual să respecte legile funcționarului:
fmap id ≡ id -- identity law
fmap (f . g) ≡ fmap f . fmap g -- composition law
unde .denotă compoziția funcției în Haskell.
Printre alte utilizări, aceasta permite definirea operațiunilor elementare pentru diferite tipuri de colecții .
Mai mult, dacă F și G sunt doi functori, o transformare naturală este o funcție de tip polimorf care respectă fmap :
- pentru orice funcție .
Dacă funcția h este definită de polimorfism parametric ca în definiția de tip de mai sus, această specificație este întotdeauna îndeplinită.
Optimizări
Baza matematică a hărților permite o serie de optimizări . Legea compoziției asigură că ambele
-
(map f . map g) listși map (f . g) list
duce la același rezultat; adică . Cu toate acestea, a doua formă este mai eficientă de calculat decât prima formă, deoarece fiecare necesită reconstruirea unei liste întregi de la zero. Prin urmare, compilatoarele vor încerca să transforme prima formă în a doua; acest tip de optimizare este cunoscut sub numele de fuziune cu hărți și este analogul funcțional al fuziunii în buclă .
map
Funcțiile hărții pot fi și sunt adesea definite în termeni de pliere, cum ar fi foldr, ceea ce înseamnă că se poate face o fuziune hartă-pliere : foldr f z . map geste echivalent cu foldr (f . g) z.
Implementarea hărții de mai sus pe listele conectate individual nu este recursivă , așa că poate acumula o mulțime de cadre pe stivă atunci când este apelată cu o listă mare. Multe limbi oferă alternativ o funcție de „hartă inversă”, care este echivalentă cu inversarea unei liste mapate, dar este recursivă. Iată o implementare care utilizează funcția fold- stânga.
reverseMap f = foldl (\ys x -> f x : ys) []
Deoarece inversarea unei liste legate individual este, de asemenea, recursivă la coadă, inversă și inversă a hărții pot fi compuse pentru a efectua o hartă normală într-un mod recursiv la coadă, deși necesită efectuarea a două treceri peste listă.
Compararea limbajului
Funcția hartă a apărut în limbaje funcționale de programare .
Limbajul Lisp a introdus o funcție de hartă numită maplistîn 1959, cu versiuni ușor diferite care apar deja în 1958. Aceasta este definiția originală pentru maplist, maparea unei funcții peste listele succesive de odihnă:
maplist[x;f] = [null[x] -> NIL;T -> cons[f[x];maplist[cdr[x];f]]]
Funcția maplisteste încă disponibilă în Lisps mai noi, cum ar fi Common Lisp , deși funcții precum mapcarsau cele mai generice mapar fi preferate.
Cadrarea elementelor unei liste folosind maplistar fi scrisă în notație S-expression astfel:
(maplist (lambda (l) (sqr (car l))) '(1 2 3 4 5))
Folosind funcția mapcar, exemplul de mai sus ar fi scris astfel:
(mapcar (function sqr) '(1 2 3 4 5))
Astăzi funcțiile de cartografiere sunt acceptate (sau poate fi definit) în mai multe procedurale , orientate-obiect , și multi-paradigmă limbi precum: În C ++ 's Library Standard , este numit std::transform, în C # (3.0) biblioteca lui LINQ, este furnizat ca o metodă de extensie numită Select. Harta este, de asemenea, o operație frecvent utilizată în limbaje de nivel înalt, cum ar fi ColdFusion Markup Language (CFML), Perl , Python și Ruby ; operațiunea se numește mapîn toate cele patru dintre aceste limbi. Un collectalias pentru mapeste furnizat și în Ruby (de la Smalltalk ). Comună Lisp oferă o familie de funcții cum ar fi hartă; cel care corespunde comportamentului descris aici se numește mapcar( -carindicând accesul folosind operația CAR ). Există, de asemenea, limbaje cu construcții sintactice care oferă aceeași funcționalitate ca și funcția de hartă.
Harta este uneori generalizată pentru a accepta funcții diadice (cu 2 argumente) care pot aplica o funcție furnizată de utilizator elementelor corespunzătoare din două liste. Unele limbi folosesc nume speciale pentru aceasta, cum ar fi map2 sau zipWith . Limbile care folosesc funcții variadice explicite pot avea versiuni ale hărții cu aritate variabilă pentru a susține funcții de aritate variabilă . Harta cu 2 sau mai multe liste întâmpină problema manipulării atunci când listele au lungimi diferite. Diverse limbi diferă în acest sens. Unii ridică o excepție. Unii se opresc după lungimea celei mai scurte liste și ignoră elementele suplimentare de pe celelalte liste. Unii continuă până la lungimea celei mai lungi liste, iar pentru listele care s-au încheiat deja, transmiteți o anumită valoare de substituent funcției care nu indică nicio valoare.
În limbile care acceptă funcții de primă clasă și currying , mappot fi parțial aplicate pentru a ridica o funcție care funcționează pe o singură valoare la un echivalent elementar care funcționează pe un container întreg; de exemplu, map squareeste o funcție Haskell care pătrează fiecare element al unei liste.
| Limba | Hartă | Harta 2 liste | Harta n liste | Note | Manevrarea listelor de diferite lungimi |
|---|---|---|---|---|---|
| APL |
func list
|
list1 func list2
|
func/ list1 list2 list3 list4
|
Abilitățile de procesare a matricei APL fac implicite operațiuni precum harta | eroare de lungime dacă lungimile listei nu sunt egale sau 1 |
| Lisp comun |
(mapcar func list)
|
(mapcar func list1 list2)
|
(mapcar func list1 list2 ...)
|
se oprește după lungimea celei mai scurte liste | |
| C ++ |
std::transform(
|
std::transform(
|
în antet <algorithm> begin , end , and result are iterators rezultatul este scris începând cu result |
||
| C # |
ienum.Select(func)sau Clauza select
|
ienum1.Zip(ienum2, func)
|
Selecteste o metodă de extensie ienum este un IEnumerable Zipeste introdus în .NET 4.0 În mod similar în toate limbile .NET |
se oprește după ce se termină cea mai scurtă listă | |
| CFML |
obj.map(func)
|
Unde objeste o matrice sau o structură. funcprimește ca argumente valoarea fiecărui articol, indexul sau cheia acestuia și o referință la obiectul original.
|
|||
| Clojure |
(map func list)
|
(map func list1 list2)
|
(map func list1 list2 ...)
|
se oprește după ce se termină cea mai scurtă listă | |
| D |
list.map!func
|
zip(list1, list2).map!func
|
zip(list1, list2, ...).map!func
|
Specificat pentru zip prin StoppingPolicy: cel mai scurt, cel mai lung sau requireSameLength | |
| Erlang |
lists:map(Fun, List)
|
lists:zipwith(Fun, List1, List2)
|
zipwith3 deasemenea disponibil
|
Listele trebuie să aibă lungimea egală | |
| Elixir |
Enum.map(list, fun)
|
Enum.zip(list1, list2) |> Enum.map(fun)
|
List.zip([list1, list2, ...]) |> Enum.map(fun)
|
se oprește după ce se termină cea mai scurtă listă | |
| F # |
List.map func list
|
List.map2 func list1 list2
|
Funcții există pentru alte tipuri ( Seq și Array ) | Aruncă excepție | |
| Macabru |
list.collect(func)
|
[list1 list2]
|
[list1 list2 ...]
|
||
| Haskell |
map func list
|
zipWith func list1 list2
|
zipWithn func list1 list2 ...
|
ncorespunde numărului de liste; predefinit până lazipWith7
|
se oprește după ce se termină cea mai scurtă listă |
| Haxe |
array.map(func)
|
||||
| J |
func list
|
list1 func list2
|
func/ list1, list2, list3 ,: list4
|
Abilitățile de procesare a matricei de J fac implicite operațiuni precum harta | eroare de lungime dacă lungimile listei nu sunt egale |
| Java 8+ |
stream.map(func)
|
||||
|
JavaScript 1.6 ECMAScript 5 |
array#map(func)
|
List1.map(function (elem1, i) {
|
List1.map(function (elem1, i) {
|
Array # hartă trece de 3 argumente FUNC : elementul, indicele elementului și matrice. Argumentele neutilizate pot fi omise. | Se oprește la sfârșitul List1 , extinzând matricile mai scurte cu elemente nedefinite , dacă este necesar. |
| Julia |
map(func, list)
|
map(func, list1, list2)
|
map(func, list1, list2, ..., listN)
|
EROARE: DimensionMismatch | |
| Logtalk |
map(Closure, List)
|
map(Closure, List1, List2)
|
map(Closure, List1, List2, List3, ...) (up to seven lists)
|
Doar argumentul de închidere trebuie instanțiat. | Eșec |
| Mathematica |
func /@ list
|
MapThread[func, {list1, list2}]
|
MapThread[func, {list1, list2, ...}]
|
Listele trebuie să aibă aceeași lungime | |
| Maxima |
map(f, expr1, ..., exprn)
|
harta returnează o expresie al cărei operator principal este același cu cel al expresiilor; maplist returnează o listă |
|||
| OCaml |
List.map func list
|
List.map2 func list1 list2
|
ridică excepția Invalid_argument | ||
| PARI / GP |
apply(func, list)
|
N / A | |||
| Perl |
map block list
|
În bloc sau expr variabila specială $ _ reține la rândul său fiecare valoare din listă. | Helper List::MoreUtils::each_arraycombină mai mult de o listă până când cea mai lungă epuizare este completată de celelalteundef.
|
||
| PHP |
array_map(callable, array)
|
array_map(callable, array1,array2)
|
array_map(callable, array1,array2, ...)
|
Numărul de parametri pentru apelabil ar trebui să se potrivească cu numărul de tablouri. |
extinde listele mai scurte cu elemente NULL |
| Prolog |
maplist(Cont, List1, List2).
|
maplist(Cont, List1, List2, List3).
|
maplist(Cont, List1, ...).
|
Argumentele listei sunt de intrare, ieșire sau ambele. Subsumează, de asemenea, zipWith, dezarhivați, toate | Eșec silențios (nu este o eroare) |
| Piton |
map(func, list)
|
map(func, list1, list2)
|
map(func, list1, list2, ...)
|
Returnează o listă în Python 2 și un iterator în Python 3. |
zip()și map()(3.x) se oprește după terminarea celei mai scurte liste, în timp ce map()(2.x) și itertools.zip_longest()(3.x) extinde listele mai scurte cu Noneelemente
|
| Rubin |
enum.collect {block}
|
enum1.zip(enum2)
|
enum1.zip(enum2, ...)
|
enum is an Enumeration
|
se oprește la sfârșitul obiectului pe care este apelat (prima listă); dacă orice altă listă este mai scurtă, aceasta este extinsă cu elemente nule |
| Rugini |
list1.into_iter().map(func)
|
list1.into_iter().zip(list2).map(func)
|
a Iterator::mapși Iterator::zipmetodele atât preia în proprietate iteratorului original și returnează unul nou; Iterator::zipmetoda face apel la interior IntoIterator::into_itermetoda pelist2
|
se oprește după terminarea listei mai scurte | |
| S - R |
lapply(list, func)
|
mapply(func, list1, list2)
|
mapply(func, list1, list2, ...)
|
Listele mai scurte sunt ciclate | |
| Scala |
list.map(func)
|
(list1, list2)
|
(list1, list2, list3)
|
notă: mai mult de 3 nu sunt posibile. | se oprește după terminarea listei mai scurte |
| Schemă (incluzând înșelăciunea și racheta ) |
(map func list)
|
(map func list1 list2)
|
(map func list1 list2 ...)
|
listele trebuie să aibă toate aceeași lungime (SRFI-1 se extinde pentru a lua liste de lungime diferită) | |
| Convorbire scurtă |
aCollection collect: aBlock
|
aCollection1 with: aCollection2 collect: aBlock
|
Eșuează | ||
| ML standard |
map func list
|
ListPair.map func (list1, list2)
|
Pentru harta cu 2 argumente, func își ia argumentele într-un tuplu |
ListPair.mapse oprește după ce cea mai scurtă listă se încheie, în timp ce ListPair.mapEqridică UnequalLengthsexcepția
|
|
| Rapid |
sequence.map(func)
|
zip(sequence1, sequence2).map(func)
|
se oprește după ce se termină cea mai scurtă listă | ||
|
XPath 3 XQuery 3 |
list ! blockfor-each(list, func)
|
for-each-pair(list1, list2, func)
|
În blockelementul context .deține valoarea curentă
|
se oprește după ce se termină cea mai scurtă listă |
Vezi si
- Functor (programare funcțională)
- Convoluție (informatică) , numită și conv sau zip
- Filtru (funcție de ordin superior)
- Fold (funcție de ordin superior)
- pentru fiecare
- Monoid gratuit
- Programare funcțională
- Funcție de ordin superior
- Înțelegerea listei
- Hartă (model paralel)