Content deleted Content added
Precisions on lists names |
m →Language comparison: {{code}} |
||
(40 intermediate revisions by 32 users not shown) | |||
Line 1:
<references group="pakistni" />
{{one source|date=November 2012}}▼
{{Short description|Computer programming function}}
{{for|the abstract data type of the same name|Map (data structure)}}
▲{{one source|date=November 2012}}
In many [[programming language]]s, '''map''' is
The concept of a map is not limited to lists: it works for sequential [[Container (abstract data type)|containers]], tree-like containers, or even abstract containers such as [[futures and promises]].
==
Suppose
<syntaxhighlight lang="haskell">
Line 13 ⟶ 16:
</syntaxhighlight>
Afterwards
<syntaxhighlight lang="haskell">
Line 19 ⟶ 22:
</syntaxhighlight>
which yields <code>[1, 4, 9, 16, 25]</code>, demonstrating that <code>map</code> has gone through the entire list and applied the function <code>square</code> to each element.
=== Visual example ===
Below, there is view of each step of the mapping process for a list of integers <code>X = [0, 5, 8, 3, 2, 1]</code> mapping into a new list <code>X'</code> according to the function <math>f(x) = x + 1</math> :
[[File:Mapping-steps-loillibe-new.gif|alt=applying map function processing steps|none|thumb|480x480px|View of processing steps when applying map function on a list]]▼
The <code>map</code> is provided as part of the Haskell's base prelude (i.e. "standard library") and is implemented as:
<syntaxhighlight lang="haskell">
Line 25 ⟶ 35:
map _ [] = []
map f (x : xs) = f x : map f xs
</syntaxhighlight>
▲[[File:Mapping-steps-loillibe-new.gif|alt=applying map function processing steps|none|thumb|480x480px|View of processing steps when applying map function on a list]]
== Generalization ==
{{see also|Functor|Category theory}}
In Haskell, the [[polymorphic function]]
The [[type constructor]] of lists <code>[]</code> can be defined as an instance of the <code>Functor</code> type class using the <code>map</code> function from the previous example:
Line 68 ⟶ 77:
Among other uses, this allows defining element-wise operations for various kinds of [[collection (computer science)|collections]].
=== Category-theoretic background ===
In [[category theory]], a [[functor]] <math>F : C \rarr D</math> consists of two maps: one that sends each object {{mvar|A}} of the category to another object {{mvar|F A}}, and one that sends each morphism <math>f : A \rarr B</math> to another morphism <math>Ff : FA \rarr FB</math>, which acts as a [[homomorphism]] on categories (i.e. it respects the category axioms). Interpreting the universe of data types as a category {{mvar|Type}}, with morphisms being functions, then a type constructor <code>F</code> that is a member of the <code>Functor</code> type class is the object part of such a functor, and {{code|2=haskell|1=fmap :: (a -> b) -> F a -> F b}} is the morphism part. The functor laws described above are precisely the category-theoretic functor axioms for this functor.
Functors can also be objects in categories, with "morphisms" called [[natural transformation]]s. Given two functors <math>F, G : C \rarr D</math>, a natural transformation <math>\eta : F \rarr G</math> consists of a collection of morphisms <math>\eta_A : FA \rarr GA</math>, one for each object {{mvar|A}} of the category {{mvar|D}}, which are 'natural' in the sense that they act as a 'conversion' between the two functors, taking no account of the objects that the functors are applied to. Natural transformations correspond to functions of the form {{code|2=haskell|1=eta :: F a -> G a}}, where <code>a</code> is a universally quantified type variable – <code>eta</code> knows nothing about the type which inhabits <code>a</code>. The naturality axiom of such functions is automatically satisfied because it is a so-called free theorem, depending on the fact that it is [[parametric polymorphism|parametrically polymorphic]].<ref>In a [[non-strict language]] that permits general recursion, such as Haskell, this is only true if the first argument to <code>fmap</code> is strict. {{cite conference |title=Theorems for free! |first=Philip |last=Wadler |author-link=Philip Wadler |conference=4th International Symposium on Functional Programming Languages and Computer Architecture |___location=London |date=September 1989 |url=https://people.mpi-sws.org/~dreyer/tor/papers/wadler.pdf |publisher=[[Association for Computing Machinery]]}}</ref> For example, {{code|2=haskell|1=reverse :: List a -> List a}}, which reverses a list, is a natural transformation, as is {{code|2=haskell|1=flattenInorder :: Tree a -> List a}}, which flattens a tree from left to right, and even {{code|2=haskell|1=sortBy :: (a -> a -> Bool) -> List a -> List a}}, which sorts a list based on a provided comparison function.
==Optimizations==
Line 78 ⟶ 88:
* <code>(map f . map g) list</code> and
* <code>map (f . g) list</code>
lead to the same result; that is, <math>\operatorname{map}(f) \circ \operatorname{map}(g) = \operatorname{map}(f \circ g)</math>.
Map functions can be and often are defined in terms of a [[Fold (higher-order function)|fold]] such as <code>foldr</code>, which means one can do a ''map-fold fusion'': <code>foldr f z . map g</code> is equivalent to <code>foldr (f . g) z</code>.
Line 88 ⟶ 98:
</syntaxhighlight>
Since reversing a singly linked list is also tail-recursive, reverse and reverse-map can be composed to perform normal map in a tail-recursive way, though it requires performing two passes over the list.
==Language comparison==
Line 114 ⟶ 124:
</syntaxhighlight>
Today mapping functions are supported (or may be defined) in many [[procedural programming|procedural]], [[Object-oriented programming|object-oriented]], and [[multi-paradigm]] languages as well: In [[C++]]'s [[C++ Standard
Map is sometimes generalized to accept dyadic (2-argument) functions that can apply a user-supplied function to corresponding elements from two lists. Some languages use special names for this, such as ''map2'' or ''zipWith''. Languages using explicit [[variadic function]]s may have versions of map with variable [[arity]] to support ''variable-arity'' functions. Map with 2 or more lists encounters the issue of handling when the lists are of different lengths. Various languages differ on this. Some raise an exception. Some stop after the length of the shortest list and ignore extra items on the other lists. Some continue on to the length of the longest list, and for the lists that have already ended, pass some placeholder value to the function indicating no value.
In languages which support [[first-class function]]s and [[currying]], <code>map</code> may be [[partial application|partially applied]] to ''lift'' a function that works on only one value to an element-wise equivalent that works on an entire container; for example, <code>map square</code> is a Haskell function which squares each element of a list.
{| class="wikitable" style="font-size: 85%"
|+ Map in various languages
! scope="col" | Language
! scope="col" | Map
! scope="col" | Map 2 lists
! scope="col" | Map n lists
! scope="col" | Notes
! scope="col" | Handling lists of different lengths
|- valign="top"
| [[APL (programming language)|APL]]
Line 142 ⟶ 157:
| <code>std::transform(<wbr/>''begin1'', ''end1'', ''begin2'', ''result'', ''func'')</code>
|
| in header {{mono|<algorithm>}}<br /> ''begin'', ''end'', and ''result'' are iterators<br /> result is written starting at ''result''
|
|- valign="top"
Line 193 ⟶ 208:
| Functions exist for other types (''Seq'' and ''Array'')
| Throws exception
|- valign="top"
| [[Gleam (programming language)|Gleam]]
| <code>list.map(''list'', ''func'')</code><br><code>yielder.map(''yielder'', ''func'')</code>
| <code>list.map2(''list1'', ''list2'', ''func'')</code><br><code>yielder.map2(''yielder1'', ''yielder2'', ''func'')</code>
|▼
| ▼
| drops the extra elements of the longer list
|- valign="top"
| [[Groovy (programming language)|Groovy]]
|
|
|
|
|
Line 209 ⟶ 231:
|- valign="top"
| [[Haxe]]
| <code>''array''.map(''func'')<br />''list''.map(''func'')<br />Lambda.map(''iterable'', ''func'')</code>
|
|
Line 319 ⟶ 338:
|-
|[[Rust (programming language)|Rust]]
| <code>''list1''.into_iter().map(''func'')</code>
|
| the <code>Iterator::map</code> and <code>Iterator::zip</code> methods both take ownership of the original iterator and return a new one; the <code>Iterator::zip</code> method internally calls the <code>IntoIterator::into_iter</code> method on <code>''list2''</code>
| stops after the shorter list ends▼
▲|
▲ list1.iter().zip(list2.iter()).map(func)
▲|
▲|stops after the shorter list ends
|- valign="top"
| [[S (programming language)|S]]-[[R (programming language)|R]]
Line 341 ⟶ 358:
| stops after the shorter list ends
|- valign="top"
| [[Scheme (programming language)|Scheme]]
| <code>(map ''func'' ''list'')</code>
| <code>(map ''func'' ''list1'' ''list2'')</code>
| <code>(map ''func'' ''list1'' ''list2'' ...)</code>
|
| lists must all have same length (SRFI-1 extends to take lists of different length)
|- valign="top"
| [[Smalltalk]]
Line 370 ⟶ 387:
|- valign="top"
| [[XPath 3]]<br />[[XQuery]] 3
|
|
|
| In <code>block</code> the context item <code>.</code> holds the current value
Line 378 ⟶ 395:
==See also==
* [[Functor (functional programming)]]
* [[
* [[Filter (higher-order function)]]
* [[Fold (higher-order function)]]
* [[foreach loop]]
* [[Free monoid]]
* [[Functional programming]]
|