Existe uma diferença entre foreach e map?

218

Ok, isso é mais uma questão de ciência da computação do que uma questão baseada em um idioma específico, mas existe uma diferença entre uma operação de mapa e uma operação foreach? Ou eles são simplesmente nomes diferentes para a mesma coisa?

Robert Gould
fonte
Curiosamente, se eu recebo um Iterator[String]dos scala.io.Source.fromFile("/home/me/file").getLines()e aplicar .foreach(s => ptintln(s))a ele, ele é impresso ok, mas fica certo vazio depois. Ao mesmo tempo, se eu aplicar .map(ptintln(_)), ele fica vazio e nada é impresso.
Ivan

Respostas:

294

Diferente.

O foreach itera sobre uma lista e aplica alguma operação com efeitos colaterais a cada membro da lista (como salvar cada um no banco de dados, por exemplo)

O mapa itera sobre uma lista, transforma cada membro dessa lista e retorna outra lista do mesmo tamanho com os membros transformados (como converter uma lista de cadeias de caracteres em maiúsculas)

madlep
fonte
Obrigado, eu pensei que era algo nesse sentido, mas não tinha certeza!
Robert Gould
19
mas você poderia ter efeitos colaterais acontecer no too.I função de mapa como esta resposta: stackoverflow.com/a/4927981/375688
TinyTimZamboni
6
Um ponto importante a menção (isto é particularmente verdadeiro no Scala) é que uma chamada para mapque não resultam na execução de sua lógica subjacente até quando a lista transformado esperado é convocado. Por outro lado, foreacha operação é calculada imediatamente.
bigT
124

A diferença importante entre eles é que mapacumula todos os resultados em uma coleção, enquanto foreachnão retorna nada. mapgeralmente é usado quando você deseja transformar uma coleção de elementos com uma função, enquanto foreachsimplesmente executa uma ação para cada elemento.

Henk
fonte
Obrigado! Agora eu entendo a diferença. Estava nebuloso para mim por um bom tempo
Robert Gould
Também é muito importante!
Мати Тернер
40

Em resumo, foreaché para aplicar uma operação em cada elemento de uma coleção de elementos, ao passo que mapé para transformar uma coleção em outra.

Existem duas diferenças significativas entre foreache map.

  1. foreachnão possui restrições conceituais sobre a operação que aplica, além de talvez aceitar um elemento como argumento. Ou seja, a operação pode não fazer nada, pode ter um efeito colateral, pode retornar um valor ou pode não retornar um valor. O que foreachimporta é iterar sobre uma coleção de elementos e aplicar a operação em cada elemento.

    map, por outro lado, tem uma restrição à operação: espera que a operação retorne um elemento e provavelmente também aceite um elemento como argumento. A mapoperação itera sobre uma coleção de elementos, aplicando a operação em cada elemento e, finalmente, armazenando o resultado de cada chamada da operação em outra coleção. Em outras palavras, o map transforma uma coleção em outra.

  2. foreachtrabalha com uma única coleção de elementos. Esta é a coleção de entrada.

    map trabalha com duas coleções de elementos: a coleção de entrada e a coleção de saída.

Não é um erro relacionar os dois algoritmos: na verdade, você pode visualizar os dois hierarquicamente, de onde mapé uma especialização foreach. Ou seja, você pode usar foreache fazer com que a operação transforme seu argumento e o insira em outra coleção. Portanto, o foreachalgoritmo é uma abstração, uma generalização, do mapalgoritmo. De fato, como foreachnão há restrição em sua operação, podemos dizer com segurança que esse foreaché o mecanismo de loop mais simples existente no mercado e pode fazer qualquer coisa que um loop possa fazer. map, além de outros algoritmos mais especializados, existe expressividade: se você deseja mapear (ou transformar) uma coleção em outra, sua intenção é mais clara se você usar do mapque se usar foreach.

Podemos estender mais essa discussão e considerar o copyalgoritmo: um loop que clona uma coleção. Este algoritmo também é uma especialização do foreachalgoritmo. Você pode definir uma operação que, dado um elemento, insira esse mesmo elemento em outra coleção. Se você usar foreachessa operação, você efetivamente executou o copyalgoritmo, embora com menor clareza, expressividade ou explicitação. Vamos levar ainda mais longe: podemos dizer que mapé uma especialização de copy, ela própria uma especialização de foreach. mappode alterar qualquer um dos elementos que itera. Se mapnão altera nenhum dos elementos, apenas copia os elementos e usa copy expressaria a intenção mais claramente.

O foreachpróprio algoritmo pode ou não ter um valor de retorno, dependendo do idioma. Em C ++, por exemplo, foreachretorna a operação que recebeu originalmente. A idéia é que a operação possa ter um estado, e você pode querer que ela volte a inspecionar como evoluiu sobre os elementos. maptambém pode ou não retornar um valor. Em C ++ transform(o equivalente mapaqui) retorna um iterador para o final do contêiner de saída (coleção). No Ruby, o valor de retorno mapé a sequência de saída (coleção). Portanto, o valor de retorno dos algoritmos é realmente um detalhe de implementação; seu efeito pode ou não ser o que eles retornam.

wilhelmtell
fonte
Para obter um exemplo de como .forEach()pode ser usado para implementar .map(), consulte aqui: stackoverflow.com/a/39159854/1524693 #
Chinoto Vokro
32

Array.protototype.mapmétodo e Array.protototype.forEachsão ambos bastante semelhantes.

Execute o seguinte código: http://labs.codecademy.com/bw1/6#:workspace

var arr = [1, 2, 3, 4, 5];

arr.map(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
});

console.log();

arr.forEach(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
});

Eles dão o mesmo resultado.

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

Mas a reviravolta ocorre quando você executa o seguinte código: -

Aqui, eu simplesmente atribuí o resultado do valor de retorno dos métodos map e forEach.

var arr = [1, 2, 3, 4, 5];

var ar1 = arr.map(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
    return val;
});

console.log();
console.log(ar1);
console.log();

var ar2 = arr.forEach(function(val, ind, arr){
    console.log("arr[" + ind + "]: " + Math.pow(val,2));
    return val;
});

console.log();
console.log(ar2);
console.log();

Agora o resultado é algo complicado!

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

[ 1, 2, 3, 4, 5 ]

arr[0]: 1
arr[1]: 4
arr[2]: 9
arr[3]: 16
arr[4]: 25

undefined

Conclusão

Array.prototype.mapretorna uma matriz, mas Array.prototype.forEachnão. Portanto, você pode manipular a matriz retornada dentro da função de retorno de chamada passada para o método map e depois devolvê-la.

Array.prototype.forEach apenas percorre a matriz fornecida para que você possa fazer suas coisas enquanto caminha pela matriz.

abhisekp
fonte
11

a diferença mais "visível" é que o mapa acumula o resultado em uma nova coleção, enquanto o foreach é feito apenas para a própria execução.

mas há algumas suposições extras: como o "objetivo" do mapa é a nova lista de valores, isso realmente não importa a ordem de execução. de fato, alguns ambientes de execução geram código paralelo ou até introduzem algumas memoizing para evitar exigir valores repetidos, ou preguiça, para evitar chamar alguns.

o foreach, por outro lado, é chamado especificamente para os efeitos colaterais; portanto, a ordem é importante e geralmente não pode ser paralelizada.

Javier
fonte
11

Resposta curta: map e forEachsão diferentes. Além disso, informalmente, mapé um superconjunto estrito de forEach.

Resposta longa: Primeiro, vamos apresentar descrições de uma linha forEache map:

  • forEach itera sobre todos os elementos, chamando a função fornecida em cada um.
  • map itera sobre todos os elementos, chamando a função fornecida em cada um e produz uma matriz transformada lembrando o resultado de cada chamada de função.

Em muitos idiomas, forEach costuma ser chamado apenas each. A discussão a seguir usa JavaScript apenas para referência. Poderia realmente ser qualquer outro idioma.

Agora, vamos usar cada uma dessas funções.

Usando forEach:

Tarefa 1: Escreva uma função printSquaresque aceite uma matriz de números arre imprima o quadrado de cada elemento nela.

Solução 1:

var printSquares = function (arr) {
    arr.forEach(function (n) {
        console.log(n * n);
    });
};

Usando map:

Tarefa 2: Escreva uma função selfDotque aceite uma matriz de números arre retorne uma matriz em que cada elemento é o quadrado do elemento correspondente emarr .

Além: Aqui, em termos de gíria, estamos tentando quadrar o array de entrada. Em termos gerais, estamos tentando calcular o produto escalar consigo mesmo.

Solução 2:

var selfDot = function (arr) {
    return arr.map(function (n) {
        return n * n;
    });
};

Como é mapum superconjunto deforEach ?

Você pode usar mappara resolver as duas tarefas, Tarefa 1 e Tarefa 2 . No entanto, você não pode usar forEachpara resolver a tarefa 2 .

Na solução 1 , se você simplesmente substituir forEachpor map, a solução ainda será válida. Na solução 2 , no entanto, a substituição mappor forEachinterromperá a solução que estava funcionando anteriormente.

Implementação forEachem termos de map:

Outra maneira de perceber mapa superioridade é implementar forEachem termos de map. Como somos bons programadores, não vamos entrar em poluição por namespace. Vamos ligar para o nosso forEach, apenas each.

Array.prototype.each = function (func) {
    this.map(func);
};

Agora, se você não gosta do prototypeabsurdo, aqui está:

var each = function (arr, func) {
    arr.map(func); // Or map(arr, func);
};

Então, umm .. Por que faz forEach existe?

A resposta é eficiência. Se você não está interessado em transformar uma matriz em outra matriz, por que calcular a matriz transformada? Apenas para despejar? Claro que não! Se você não quer uma transformação, não deve fazer uma transformação.

Portanto, embora o mapa possa ser usado para resolver a Tarefa 1 , provavelmente não deveria. Pois cada um é o candidato certo para isso.


Resposta original:

Embora eu concorde amplamente com a resposta de @madlep, gostaria de salientar que esse map()é um superconjunto estrito de forEach().

Sim, map()geralmente é usado para criar uma nova matriz. No entanto, também pode ser usado para alterar a matriz atual.

Aqui está um exemplo:

var a = [0, 1, 2, 3, 4], b = null;
b = a.map(function (x) { a[x] = 'What!!'; return x*x; });
console.log(b); // logs [0, 1, 4, 9, 16] 
console.log(a); // logs ["What!!", "What!!", "What!!", "What!!", "What!!"]

No exemplo acima, afoi convenientemente definido a[i] === ipara que i < a.length. Mesmo assim, demonstra o poder de map().

Aqui está a descrição oficial demap() . Observe que map()pode até alterar a matriz na qual é chamada! Salve map().

Espero que isso tenha ajudado.


Editado em 10/11/2015: Elaboração adicionada.

Sumukh Barve
fonte
-1, pois isso é válido apenas em javascript; enquanto a pergunta fala especificamente sobre agnóstico da linguagem e conceitos de ciência da computação, não implementações limitadas.
Javier
1
@Javier: Hmm .., eu tenho que concordar com você sobre a minha resposta ser específica para JavaScript. Mas pergunte a si mesmo: se um idioma tinha uma mapfunção nativa , mas não forEach; você simplesmente não poderia usar em mapvez de forEach? Por outro lado, se um idioma tivesse forEachmas não map, você teria que implementar o seu próprio map. Você não poderia simplesmente usar em forEachvez de map. Me diga o que você acha.
Sumukh Barve
3

Aqui está um exemplo no Scala usando listas: mapa retorna lista, foreach não retorna nada.

def map(f: Int ⇒ Int): List[Int]
def foreach(f: Int ⇒ Unit): Unit

Portanto, map retorna a lista resultante da aplicação da função f em cada elemento da lista:

scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)

scala> list map (x => x * 2)
res0: List[Int] = List(2, 4, 6)

O Foreach apenas aplica f a cada elemento:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

scala> sum
res2: Int = 6 // res1 is empty
irudyak
fonte
2

Se você está falando sobre Javascript em particular, a diferença é que mapé uma função de loop enquanto forEaché um iterador.

Use mapquando quiser aplicar uma operação a cada membro da lista e obter os resultados novamente como uma nova lista, sem afetar a lista original.

Use forEachquando quiser fazer algo com base em cada elemento da lista. Você pode adicionar coisas à página, por exemplo. Essencialmente, é ótimo para quando você deseja "efeitos colaterais".

Outras diferenças: forEachnão retorna nada (já que é realmente uma função de fluxo de controle), e a função transmitida obtém referências ao índice e a lista inteira, enquanto o mapa retorna a nova lista e passa apenas no elemento atual.

trate bem seus mods
fonte
0

O ForEach tenta aplicar uma função como escrever em db etc em cada elemento do RDD sem retornar nada.

Mas o map()aplica alguma função sobre os elementos de rdd e retorna o rdd. Portanto, quando você executa o método abaixo, ele não falha na linha3, mas ao coletar o rdd após aplicar o foreach, ele falha e gera um erro que diz

Arquivo "<stdin>", linha 5, em <module>

AttributeError: o objeto 'NoneType' não tem atributo 'collect'

nums = sc.parallelize([1,2,3,4,5,6,7,8,9,10])
num2 = nums.map(lambda x: x+2)
print ("num2",num2.collect())
num3 = nums.foreach(lambda x : x*x)
print ("num3",num3.collect())
user2456047
fonte