C'è una differenza tra foreach e map?


218

Ok, si tratta più di una domanda di informatica, che di una domanda basata su un linguaggio particolare, ma c'è una differenza tra un'operazione di mappa e un'operazione di foreach? O sono semplicemente nomi diversi per la stessa cosa?


Curiosamente, se ottengo un Iterator[String]da scala.io.Source.fromFile("/home/me/file").getLines()e lo applico .foreach(s => ptintln(s)), viene stampato ok ma si svuota subito dopo. Allo stesso tempo, se lo applico .map(ptintln(_)), si svuota e non viene stampato nulla.
Ivan

Risposte:


294

Diverso.

foreach scorre su un elenco e applica alcune operazioni con effetti collaterali a ciascun membro dell'elenco (come ad esempio il salvataggio di ognuno nel database)

la mappa scorre su un elenco, trasforma ogni membro di quell'elenco e restituisce un altro elenco della stessa dimensione con i membri trasformati (come convertire un elenco di stringhe in maiuscolo)


Grazie, ho pensato che fosse qualcosa del genere, ma non ne ero sicuro!
Robert Gould,

19
ma si potrebbe avere accadono effetti collaterali nella mappa delle funzioni too.I come questa risposta: stackoverflow.com/a/4927981/375688
TinyTimZamboni

6
Un punto importante da menzionare (ciò è particolarmente vero in Scala) è che una chiamata a mapnon non comporta l'esecuzione della sua logica sottostante fino quando la lista trasformata atteso viene chiamato. Al contrario, foreachl'operazione viene calcolata immediatamente.
bigT

124

La differenza importante tra loro è che mapaccumula tutti i risultati in una raccolta, mentre foreachnon restituisce nulla. mapviene solitamente utilizzato quando si desidera trasformare una raccolta di elementi con una funzione, mentre foreachesegue semplicemente un'azione per ciascun elemento.


Grazie! Ora capisco la differenza. Era stato un po 'confuso per me da un po' di tempo
Robert Gould il

Anche molto importante!
Мати Тернер,

40

In breve, foreachè per applicare un'operazione su ciascun elemento di una raccolta di elementi, mentremap per trasformare una raccolta in un'altra.

Esistono due differenze significative tra foreache map.

  1. foreachnon ha restrizioni concettuali sull'operazione che applica, a parte forse accettare un elemento come argomento. Cioè, l'operazione potrebbe non fare nulla, potrebbe avere un effetto collaterale, può restituire un valore o non può restituire un valore. Tuttiforeach importa è iterare su una raccolta di elementi e applicare l'operazione su ciascun elemento.

    mapd'altra parte, ha una restrizione sull'operazione: si aspetta che l'operazione restituisca un elemento e probabilmente accetta anche un elemento come argomento. L' mapoperazione scorre su una raccolta di elementi, applicando l'operazione su ciascun elemento e infine memorizzando il risultato di ogni invocazione dell'operazione in un'altra raccolta. In altre parole, map trasforma una raccolta in un'altra.

  2. foreachfunziona con una singola raccolta di elementi. Questa è la raccolta di input.

    map funziona con due raccolte di elementi: la raccolta di input e la raccolta di output.

Non è un errore correlare i due algoritmi: in effetti, è possibile visualizzarli gerarchicamente, dove mapè una specializzazione di foreach. Cioè, è possibile utilizzare foreache fare in modo che l'operazione trasformi il suo argomento e lo inserisca in un'altra raccolta. Quindi, l' foreachalgoritmo è un'astrazione, una generalizzazione, mapdell'algoritmo. In effetti, poiché foreachnon ha restrizioni sul suo funzionamento, possiamo tranquillamente affermare che foreachè il meccanismo di looping più semplice in circolazione e che può fare qualsiasi cosa un loop possa fare. map, così come altri algoritmi più specializzati, esiste per l'espressività: se desideri mappare (o trasformare) una raccolta in un'altra, la tua intenzione è più chiara se la usi mapche se la usi foreach.

Possiamo estendere ulteriormente questa discussione e considerare l' copyalgoritmo: un ciclo che clona una raccolta. Anche questo algoritmo è una specializzazione foreachdell'algoritmo. È possibile definire un'operazione che, dato un elemento, inserirà lo stesso elemento in un'altra raccolta. Se si utilizza foreachcon tale operazione, è stato effettivamente eseguito l' copyalgoritmo, sebbene con chiarezza, espressività o esplicitazione ridotte. Andiamo ancora oltre: possiamo dire che mapè una specializzazione di per copysé una specializzazione di foreach. mappuò cambiare uno qualsiasi degli elementi su cui scorre. Se mapnon cambia nessuno degli elementi, semplicemente copia gli elementi e li usa copia esprimerebbe l'intento più chiaramente.

L' foreachalgoritmo stesso può avere o meno un valore di ritorno, a seconda della lingua. In C ++, ad esempio, foreachrestituisce l'operazione originariamente ricevuta. L'idea è che l'operazione potrebbe avere uno stato e potresti voler ricambiare l'operazione per controllare come si è evoluta sugli elementi. mapanche, può o non può restituire un valore. In C ++ transform(l'equivalente per mapqui) capita di restituire un iteratore alla fine del contenitore di output (raccolta). In Ruby, il valore restituito di mapè la sequenza di output (raccolta). Quindi, il valore di ritorno degli algoritmi è davvero un dettaglio di implementazione; il loro effetto può o meno essere quello che restituiscono.


Per un esempio di come .forEach()può essere utilizzato per implementare .map(), vedere qui: stackoverflow.com/a/39159854/1524693
Chinoto Vokro

32

Array.protototype.mapmetodo e Array.protototype.forEachsono entrambi abbastanza simili.

Esegui il seguente codice: 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));
});

Danno il risultato esatto idem.

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

Ma la svolta arriva quando si esegue il seguente codice: -

Qui ho semplicemente assegnato il risultato del valore restituito dalla mappa e per ogni metodo.

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();

Ora il risultato è qualcosa di complicato!

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

Conclusione

Array.prototype.maprestituisce un array ma Array.prototype.forEachnon lo fa. Quindi è possibile manipolare l'array restituito all'interno della funzione di callback passata al metodo map e quindi restituirlo.

Array.prototype.forEach cammina solo attraverso l'array dato in modo che tu possa fare le tue cose mentre cammini nell'array.


11

la differenza più 'visibile' è che la mappa accumula il risultato in una nuova raccolta, mentre foreach viene fatto solo per l'esecuzione stessa.

ma ci sono un paio di ipotesi extra: poiché lo "scopo" della mappa è il nuovo elenco di valori, non importa davvero l'ordine di esecuzione. in effetti, alcuni ambienti di esecuzione generano codice parallelo, o addirittura introducono alcuni memoizing per evitare di chiamare valori ripetuti, o pigrizia, per evitare di chiamarli affatto.

foreach, d'altra parte, è chiamato specificamente per gli effetti collaterali; pertanto l'ordine è importante e di solito non può essere parallelizzato.


11

Risposta breve: map e forEachsono diversi. Inoltre, informalmente parlando, mapè un superset rigoroso di forEach.

Risposta lunga: in primo luogo, arriviamo a una riga di descrizioni di forEache map:

  • forEach scorre su tutti gli elementi, chiamando la funzione fornita su ciascuno di essi.
  • map scorre su tutti gli elementi, chiamando la funzione fornita su ciascuno e produce un array trasformato ricordando il risultato di ciascuna chiamata di funzione.

In molte lingue, forEachviene spesso chiamato solo each. La seguente discussione utilizza JavaScript solo come riferimento. Potrebbe davvero essere qualsiasi altra lingua.

Ora, usiamo ognuna di queste funzioni.

utilizzando forEach :

Attività 1: scrivere una funzione printSquares, che accetta una matrice di numeriarr e stampa il quadrato di ciascun elemento in essa.

Soluzione 1:

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

utilizzando map :

Attività 2: scrivere una funzione selfDot, che accetta una matrice di numeri arre restituisce una matrice in cui ogni elemento è il quadrato dell'elemento corrispondente inarr .

A parte: qui, in termini gergali, stiamo cercando di quadrare l'array di input. In termini formali, stiamo cercando di calcolare il suo prodotto punto con se stesso.

Soluzione 2:

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

Come è mapun superset diforEach ?

È possibile utilizzare mapper risolvere entrambe le attività, Attività 1 e Attività 2 . Tuttavia, non è possibile utilizzare forEachper risolvere l' attività 2 .

Nella soluzione 1 , se si sostituisce semplicemente forEachcon map, la soluzione sarà comunque valida. Nella soluzione 2 , tuttavia, la sostituzione mapcon forEachinterromperà la soluzione precedentemente funzionante.

Implementazione forEachin termini di map:

Un altro modo di realizzare mapla superiorità è implementare forEachin termini di map. Dato che siamo dei bravi programmatori, non ci abbandoneremo all'inquinamento dello spazio dei nomi. Chiameremo il nostro forEach, giusto each.

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

Ora, se non ti piace l' prototypeassurdità, ecco qui:

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

Quindi, umm .. Perché forEach esiste?

La risposta è efficienza. Se non sei interessato a trasformare un array in un altro array, perché dovresti calcolare l'array trasformato? Solo per scaricarlo? Ovviamente no! Se non vuoi una trasformazione, non dovresti fare una trasformazione.

Quindi, mentre la mappa può essere utilizzata per risolvere l' attività 1 , probabilmente non dovrebbe. Per ognuno è il candidato giusto per quello.


Risposta originale:

Mentre sono in gran parte d'accordo con la risposta di @madlep, vorrei sottolineare che si map()tratta di un super set rigoroso di forEach().

Sì, map()viene solitamente utilizzato per creare un nuovo array. Tuttavia, può anche essere utilizzato per modificare l'array corrente.

Ecco un esempio:

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!!"]

Nell'esempio sopra, è astato convenientemente impostato tale che a[i] === iper i < a.length. Anche così, dimostra il potere dimap() .

Ecco la descrizione ufficiale dimap() . Nota che map()potrebbe persino cambiare l'array su cui viene chiamato! Grandine map().

Spero che questo abbia aiutato.


A cura del 10-nov-2015: aggiunta elaborazione.


-1, poiché questo è valido solo con JavaScript; mentre la domanda parla specificamente di concetti di linguaggio agnostico e informatica, non di limitate implementazioni.
Javier

1
@Javier: Hmm .., devo essere d'accordo con te sulla mia risposta essendo JavaScript specifico. Ma chiediti questo: se una lingua avesse una mapfunzione nativa ma no forEach; non potresti semplicemente non usare mapinvece di forEach? D'altro canto, se una lingua avesse forEachma no map, dovresti implementare la tua map. Non puoi semplicemente usare forEachinvece di map. Ditemi cosa ne pensate.
Sumukh Barve,

3

Ecco un esempio in Scala usando gli elenchi: map restituisce l'elenco, foreach non restituisce nulla.

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

Quindi map restituisce l'elenco risultante dall'applicazione della funzione f a ciascun elemento dell'elenco:

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)

Foreach applica solo f a ciascun elemento:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

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

2

Se stai parlando di Javascript in particolare, la differenza è che mapè una funzione loop mentreforEach è un iteratore.

Uso map quando si desidera applicare un'operazione a ciascun membro dell'elenco e ottenere i risultati come nuovo elenco, senza influire sull'elenco originale.

Usa forEachquando vuoi fare qualcosa sulla base di ciascun elemento dell'elenco. Ad esempio, potresti aggiungere elementi alla pagina. Fondamentalmente, è ottimo per quando vuoi "effetti collaterali".

Altre differenze: forEachnon restituisce nulla (poiché è in realtà una funzione del flusso di controllo) e la funzione pass-in ottiene riferimenti all'indice e all'intero elenco, mentre map restituisce il nuovo elenco e passa solo l'elemento corrente.


0

Ciascuno cerca di applicare una funzione come scrivere su db ecc su ogni elemento del RDD senza restituire nulla.

Ma map()applica una funzione sugli elementi di rdd e restituisce il rdd. Quindi quando esegui il metodo seguente non fallirà alla riga 3 ma mentre raccogli il rdd dopo aver applicato foreach fallirà e genererà un errore che dice

File "<stdin>", riga 5, in <modulo>

AttributeError: l'oggetto 'NoneType' non ha alcun attributo '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())
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.