Y a-t-il une différence entre foreach et map?

218

Ok, c'est plus une question d'informatique qu'une question basée sur une langue particulière, mais y a-t-il une différence entre une opération de carte et une opération foreach? Ou s'agit-il simplement de noms différents pour la même chose?

Robert Gould
la source
Curieusement, si j'obtiens un Iterator[String]de scala.io.Source.fromFile("/home/me/file").getLines()et que je l' applique .foreach(s => ptintln(s)), il est imprimé correctement mais se vide juste après. En même temps, si je l'applique .map(ptintln(_)), il devient vide et rien n'est imprimé.
Ivan

Réponses:

294

Différent.

foreach itère sur une liste et applique une opération avec des effets secondaires à chaque membre de la liste (comme l'enregistrement de chacun dans la base de données par exemple)

map itère sur une liste, transforme chaque membre de cette liste et renvoie une autre liste de la même taille avec les membres transformés (comme la conversion d'une liste de chaînes en majuscules)

madlep
la source
Merci, je pensais que c'était quelque chose dans ce sens, mais je n'en étais pas sûr!
Robert Gould
19
mais vous pourriez également avoir des effets secondaires dans la fonction de carte. J'aime cette réponse: stackoverflow.com/a/4927981/375688
TinyTimZamboni
6
Un point à mentionner importante (ce qui est particulièrement vrai dans Scala) est qu'un appel à mapne pas entraîner l'exécution de sa logique sous - jacente jusqu'au moment où la liste transformée attendu est convoqué. En revanche, foreachle fonctionnement de est calculé immédiatement.
bigT
124

La différence importante entre eux est qu'ils mapaccumulent tous les résultats dans une collection, alors qu'ils foreachne renvoient rien. mapest généralement utilisé lorsque vous souhaitez transformer une collection d'éléments avec une fonction, alors qu'il foreachexécute simplement une action pour chaque élément.

Henk
la source
Merci! Maintenant je comprends la différence. Cela avait été brumeux pour moi pendant un certain temps
Robert Gould
Très important aussi!
Мати Тернер
40

En bref, foreachc'est pour appliquer une opération sur chaque élément d'une collection d'éléments, alors mapc'est pour transformer une collection en une autre.

Il existe deux différences importantes entre foreachet map.

  1. foreachn'a aucune restriction conceptuelle sur l'opération qu'il applique, à part peut-être accepter un élément comme argument. Autrement dit, l'opération peut ne rien faire, peut avoir un effet secondaire, peut renvoyer une valeur ou peut ne pas retourner une valeur. Tout ce qui compte foreachest d'itérer sur une collection d'éléments et d'appliquer l'opération sur chaque élément.

    map, d'autre part, a une restriction sur l'opération: il s'attend à ce que l'opération retourne un élément, et accepte probablement aussi un élément comme argument. L' mapopération parcourt une collection d'éléments, en appliquant l'opération sur chaque élément, et enfin en stockant le résultat de chaque appel de l'opération dans une autre collection. En d'autres termes, le map transforme une collection en une autre.

  2. foreachfonctionne avec une seule collection d'éléments. Il s'agit de la collection d'entrées.

    map fonctionne avec deux collections d'éléments: la collection d'entrée et la collection de sortie.

Ce n'est pas une erreur de relier les deux algorithmes: en fait, vous pouvez voir les deux hiérarchiquement, où se maptrouve une spécialisation de foreach. Autrement dit, vous pouvez utiliser foreachet faire transformer l'opération par son argument et l'insérer dans une autre collection. Ainsi, l' foreachalgorithme est une abstraction, une généralisation de l' mapalgorithme. En fait, parce qu'il foreachn'y a aucune restriction sur son fonctionnement, nous pouvons dire en toute sécurité que foreachc'est le mécanisme de boucle le plus simple qui existe et qu'il peut faire tout ce qu'une boucle peut faire. map, ainsi que d'autres algorithmes plus spécialisés, sont là pour l'expressivité: si vous souhaitez mapper (ou transformer) une collection en une autre, votre intention est plus claire si vous utilisez mapque si vous utilisez foreach.

Nous pouvons approfondir cette discussion et considérer l' copyalgorithme: une boucle qui clone une collection. Cet algorithme est également une spécialisation de l' foreachalgorithme. Vous pouvez définir une opération qui, étant donné un élément, insérera ce même élément dans une autre collection. Si vous utilisez foreachcette opération, vous avez en fait exécuté l' copyalgorithme, mais avec une clarté, une expressivité ou une explication réduites. Allons plus loin: nous pouvons dire que mapc'est une spécialisation de copy, elle-même une spécialisation de foreach. mappeut modifier l' un des éléments sur lesquels il itère. Si mapne modifie aucun des éléments, il a simplement copié les éléments et utilisé la copie exprimerait l'intention plus clairement.

L' foreachalgorithme lui-même peut ou non avoir une valeur de retour, selon la langue. En C ++, par exemple, foreachrenvoie l'opération qu'il a initialement reçue. L'idée est que l'opération peut avoir un état, et vous souhaiterez peut-être que cette opération revienne sur son évolution au fil des éléments. mappeut également renvoyer une valeur. En C ++ transform(l'équivalent mapici) arrive à renvoyer un itérateur à la fin du conteneur de sortie (collection). Dans Ruby, la valeur de retour de mapest la séquence de sortie (collection). Ainsi, la valeur de retour des algorithmes est vraiment un détail d'implémentation; leur effet peut être ou non ce qu'ils retournent.

wilhelmtell
la source
Pour un exemple de la façon dont .forEach()peut être utilisé pour l'implémentation .map(), voir ici: stackoverflow.com/a/39159854/1524693
Chinoto Vokro
32

Array.protototype.mapméthode et Array.protototype.forEachsont tous deux assez similaires.

Exécutez le code suivant: 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));
});

Ils donnent le résultat idem exact.

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

Mais la torsion survient lorsque vous exécutez le code suivant: -

Ici, j'ai simplement attribué le résultat de la valeur de retour des méthodes map et 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();

Maintenant, le résultat est quelque chose de délicat!

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

Conclusion

Array.prototype.maprenvoie un tableau mais Array.prototype.forEachpas. Vous pouvez donc manipuler le tableau retourné à l'intérieur de la fonction de rappel passée à la méthode map, puis le renvoyer.

Array.prototype.forEach marche uniquement à travers le tableau donné afin que vous puissiez faire votre travail tout en parcourant le tableau.

abhisekp
la source
11

la différence la plus «visible» est que map accumule le résultat dans une nouvelle collection, tandis que foreach n'est fait que pour l'exécution elle-même.

mais il y a quelques hypothèses supplémentaires: puisque le «but» de map est la nouvelle liste de valeurs, peu importe l'ordre d'exécution. en fait, certains environnements d'exécution génèrent du code parallèle, ou même introduisent une certaine mémorisation pour éviter d'appeler des valeurs répétées, ou la paresse, pour éviter d'en appeler du tout.

foreach, d'autre part, est appelé spécifiquement pour les effets secondaires; par conséquent, l'ordre est important et ne peut généralement pas être mis en parallèle.

Javier
la source
11

Réponse courte: map et forEachsont différents. En outre, de manière informelle, mapest un surensemble strict de forEach.

Réponse longue: Tout d'abord, trouvons des descriptions d'une ligne de forEachet map:

  • forEach itère sur tous les éléments, appelant la fonction fournie sur chacun.
  • map itère sur tous les éléments, appelle la fonction fournie sur chacun et produit un tableau transformé en se souvenant du résultat de chaque appel de fonction.

Dans de nombreuses langues, forEachest souvent appelé juste each. La discussion suivante utilise JavaScript uniquement à titre de référence. Ce pourrait être n'importe quelle autre langue.

Maintenant, utilisons chacune de ces fonctions.

En utilisant forEach:

Tâche 1: écrivez une fonction printSquaresqui accepte un tableau de nombres arret imprime le carré de chaque élément qu'il contient.

Solution 1:

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

En utilisant map:

Tâche 2: écrire une fonction selfDotqui accepte un tableau de nombres arret renvoie un tableau dans lequel chaque élément est le carré de l'élément correspondant dans arr.

En plus: Ici, en termes d'argot, nous essayons de cadrer le tableau d'entrée. Formellement, nous essayons de calculer son produit scalaire avec lui-même.

Solution 2:

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

Comment est mapun surensemble de forEach?

Vous pouvez utiliser mappour résoudre les deux tâches, Tâche 1 et Tâche 2 . Cependant, vous ne pouvez pas utiliser forEachpour résoudre la tâche 2 .

Dans la solution 1 , si vous remplacez simplement forEachpar map, la solution sera toujours valide. Cependant, dans la solution 2 , le remplacement mappar forEachinterrompra votre solution qui fonctionnait auparavant.

Mise forEachen œuvre en termes de map:

Une autre façon de réaliser mapla supériorité de est de mettre forEachen œuvre en termes de map. Comme nous sommes de bons programmeurs, nous ne nous livrerons pas à la pollution de l'espace de noms. Nous appellerons notre forEach, juste each.

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

Maintenant, si vous n'aimez pas le prototypenon - sens, c'est parti:

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

Alors, euh .. Pourquoi ça forEachexiste même?

La réponse est l'efficacité. Si vous n'êtes pas intéressé par la transformation d'un tableau en un autre tableau, pourquoi devriez-vous calculer le tableau transformé? Seulement pour le vider? Bien sûr que non! Si vous ne voulez pas de transformation, vous ne devriez pas faire de transformation.

Ainsi, bien que la carte puisse être utilisée pour résoudre la tâche 1 , elle ne devrait probablement pas. Car chacun est le bon candidat pour cela.


Réponse originale:

Bien que je sois largement d'accord avec la réponse de @madlep, je voudrais souligner qu'il map()s'agit d'un super-ensemble strict de forEach().

Oui, map()est généralement utilisé pour créer un nouveau tableau. Cependant, il peut également être utilisé pour modifier le tableau actuel.

Voici un exemple:

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

Dans l'exemple ci-dessus, a aété commodément réglé de telle sorte que a[i] === ipour i < a.length. Même ainsi, cela démontre la puissance de map().

Voici la description officielle demap() . Notez que cela map()peut même changer le tableau sur lequel il est appelé! Salut map().

J'espère que cela vous a aidé.


Édité le 10 novembre 2015: ajout de l'élaboration.

Sumukh Barve
la source
-1, car cela n'est valable que sous javascript; tandis que la question traite spécifiquement de l'agnosticy du langage et des concepts informatiques, pas des implémentations limitées.
Javier
1
@Javier: Hmm .., je dois être d'accord avec vous sur le fait que ma réponse soit spécifique à JavaScript. Mais demandez-vous ceci: si une langue avait un natifmap fonction mais non forEach; ne pourriez-vous pas simplement ne pas utiliser à la mapplace de forEach? D'un autre côté, si une langue avait forEachmais non map, vous devez implémenter la vôtre map. Vous ne pouvez pas simplement utiliser à la forEachplace de map. Dis-moi ce que tu penses.
Sumukh Barve
3

Voici un exemple dans Scala utilisant des listes: la carte renvoie la liste, foreach ne renvoie rien.

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

Ainsi, map renvoie la liste résultant de l'application de la fonction f à chaque élément de la liste:

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 applique simplement f à chaque élément:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

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

Si vous parlez de Javascript en particulier, la différence est qu'il maps'agit d'une fonction de boucle et d' forEachun itérateur.

À utiliser maplorsque vous souhaitez appliquer une opération à chaque membre de la liste et récupérer les résultats en tant que nouvelle liste, sans affecter la liste d'origine.

À utiliser forEachlorsque vous voulez faire quelque chose sur la base de chaque élément de la liste. Vous pourriez ajouter des éléments à la page, par exemple. Essentiellement, c'est génial quand vous voulez des "effets secondaires".

Autres différences: forEachne renvoie rien (car il s'agit vraiment d'une fonction de flux de contrôle), et la fonction transmise obtient des références à l'index et à la liste entière, tandis que map renvoie la nouvelle liste et ne passe que dans l'élément actuel.

traitez bien vos mods
la source
0

ForEach essaie d'appliquer une fonction telle que l'écriture dans db etc sur chaque élément du RDD sans rien renvoyer.

Mais le map()applique une fonction sur les éléments de rdd et renvoie le rdd. Ainsi, lorsque vous exécutez la méthode ci-dessous, elle n'échouera pas à la ligne 3, mais lors de la collecte du rdd après avoir appliqué foreach, elle échouera et générera une erreur qui dit

Fichier "<stdin>", ligne 5, dans <module>

AttributeError: l'objet 'NoneType' n'a pas d'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
la source