Mapa (função de ordem superior) - Map (higher-order function)
Em muitas linguagens de programação , map é o nome de uma função de ordem superior que aplica uma determinada função a cada elemento de um functor , por exemplo, uma lista , retornando uma lista de resultados na mesma ordem. Freqüentemente, é chamado de aplicar a todos quando considerado na forma funcional .
O conceito de um mapa não se limita a listas: ele funciona para contêineres sequenciais , contêineres em forma de árvore ou mesmo contêineres abstratos, como futuros e promessas .
Exemplos: mapeamento de uma lista
Suponha que temos uma lista de inteiros [1, 2, 3, 4, 5]
e gostaríamos de calcular o quadrado de cada inteiro. Para fazer isso, primeiro definimos uma função para square
um único número (mostrado aqui em Haskell ):
square x = x * x
Depois podemos ligar
>>> map square [1, 2, 3, 4, 5]
que rende [1, 4, 9, 16, 25]
, demonstrando que map
percorreu toda a lista e aplicou a função square
a cada elemento.
Exemplo visual
Abaixo, você pode ver uma visão de cada etapa do processo de mapeamento para uma lista de inteiros X = [0, 5, 8, 3, 2, 1]
que queremos mapear em uma nova lista de X'
acordo com a função :
O map
é fornecido como parte do prelúdio base do Haskell (ou seja, "biblioteca padrão") e é implementado como:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x : xs) = f x : map f xs
Generalização
Em Haskell, a função polimórfica map :: (a -> b) -> [a] -> [b]
é generalizada para uma função politípica fmap :: Functor f => (a -> b) -> f a -> f b
, que se aplica a qualquer tipo pertencente à classe de tipo .
Functor
O construtor de tipo de listas []
pode ser definido como uma instância da Functor
classe de tipo usando a map
função do exemplo anterior:
instance Functor [] where
fmap = map
Outros exemplos de Functor
instâncias incluem árvores:
-- 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)
O mapeamento de uma árvore produz:
>>> fmap square (Fork (Fork (Leaf 1) (Leaf 2)) (Fork (Leaf 3) (Leaf 4)))
Fork (Fork (Leaf 1) (Leaf 4)) (Fork (Leaf 9) (Leaf 16))
Para cada instância da Functor
classe de tipo, fmap
é contratualmente obrigado a obedecer às leis do functor:
fmap id ≡ id -- identity law
fmap (f . g) ≡ fmap f . fmap g -- composition law
onde .
denota a composição da função em Haskell.
Entre outros usos, isso permite definir operações de elementos para vários tipos de coleções .
Além disso, se F e G são dois functores, uma transformação natural é uma função do tipo polimórfico que respeita fmap :
- para qualquer função .
Se a função h for definida por polimorfismo paramétrico como na definição de tipo acima, esta especificação é sempre satisfeita.
Otimizações
A base matemática dos mapas permite várias otimizações . A lei de composição garante que ambos
-
(map f . map g) list
e map (f . g) list
levar ao mesmo resultado; isto é ,. No entanto, o segundo formulário é mais eficiente de calcular do que o primeiro, porque cada um requer a reconstrução de uma lista inteira do zero. Portanto, os compiladores tentarão transformar a primeira forma na segunda; esse tipo de otimização é conhecido como fusão de mapa e é o análogo funcional da fusão de loop .
map
As funções de mapa podem ser e frequentemente são definidas em termos de uma dobra como foldr
, o que significa que se pode fazer uma fusão de dobra do mapa : foldr f z . map g
é equivalente a foldr (f . g) z
.
A implementação do mapa acima em listas unidas individualmente não é recursiva no final , portanto, pode acumular muitos quadros na pilha quando chamado com uma lista grande. Muitos idiomas fornecem alternadamente uma função de "mapa reverso", que é equivalente a reverter uma lista mapeada, mas é recursiva na cauda. Aqui está uma implementação que utiliza a função fold -left.
reverseMap f = foldl (\ys x -> f x : ys) []
Visto que a reversão de uma lista unida individualmente também é recursiva na cauda, o mapa reverso e reverso pode ser composto para executar o mapa normal de uma forma recursiva na cauda, embora exija a execução de duas passagens sobre a lista.
Comparação de linguagem
A função de mapa originou-se em linguagens de programação funcionais .
A linguagem Lisp introduziu uma função de mapa chamada maplist
em 1959, com versões ligeiramente diferentes já aparecendo em 1958. Esta é a definição original para maplist
, mapear uma função em listas de resto sucessivas:
maplist[x;f] = [null[x] -> NIL;T -> cons[f[x];maplist[cdr[x];f]]]
A função maplist
ainda está disponível em Lisps mais recentes, como Common Lisp , embora funções como mapcar
ou mais genéricas map
sejam preferidas.
Quadrar os elementos de uma lista usando maplist
seria escrito em notação de expressão S assim:
(maplist (lambda (l) (sqr (car l))) '(1 2 3 4 5))
Usando a função mapcar
, o exemplo acima seria escrito assim:
(mapcar (function sqr) '(1 2 3 4 5))
Hoje funções de mapeamento são suportadas (ou pode ser definida), em muitos processuais , orientada a objeto , e multi-paradigma línguas também: Em C ++ 's biblioteca padrão , ele é chamado std::transform
, em C # (3,0)' s biblioteca LINQ, é fornecido como um método de extensão chamado Select
. Map também é uma operação freqüentemente usada em linguagens de alto nível, como ColdFusion Markup Language (CFML), Perl , Python e Ruby ; a operação é chamada map
em todos esses quatro idiomas. Um collect
alias para map
também é fornecido em Ruby (de Smalltalk ). Common Lisp fornece uma família de funções semelhantes a mapas; é chamado aquele que corresponde ao comportamento aqui descrito mapcar
( -car
indicando o acesso através da operação CAR ). Existem também linguagens com construções sintáticas que fornecem a mesma funcionalidade que a função de mapa.
O mapa às vezes é generalizado para aceitar funções diádicas (2 argumentos) que podem aplicar uma função fornecida pelo usuário a elementos correspondentes de duas listas. Alguns idiomas usam nomes especiais para isso, como map2 ou zipWith . As linguagens que usam funções variáveis explícitas podem ter versões de mapa com aridade variável para suportar funções de aridade variável . O mapa com 2 ou mais listas encontra o problema de manuseio quando as listas são de comprimentos diferentes. Vários idiomas diferem nisso. Alguns levantam uma exceção. Alguns param após o comprimento da lista mais curta e ignoram os itens extras nas outras listas. Alguns continuam com o comprimento da lista mais longa e, para as listas que já terminaram, passam algum valor de espaço reservado para a função que não indica nenhum valor.
Em linguagens que suportam funções de primeira classe e currying , map
pode ser parcialmente aplicado para levantar uma função que funciona em apenas um valor para um equivalente elementar que funciona em um contêiner inteiro; por exemplo, map square
é uma função Haskell que quadrada cada elemento de uma lista.
Língua | Mapa | Mapa 2 listas | Mapear listas n | Notas | Lidar com listas de diferentes comprimentos |
---|---|---|---|---|---|
APL |
func list
|
list1 func list2
|
func/ list1 list2 list3 list4
|
As habilidades de processamento de array do APL tornam operações como mapear implícitas | erro de comprimento se os comprimentos da lista não forem iguais ou 1 |
Lisp Comum |
(mapcar func list)
|
(mapcar func list1 list2)
|
(mapcar func list1 list2 ...)
|
pára após o comprimento da lista mais curta | |
C ++ |
std::transform(
|
std::transform(
|
no cabeçalho <algorithm> início , fim e resultado são iteradores, o resultado é escrito começando no resultado |
||
C # |
ienum.Select(func) ou a select cláusula
|
ienum1.Zip(ienum2, func)
|
Select é um método de extensão ienum é um IEnumerable Zip é introduzido no .NET 4.0 Da mesma forma em todas as linguagens .NET |
pára depois que a lista mais curta termina | |
CFML |
obj.map(func)
|
Onde obj está um array ou uma estrutura. func recebe como argumentos o valor de cada item, seu índice ou chave e uma referência ao objeto original.
|
|||
Clojure |
(map func list)
|
(map func list1 list2)
|
(map func list1 list2 ...)
|
pára depois que a lista mais curta termina | |
D |
list.map!func
|
zip(list1, list2).map!func
|
zip(list1, list2, ...).map!func
|
Especificado para compactar por StoppingPolicy: shortest, longest ou requireSameLength | |
Erlang |
lists:map(Fun, List)
|
lists:zipwith(Fun, List1, List2)
|
zipwith3 também disponível
|
As listas devem ter o mesmo comprimento | |
Elixir |
Enum.map(list, fun)
|
Enum.zip(list1, list2) |> Enum.map(fun)
|
List.zip([list1, list2, ...]) |> Enum.map(fun)
|
pára depois que a lista mais curta termina | |
F # |
List.map func list
|
List.map2 func list1 list2
|
Existem funções para outros tipos ( Seq e Array ) | Lança exceção | |
Groovy |
list.collect(func)
|
[list1 list2]
|
[list1 list2 ...]
|
||
Haskell |
map func list
|
zipWith func list1 list2
|
zipWithn func list1 list2 ...
|
n corresponde ao número de listas; predefinido atézipWith7
|
pára depois que a lista mais curta termina |
Haxe |
array.map(func)
|
||||
J |
func list
|
list1 func list2
|
func/ list1, list2, list3 ,: list4
|
As habilidades de processamento de array de J tornam operações como mapear implícitas | erro de comprimento se os comprimentos da lista não forem iguais |
Java 8+ |
stream.map(func)
|
||||
JavaScript 1.6 ECMAScript 5 |
array#map(func)
|
List1.map(function (elem1, i) {
|
List1.map(function (elem1, i) {
|
Array # map passa 3 argumentos para func : o elemento, o índice do elemento e o array. Os argumentos não utilizados podem ser omitidos. | Pára no final da Lista1 , estendendo as matrizes mais curtas com itens indefinidos, se necessário. |
Julia |
map(func, list)
|
map(func, list1, list2)
|
map(func, list1, list2, ..., listN)
|
ERROR: DimensionMismatch | |
Logtalk |
map(Closure, List)
|
map(Closure, List1, List2)
|
map(Closure, List1, List2, List3, ...) (up to seven lists)
|
Apenas o argumento Closure deve ser instanciado. | Fracasso |
Mathematica |
func /@ list
|
MapThread[func, {list1, list2}]
|
MapThread[func, {list1, list2, ...}]
|
As listas devem ter o mesmo comprimento | |
Maxima |
map(f, expr1, ..., exprn)
|
map retorna uma expressão cujo operador líder é o mesmo das expressões; maplist retorna uma lista |
|||
OCaml |
List.map func list
|
List.map2 func list1 list2
|
levanta exceção Invalid_argument | ||
PARI / GP |
apply(func, list)
|
N / D | |||
Perl |
map block list
|
No bloco ou na expr, a variável especial $ _ mantém cada valor da lista sucessivamente. | Helper List::MoreUtils::each_array combina mais de uma lista até que a mais longa se esgote, preenchendo as outras comundef.
|
||
PHP |
array_map(callable, array)
|
array_map(callable, array1,array2)
|
array_map(callable, array1,array2, ...)
|
O número de parâmetros para chamada deve corresponder ao número de matrizes. |
estende as listas mais curtas com itens NULL |
Prolog |
maplist(Cont, List1, List2).
|
maplist(Cont, List1, List2, List3).
|
maplist(Cont, List1, ...).
|
Os argumentos da lista são entrada, saída ou ambos. Inclui também zipWith, unzip, all | Falha silenciosa (não é um erro) |
Pitão |
map(func, list)
|
map(func, list1, list2)
|
map(func, list1, list2, ...)
|
Retorna uma lista em Python 2 e um iterador em Python 3. |
zip() e map() (3.x) para após o término da lista mais curta, enquanto map() (2.x) e itertools.zip_longest() (3.x) estendem as listas mais curtas com None itens
|
Rubi |
enum.collect {block}
|
enum1.zip(enum2)
|
enum1.zip(enum2, ...)
|
enum is an Enumeration
|
para no final do objeto em que é chamado (a primeira lista); se qualquer outra lista for mais curta, ela é estendida com nenhum item |
Ferrugem |
list1.into_iter().map(func)
|
list1.into_iter().zip(list2).map(func)
|
os métodos Iterator::map e Iterator::zip assumem a propriedade do iterador original e retornam um novo; o Iterator::zip método chama internamente o IntoIterator::into_iter método emlist2
|
pára depois que a lista mais curta termina | |
S - R |
lapply(list, func)
|
mapply(func, list1, list2)
|
mapply(func, list1, list2, ...)
|
Listas mais curtas são cicladas | |
Scala |
list.map(func)
|
(list1, list2)
|
(list1, list2, list3)
|
nota: mais de 3 não são possíveis. | pára depois que a lista mais curta termina |
Esquema (incluindo Guile e Racket ) |
(map func list)
|
(map func list1 list2)
|
(map func list1 list2 ...)
|
as listas devem ter o mesmo comprimento (SRFI-1 se estende para receber listas de tamanhos diferentes) | |
Conversa fiada |
aCollection collect: aBlock
|
aCollection1 with: aCollection2 collect: aBlock
|
Falha | ||
ML padrão |
map func list
|
ListPair.map func (list1, list2)
|
Para mapa de 2 argumentos, func recebe seus argumentos em uma tupla |
ListPair.map para depois que a lista mais curta termina, enquanto ListPair.mapEq levanta uma UnequalLengths exceção
|
|
Rápido |
sequence.map(func)
|
zip(sequence1, sequence2).map(func)
|
pára depois que a lista mais curta termina | ||
XPath 3 XQuery 3 |
list ! block for-each(list, func)
|
for-each-pair(list1, list2, func)
|
No block item de contexto . contém o valor atual
|
pára depois que a lista mais curta termina |
Veja também
- Functor (programação funcional)
- Convolução (ciência da computação) , também chamada de conv ou zip
- Filtro (função de ordem superior)
- Dobrar (função de ordem superior)
- para cada
- Monóide grátis
- Programação funcional
- Função de ordem superior
- Compreensão de lista
- Mapa (padrão paralelo)