Map (高阶函数)

在很多编程语言中，映射（map）是一个高阶函数的名字，它将一个给定函数应用到一个函子比如列表的每个元素，返回按相同次序的一个列表。映射的概念不受限于列表：它可工作在顺序的容器，类似树的容器，甚至是抽象容器比如future与promise。
映射在作为泛函形式考虑时，经常叫做“应用于全部”。在支持头等函数和柯里化的语言中，map可以部份应用在一个函数上，将这个只工作在一个值之上函数，提升为工作在整个容器上的逐个元素上的函数。

**定义**
map作为Haskell的基本前序（prelude：就是标准库）的一部份提供并被实现为：
map :: (a -> b) -> a -> b
map _        = 
map f (x : xs) = f x : map f xs
在这个定义之中，涉及到四种不同的模式：- f是匹配“无论任何事物”的模式，并绑定f变量至所匹配的任何事物。- (x : xs)是匹配“非空列表”的模式，它是通过将要被绑定到x变量某个事物，cons（这里是(:)函数）到要被绑定到xs变量的某个其他事物之上而形成的。- 是匹配“空列表”的模式，它不绑定任何变量。- _是匹配不被绑定的任何事物的模式（通配符即“不在意”模式）。
Lisp语言在1959年介入了叫做maplist的映射函数，与1958年出现的版本稍有不同。这是maplist的最初定义，将一个函数连续的映射到整个列表和去除第一个元素余下列表之上：
maplistx;f = nullx -> NIL;T -> consfx;maplistcdrx;f
函数maplist在更新近的Lisp比如Common Lisp中仍可获得到。Common Lisp提供映射函数家族，如mapcar和更一般性的map等，其中对应这里所描述行为的那叫做mapcar，这里的后缀car指示取列表第一个元素的CAR运算。

**例子：映射一个列表**
假定我们有一个整数的列表1, 2, 3, 4, 5，并想要计算每个整数的平方。要完成这个任务，首先要定义一个函数square来做单一数值的平方，下面用Haskell演示：
square x = x * x
然后就可以调用：
>>> map square 1, 2, 3, 4, 5
它产生1, 4, 9, 16, 25，展示了map遍历了整个列表并应用函数square于每个元素。在ML、Haskell和F#中，函数是缺省以柯里化形式定义的，从而map square是对列表的每个元素做平方的Haskell函数。 
在Lisp中，使用mapcar对列表的元素做平方，使用S-表达式表示法写为：
(mapcar (function sqr) '(1 2 3 4 5))
使用函数maplist，上例将写为：
(maplist (lambda (l) (sqr (car l))) '(1 2 3 4 5))

**可视的例子**
下面将看到一个映射过程的每一步骤的可视演示，它依据函数将整数列表X = 0, 5, 8, 3, 2, 1映射成新的列表X'：
在应用一个函数于一个列表时的处理步骤的演示

**推广**
在Haskell中，多态函数map :: (a -> b) -> a -> b，被推广成多类型函数fmap :: Functor f => (a -> b) -> f a -> f b，它应用于属于函子Functor类型类的任何类型上。
列表的类型构造子可以定义为Functor类型类的实例，使用上例中的map函数：
instance Functor  where
fmap = map
其他Functor实例的例子包括树：
-- 一个简单的二叉树
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)
在一个树之上的映射产生：
>>> fmap square (Fork (Fork (Leaf 1) (Leaf 2)) (Fork (Leaf 3) (Leaf 4)))
Fork (Fork (Leaf 1) (Leaf 4)) (Fork (Leaf 9) (Leaf 16))
对于Functor类型类的所有实例，fmap都在契约义务上要满足函子定律：
fmap id      ≡ id              -- 同一律
fmap (f . g) ≡ fmap f . fmap g -- 复合律
这里的.在Haskell中指示函数复合。
尤其是，它允许为各种搜集定义逐个元素的运算。
此外，如果F和G是两个函子，自然变换是多态类型的函数，它遵守：
 ，对于任何函数。
如果函数是按上述类型定义那样用参数多态定义的，这个规定总是满足的。

**优化**
映射的数学基础允许很多优化。复合定律确保了如下二者：- (map f . map g) list- map (f . g) list
导致相同的结果；就是说。但是第二种形式比第一种形式在计算上更加有效率，因为每个map要求从头重建整个列表。因此，编译器将尝试将第一种形式变换第二种形式；这种类型的优化叫做“映射融合”，是循环融合的函数式类似者。
映射函数可以并经常依据fold比如foldr来定义，这意味着可以做“map-fold融合”：foldr f z . map g等价于foldr (f . g) z。
在一个单链表上的map实现不是尾递归的，所以它在调用于大型列表的时候，可能在栈上建造大量的帧。很多语言作为替代的提供“逆向map”函数，它等价于逆转一个映射后的列表，但它是尾递归的。下面是利用了左fold函数的一个实现：
reverseMap f = foldl (\ys x -> f x : ys) 
因为逆转单链表也是尾递归的，reverse和reverse-map可以复合起来以尾递归方式进行正常的map，尽管这需要在这个列表上进行两趟。

**语言比较**
映射函数出现在函数式编程语言之中。现在映射函数在很多过程式、面向对象和多范型语言中也能获得到（或能够定义）：在C++的标准模板库中，它叫做std::transform，在C#（3.0）的LINQ库中，它被作为叫做Select的扩展方法。映射也经常在高级语言中的计算，比如ColdFusion Markup Language（CFML）、Perl、Python和Ruby；在这四种语言中这个运算都叫做map 。在Ruby中还提供collect作为map的别名（来自Smalltalk）。有些语言通过语法构造提供与映射函数相同的功能。
映射有时被推广为接收二元的（2个参数）函数，它可以把用户提供的函数应用到来自两个列表的对应元素上。有些语言对它使用特殊名字，比如“map2”或“zipWith”。使用显式的可变元数函数的语言拥有可变元数版本的映射来支持可变元数函数。有2个或更多列表在这些列表有不同长度的时候遇到需要处理的问题。各种语言在这个问题上是不同的。有些引起异常。有些在达到最短列表长度之后停止并忽略在其他列表上的额外项目。有些继续直到最长列表长度，并对已经结束的列表，向函数传递某个占位符的值来指示没有值。
}
apply(func, list)


Perl
map block list map expr, list
在block或expr中特殊变量$_持有依次来自这个列表的值。
Helper List::MoreUtils::each_array组合多于一个列表直到最长的那个被耗尽，用undef.填入其他的。
PHP
array_map(callable, array)
array_map(callable, array1,array2)
array_map(callable, array1,array2, ...)
callable的形式参数的数目应当匹配数组的数目。
用NULL项目扩展较短的列表
Prolog
maplist(Cont, List1, List2).
maplist(Cont, List1, List2, List3).
maplist(Cont, List1, ...).
列表实际参数是输入、输出或二者。也包括zipWith, unzip, all
静默失败（不是错误）
Python
map(func, list)
map(func, list1, list2)
map(func, list1, list2, ...)
在Python 2中返回一个列表，在Python 3中返回一个迭代器。
zip()和map() (3.x)在最短列表结束后停止，而map()(2.x)和itertools.zip_longest()(3.x)用None项目扩展较短的列表
Ruby
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
在它调用在其上的对象（第一个列表）结束时停止；如果任何其他列表更短，用nil项目扩展它
Rust
list1.into_iter().map(func)
list1.into_iter().zip(list2).map(func)
Iterator::map和Iterator::zip方法二者接受最初迭代器的所属关系并返回一个新的；Iterator::zip方法内部的调用IntoIterator::into_iter方法在list2之上
在较短列表结束后停止
S-R
lapply(list, func)
mapply(func, list1, list2)
mapply(func, list1, list2, ...)
较短列表被循环
Scala
list.map(func)
(list1, list2).zipped.map(func)
(list1, list2, list3).zipped.map(func)
注意：多于3不可能。
在较短列表结束后停止
Scheme（包括Guile和Racket）
(map func list)
(map func list1 list2)
(map func list1 list2 ...)
列表必须都有相同长度（SRFI-1扩展接受不同长度的列表）
Smalltalk
aCollection collect: aBlock
aCollection1 with: aCollection2 collect: aBlock
失败
Standard ML
map func list
ListPair.map func (list1, list2)  ListPair.mapEq func (list1, list2)
对于2实际参数map，func接受在一个元组中的实际参数
ListPair.map在最短列表结束后停止，而ListPair.mapEq引起UnequalLengths异常
Swift
sequence.map(func)
zip(sequence1, sequence2).map(func)
在最短列表结束后停止
XPath 3 3
list ! block for-each(list, func)
for-each-pair(list1, list2, func)
在block上下文中项目.持有当前值
在最短列表结束后停止

**参见**
函子 (函数式编程)- Convolution (computer science)，也叫做“conv”或“zip”- Filter (高阶函数)- Fold (高阶函数)- foreach循环- 自由幺半群- 高阶函数- 列表推导式- Map (parallel pattern)