Gibt es einen Unterschied zwischen foreach und map?

218

Ok, dies ist eher eine Informatikfrage als eine Frage, die auf einer bestimmten Sprache basiert. Gibt es jedoch einen Unterschied zwischen einer Kartenoperation und einer Foreach-Operation? Oder sind es einfach unterschiedliche Namen für dasselbe?

Robert Gould
quelle
Seltsamerweise wird es gedruckt , wenn ich ein Iterator[String]von bekomme scala.io.Source.fromFile("/home/me/file").getLines()und mich darauf .foreach(s => ptintln(s))bewerbe, aber es wird gleich danach leer. Zur gleichen Zeit, wenn ich mich darauf bewerbe .map(ptintln(_))- es wird einfach leer und nichts wird gedruckt.
Ivan

Antworten:

294

Anders.

foreach iteriert über eine Liste und wendet auf jedes Listenmitglied eine Operation mit Nebenwirkungen an (z. B. Speichern jeder in der Datenbank).

map iteriert über eine Liste, transformiert jedes Mitglied dieser Liste und gibt eine andere Liste derselben Größe mit den transformierten Mitgliedern zurück (z. B. Konvertieren einer Liste von Zeichenfolgen in Großbuchstaben).

Madlep
quelle
Danke, ich dachte, es wäre etwas in diese Richtung, war mir aber nicht sicher!
Robert Gould
19
Aber Sie könnten auch in der Kartenfunktion Nebenwirkungen haben. Ich mag diese Antwort: stackoverflow.com/a/4927981/375688
TinyTimZamboni
6
Ein wichtiger Punkt zu erwähnen (dies gilt insbesondere in Scala) ist , dass ein Aufruf mapist nicht in der Ausführung ihrer zugrundeliegenden Logik zur Folge haben, bis , wenn die erwartete transformierte Liste beschworen wird. Im Gegensatz dazu wird foreachdie Operation sofort berechnet.
BigT
124

Der wichtige Unterschied zwischen ihnen besteht darin, dass mapalle Ergebnisse in einer Sammlung zusammengefasst werden, während foreachnichts zurückgegeben wird. mapwird normalerweise verwendet, wenn Sie eine Sammlung von Elementen mit einer Funktion transformieren möchten, während foreacheinfach eine Aktion für jedes Element ausgeführt wird.

Henk
quelle
Vielen Dank! Jetzt verstehe ich den Unterschied. Es war schon seit einiger Zeit dunstig für mich
Robert Gould
Auch sehr wichtig!
20ати Тернер
40

Kurz gesagt, foreachdient zum Anwenden einer Operation auf jedes Element einer Sammlung von Elementen, während mapzum Transformieren einer Sammlung in eine andere.

Es gibt zwei signifikante Unterschiede zwischen foreachund map.

  1. foreachhat keine konzeptionellen Einschränkungen für die Operation, die angewendet wird, außer vielleicht ein Element als Argument zu akzeptieren. Das heißt, die Operation kann nichts tun, kann eine Nebenwirkung haben, kann einen Wert zurückgeben oder kann keinen Wert zurückgeben. Allesforeach tun müssen, ist, eine Sammlung von Elementen zu durchlaufen und die Operation auf jedes Element anzuwenden.

    mapAuf der anderen Seite gibt es eine Einschränkung für die Operation: Sie erwartet, dass die Operation ein Element zurückgibt und wahrscheinlich auch ein Element als Argument akzeptiert. Die mapOperation durchläuft eine Sammlung von Elementen, wendet die Operation auf jedes Element an und speichert schließlich das Ergebnis jedes Aufrufs der Operation in einer anderen Sammlung. Mit anderen Worten, das map transformiert eine Sammlung in eine andere.

  2. foreacharbeitet mit einer einzigen Sammlung von Elementen. Dies ist die Eingabesammlung.

    map arbeitet mit zwei Sammlungen von Elementen: der Eingabesammlung und der Ausgabesammlung.

Es ist kein Fehler, die beiden Algorithmen in Beziehung zu setzen: Tatsächlich können Sie die beiden Algorithmen hierarchisch anzeigen, wobei mapeine Spezialisierung von vorliegt foreach. Das heißt, Sie können foreachdas Argument der Operation verwenden und transformieren und in eine andere Sammlung einfügen. Der foreachAlgorithmus ist also eine Abstraktion, eine Verallgemeinerung des mapAlgorithmus. Da foreachder Betrieb nicht eingeschränkt ist, können wir mit Sicherheit sagen, dass dies foreachder einfachste Schleifenmechanismus auf dem Markt ist und alles kann, was eine Schleife tun kann. mapWie auch andere, speziellere Algorithmen dienen der Ausdruckskraft: Wenn Sie eine Sammlung in eine andere abbilden (oder transformieren) möchten, ist Ihre Absicht bei Verwendung klarer mapals bei Verwendung foreach.

Wir können diese Diskussion weiter ausdehnen und den copyAlgorithmus betrachten: eine Schleife, die eine Sammlung klont. Auch dieser Algorithmus ist eine Spezialisierung des foreachAlgorithmus. Sie können eine Operation definieren, die bei einem bestimmten Element dasselbe Element in eine andere Sammlung einfügt. Wenn Sie foreachdiese Operation verwenden, haben Sie den copyAlgorithmus tatsächlich ausgeführt, allerdings mit reduzierter Klarheit, Ausdruckskraft oder Aussagekraft. Gehen wir noch weiter: Wir können sagen, dass dies mapeine Spezialisierung von copy, selbst eine Spezialisierung von ist foreach. mapkann jedes der Elemente ändern, über die es iteriert. Wenn mapkeines der Elemente geändert wird, werden lediglich die Elemente kopiert und verwendet Kopie verwendet würde die Absicht klarer ausdrücken.

Der foreachAlgorithmus selbst kann abhängig von der Sprache einen Rückgabewert haben oder nicht. Gibt in C ++ beispielsweise foreachdie ursprünglich empfangene Operation zurück. Die Idee ist, dass die Operation möglicherweise einen Status hat, und Sie möchten möglicherweise, dass diese Operation überprüft, wie sie sich über die Elemente entwickelt hat. mapkann auch einen Wert zurückgeben oder nicht. In C ++ transform(das Äquivalent für maphier) wird zufällig ein Iterator an das Ende des Ausgabecontainers (Sammlung) zurückgegeben. In Ruby ist der Rückgabewert von mapdie Ausgabesequenz (Sammlung). Der Rückgabewert der Algorithmen ist also wirklich ein Implementierungsdetail. Ihre Wirkung kann oder kann nicht das sein, was sie zurückgeben.

wilhelmtell
quelle
Ein Beispiel für .forEach()die Implementierung .map()finden Sie hier: stackoverflow.com/a/39159854/1524693
Chinoto Vokro
32

Array.protototype.mapMethode & Array.protototype.forEachsind beide ziemlich ähnlich.

Führen Sie den folgenden Code aus: 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));
});

Sie geben genau das gleiche Ergebnis.

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

Aber die Wendung kommt, wenn Sie den folgenden Code ausführen: -

Hier habe ich einfach das Ergebnis des Rückgabewerts aus den Methoden map und forEach zugewiesen.

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

Jetzt ist das Ergebnis etwas knifflig!

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

Fazit

Array.prototype.mapgibt ein Array zurück, Array.prototype.forEachtut es aber nicht. Sie können also das zurückgegebene Array innerhalb der an die Map-Methode übergebenen Rückruffunktion bearbeiten und dann zurückgeben.

Array.prototype.forEach Geht nur durch das angegebene Array, damit Sie Ihre Arbeit erledigen können, während Sie durch das Array gehen.

abhisekp
quelle
11

Der sichtbarste Unterschied besteht darin, dass die Karte das Ergebnis in einer neuen Sammlung sammelt, während foreach nur für die Ausführung selbst durchgeführt wird.

Es gibt jedoch einige zusätzliche Annahmen: Da der 'Zweck' der Karte die neue Liste von Werten ist, spielt die Reihenfolge der Ausführung keine Rolle. In der Tat generieren einige Ausführungsumgebungen parallelen Code oder führen sogar Memoizing ein, um das Aufrufen wiederholter Werte oder Faulheit zu vermeiden, um das Aufrufen von Werten überhaupt zu vermeiden.

foreach hingegen wird speziell für die Nebenwirkungen genannt; Daher ist die Reihenfolge wichtig und kann normalerweise nicht parallelisiert werden.

Javier
quelle
11

Kurze Antwort: map und forEachsind anders. Auch informell gesehen mapist eine strikte Obermenge von forEach.

Lange Antwort: Lassen Sie uns zunächst eine einzeilige Beschreibung von forEachund erstellen map:

  • forEach iteriert über alle Elemente und ruft die jeweils bereitgestellte Funktion auf.
  • map iteriert über alle Elemente, ruft die angegebene Funktion für jedes Element auf und erzeugt ein transformiertes Array, indem das Ergebnis jedes Funktionsaufrufs gespeichert wird.

In vielen Sprachen forEachwird oft nur genannteach . In der folgenden Diskussion wird JavaScript nur als Referenz verwendet. Es könnte wirklich jede andere Sprache sein.

Lassen Sie uns nun jede dieser Funktionen verwenden.

Verwenden von forEach:

Aufgabe 1: Schreiben Sie eine Funktion printSquares, die ein Array von Zahlen akzeptiert arrund das Quadrat jedes Elements darin druckt.

Lösung 1:

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

Verwenden von map:

Aufgabe 2: Schreiben Sie eine Funktion selfDot, die ein Array von Zahlen akzeptiert und ein Array arrzurückgibt, in dem jedes Element das Quadrat des entsprechenden Elements in istarr .

Nebenbei: Hier versuchen wir in Slang-Begriffen, das Eingabearray zu quadrieren. Formal ausgedrückt versuchen wir, das Punktprodukt mit sich selbst zu berechnen.

Lösung 2:

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

Wie ist mapeine Obermenge von forEach?

Sie können mapbeide Aufgaben, Aufgabe 1 und Aufgabe 2 , lösen . Sie können forEachdie Aufgabe 2 jedoch nicht verwenden .

In Lösung 1 , wenn Sie einfach ersetzen forEachdurch map, wird die Lösung noch gültig sein. In Lösung 2 wird Ihre zuvor funktionierende Lösung jedoch durch Ersetzen mapdurch forEachbeschädigt.

Implementierung forEachin Bezug auf map:

Eine andere Möglichkeit, mapdie Überlegenheit zu erkennen , ist die Umsetzung forEachin Bezug auf map. Da wir gute Programmierer sind, werden wir uns nicht der Verschmutzung von Namespaces hingeben. Wir werden unsere forEacheinfach anrufen each.

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

Wenn Sie den prototypeUnsinn nicht mögen , können Sie loslegen:

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

Also, ähm ... warum? forEach überhaupt?

Die Antwort lautet Effizienz. Wenn Sie nicht daran interessiert sind, ein Array in ein anderes Array umzuwandeln, warum sollten Sie das transformierte Array berechnen? Nur um es wegzuwerfen? Natürlich nicht! Wenn Sie keine Transformation wünschen, sollten Sie keine Transformation durchführen.

Während Map zur Lösung von Aufgabe 1 verwendet werden kann , sollte dies wahrscheinlich nicht der Fall sein. Für jeden ist der richtige Kandidat dafür.


Ursprüngliche Antwort:

Obwohl ich der Antwort von @madlep weitgehend zustimme, möchte ich darauf hinweisen, dass dies map()eine strikte Supermenge von ist forEach().

Ja, map()wird normalerweise verwendet, um ein neues Array zu erstellen. Es kann jedoch auch verwendet werden, um das aktuelle Array zu ändern.

Hier ist ein Beispiel:

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

Im obigen Beispiel awurde zweckmäßigerweise so eingestellt, dass a[i] === iz i < a.length. Trotzdem zeigt es die Kraft von map().

Hier ist die offizielle Beschreibung vonmap() . Beachten Sie, dass dies map()sogar das Array ändern kann, auf dem es aufgerufen wird! Hagelmap() .

Hoffe das hat geholfen.


Bearbeitet am 10.11.2015: Ausarbeitung hinzugefügt.

Sumukh Barve
quelle
-1, da dies nur unter Javascript gültig ist; Während die Frage speziell über Sprachunabhängigkeit und Informatikkonzepte spricht, nicht über eingeschränkte Implementierungen.
Javier
1
@ Javier: Hmm .., ich muss dir zustimmen, dass meine Antwort JavaScript-spezifisch ist. Aber fragen Sie sich Folgendes: Wenn eine Sprache eine native mapFunktion hätte, aber nein forEach; Könntest du nicht einfach nicht verwenden mapanstatt forEach? Auf der anderen Seite, wenn eine Sprache forEachaber nein hätte map, müssten Sie Ihre eigene implementieren map. Sie könnten nicht einfach forEachanstelle von verwenden map. Sag mir was du denkst.
Sumukh Barve
3

Hier ist ein Beispiel in Scala mit Listen: Map gibt Liste zurück, foreach gibt nichts zurück.

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

Map gibt also die Liste zurück, die sich aus der Anwendung der Funktion f auf jedes Listenelement ergibt:

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 wendet nur f auf jedes Element an:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

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

Wenn Sie insbesondere über Javascript sprechen, besteht der Unterschied darin, dass mapes sich dabei um eine Schleifenfunktion handeltforEach es sich um einen Iterator handelt.

Verwenden mapSie diese Option, wenn Sie auf jedes Mitglied der Liste eine Operation anwenden und die Ergebnisse als neue Liste zurückerhalten möchten, ohne die ursprüngliche Liste zu beeinflussen.

Verwenden forEachSie diese Option, wenn Sie anhand der einzelnen Elemente der Liste etwas tun möchten . Sie können der Seite beispielsweise Dinge hinzufügen. Im Wesentlichen ist es ideal, wenn Sie "Nebenwirkungen" wollen.

Andere Unterschiede: forEachGibt nichts zurück (da es sich wirklich um eine Kontrollflussfunktion handelt), und die übergebene Funktion erhält Verweise auf den Index und die gesamte Liste, während map die neue Liste zurückgibt und nur das aktuelle Element übergibt.

Behandle deine Mods gut
quelle
0

ForEach versucht, eine Funktion wie das Schreiben in db usw. auf jedes Element der RDD anzuwenden, ohne etwas zurückzugeben.

Aber das map()wendet eine Funktion auf die Elemente von rdd an und gibt das rdd zurück. Wenn Sie also die folgende Methode ausführen, schlägt sie in Zeile 3 nicht fehl, aber beim Sammeln der Festplatte nach dem Anwenden von foreach schlägt sie fehl und es wird ein Fehler ausgegeben, der besagt

Datei "<stdin>", Zeile 5, in <Modul>

AttributeError: Das Objekt 'NoneType' hat kein Attribut '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
quelle