Есть ли разница между foreach и map?

218

Хорошо, это больше вопрос информатики, чем вопрос, основанный на конкретном языке, но есть ли разница между операцией отображения и операцией foreach? Или это просто разные имена для одной и той же вещи?

Роберт Гулд
источник
Любопытно, что если я получаю Iterator[String]от scala.io.Source.fromFile("/home/me/file").getLines()и применить .foreach(s => ptintln(s))к нему, он печатается нормально , но пустеет сразу после. В то же время, если я обращаюсь .map(ptintln(_))к нему - он просто становится пустым и ничего не печатается.
Иван

Ответы:

294

Разные.

foreach выполняет итерацию по списку и применяет некоторые операции с побочными эффектами к каждому члену списка (например, сохранение каждого из них в базе данных)

Карта выполняет итерацию по списку, преобразует каждого члена этого списка и возвращает другой список того же размера с преобразованными элементами (например, преобразование списка строк в верхний регистр)

madlep
источник
Спасибо, я думал, что это что-то в этом роде, но не был уверен!
Роберт Гулд
19
но у вас также могут возникнуть побочные эффекты в функции карты. Мне нравится этот ответ: stackoverflow.com/a/4927981/375688
TinyTimZamboni
6
Один важный момент , чтобы упомянуть (это особенно верно в Scala) является то , что вызов mapне делает , не приводит к исполнению лежащих в его основе логики , пока когда ожидаемый трансформируются список вызывается. В отличие от этого, foreachоперация вычисляется немедленно.
bigT
124

Важное различие между ними заключается в том, что mapвсе результаты накапливаются в коллекции, а foreachничего не возвращается. mapобычно используется, когда вы хотите преобразовать коллекцию элементов с помощью функции, тогда как foreachпросто выполняет действие для каждого элемента.

Henk
источник
Спасибо! Теперь я понимаю разницу. Это было довольно туманным для меня довольно долгое время
Роберт Гулд
Также очень важно!
Мати Тернер
40

Короче говоря, foreachдля применения операции к каждому элементу коллекции элементов, а mapдля преобразования одной коллекции в другую.

Есть два существенных различия между foreachи map.

  1. foreachне имеет концептуальных ограничений на операцию, которую он применяет, кроме, возможно, принятия элемента в качестве аргумента То есть операция может ничего не делать, может иметь побочный эффект, может возвращать значение или может не возвращать значение. Все, foreachчто нужно, - это перебрать коллекцию элементов и применить операцию к каждому элементу.

    mapс другой стороны, имеет ограничение на операцию: она ожидает, что операция вернет элемент, и, вероятно, также примет элемент в качестве аргумента. mapОперации перебирает коллекцию элементов, применяя операцию на каждом элементе, и , наконец , сохранение результата каждого вызова операции в другой коллекции. Другими словами, map превращает одну коллекцию в другую.

  2. foreachработает с одной коллекцией элементов. Это входная коллекция.

    map работает с двумя коллекциями элементов: входной набор и выходной набор.

Не является ошибкой связывать два алгоритма: на самом деле, вы можете рассматривать два иерархически, где mapесть специализация foreach. То есть вы могли бы использовать foreachэту операцию, чтобы преобразовать ее аргумент и вставить его в другую коллекцию. Таким образом, foreachалгоритм является абстракцией, обобщением mapалгоритма. Фактически, поскольку foreachон не имеет ограничений по своей работе, мы можем с уверенностью сказать, что foreachэто самый простой механизм зацикливания, и он может делать все, что может делать цикл. mapКак и другие более специализированные алгоритмы, есть для выразительности: если вы хотите отобразить (или преобразовать) одну коллекцию в другую, ваше намерение будет более ясным, если вы используете, mapчем если бы вы использовали foreach.

Мы можем продолжить это обсуждение и рассмотреть copyалгоритм: цикл, который клонирует коллекцию. Этот алгоритм тоже является специализацией foreachалгоритма. Вы можете определить операцию, которая при наличии элемента будет вставлять этот же элемент в другую коллекцию. Если вы используете foreachэту операцию, вы фактически выполняете copyалгоритм, хотя и с меньшей ясностью, выразительностью или ясностью. Давайте сделаем это еще дальше: мы можем сказать, что mapэто специализация copy, сама специализацияforeach . mapможет изменить любой из элементов, которые он повторяет. Если mapне изменяет ни один из элементов, он просто копирует элементы и использует копию выразит намерение более четко.

Сам foreachалгоритм может иметь или не иметь возвращаемое значение, в зависимости от языка. Например, foreachв C ++ возвращает первоначально полученную операцию. Идея состоит в том, что операция может иметь состояние, и вы можете захотеть, чтобы эта операция вернулась, чтобы проверить, как она развивалась над элементами. mapтоже может возвращать или не возвращать значение. В C ++ transform(здесь эквивалент map) происходит возврат итератора в конец контейнера вывода (коллекции). В Ruby возвращаемое значение mapявляется выходной последовательностью (коллекцией). Таким образом, возвращаемое значение алгоритмов действительно является деталью реализации; их эффект может быть или не быть тем, что они возвращают.

wilhelmtell
источник
Пример того, как .forEach()можно использовать для реализации .map(), см. Здесь: stackoverflow.com/a/39159854/1524693
Чиното Вокро,
32

Array.protototype.mapМетод & Array.protototype.forEachочень похожи.

Запустите следующий код: 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));
});

Они дают точный тот же результат.

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

Но поворот возникает, когда вы запускаете следующий код:

Здесь я просто присвоил результат возвращаемого значения из карты и методы 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();

Теперь результат чего-то сложного!

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

Вывод

Array.prototype.mapвозвращает массив, но Array.prototype.forEachне делает. Таким образом, вы можете манипулировать возвращенным массивом внутри функции обратного вызова, переданной методу map, а затем вернуть его.

Array.prototype.forEach только проходит через данный массив, так что вы можете делать свои вещи во время обхода массива.

abhisekp
источник
11

самое «видимое» отличие состоит в том, что map накапливает результат в новой коллекции, тогда как foreach выполняется только для самого выполнения.

но есть пара дополнительных предположений: поскольку «цель» карты - это новый список значений, на самом деле не имеет значения порядок выполнения. на самом деле, некоторые среды выполнения генерируют параллельный код или даже вводят некоторую запоминание, чтобы не вызывать повторяющиеся значения, или лень, чтобы вообще не вызывать некоторые.

foreach, с другой стороны, специально предназначен для побочных эффектов; поэтому порядок важен и обычно не может быть распараллелен.

Хавьер
источник
11

Краткий ответ: map и forEachразные. Также, неофициально говоря, mapэто строгий надмножество forEach.

Длинный ответ: во- первых, давайте придумаем одну строку описания forEachи map:

  • forEach перебирает все элементы, вызывая предоставленную функцию для каждого элемента.
  • map перебирает все элементы, вызывая предоставленную функцию для каждого, и создает преобразованный массив, запоминая результат каждого вызова функции.

На многих языках forEachчасто называют простоeach . Следующее обсуждение использует JavaScript только для справки. Это действительно может быть любой другой язык.

Теперь давайте используем каждую из этих функций.

С помощью forEach :

Задача 1: написать функциюprintSquares , которая принимает массив чисел arrи печатает квадрат каждого элемента в нем.

Решение 1:

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

С помощью map :

Задача 2: Напишите функцию selfDot, которая принимает массив чиселarr и возвращает массив, в котором каждый элемент является квадратом соответствующего элемента в arr.

В сторону: здесь, в сленговом выражении, мы пытаемся выровнять входной массив. Формально говоря, мы пытаемся вычислить это точечное произведение с самим собой.

Решение 2:

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

Как mapэто суперсетforEach ?

Вы можете использовать mapдля решения обеих задач, Задача 1 и Задача 2 . Тем не менее, вы не можете использовать forEachдля решения задачи 2 .

В решении 1 , если вы просто замените forEachего map, решение все равно будет действовать. Однако в решении 2 замена mapна forEachсломает ранее работавшее решение.

Реализация forEachс точки зрения map:

Еще один способ реализации mapпревосходства России заключается в реализации forEachс точки зрения map. Поскольку мы хорошие программисты, мы не будем баловаться загрязнением пространства имен. Мы позвоним нашим forEach, просто each.

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

Теперь, если вам не нравится prototypeерунда, вот и вы:

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

Итак ... почему forEach существует?

Ответ - эффективность. Если вы не заинтересованы в преобразовании массива в другой массив, зачем вам вычислять преобразованный массив? Только чтобы свалить это? Конечно нет! Если вы не хотите преобразование, вы не должны делать преобразование.

Таким образом, хотя карта может быть использована для решения задачи 1 , она, вероятно, не должна. Для каждого это правильный кандидат на это.


Оригинальный ответ:

Хотя я в основном согласен с ответом @madlep, я хотел бы отметить, что map()это строгий супер-набор из forEach().

Да, map() обычно используется для создания нового массива. Тем не менее, он также может быть использован для изменения текущего массива.

Вот пример:

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

В приведенном выше примере aбыло удобно установить так, чтобыa[i] === i для i < a.length. Несмотря на это, он демонстрирует силуmap() .

Вот официальное описаниеmap() . Обратите внимание, чтоmap() может даже изменить массив, на котором он вызывается! Радуйся map().

Надеюсь, это помогло.


Отредактировано 10 ноября 2015: добавлена ​​разработка.

Сумух Барве
источник
-1, так как это допустимо только в javascript; в то время как вопрос конкретно говорит о понятиях языковой агностики и информатики, а не об ограниченных реализациях.
Хавьер
1
@ Хавьер: Хм .., я должен согласиться с тобой, что мой ответ специфичен для JavaScript. Но спросите себя: если у языка есть нативная mapфункция, но нет forEach; не могли бы вы просто не использовать mapвместо forEach? С другой стороны, если у языка есть, forEachно нет map, вам придется реализовать свой собственный map. Вы не могли бы просто использовать forEachвместо map. Скажи мне, что ты думаешь.
Сумух Барв
3

Вот пример использования Scala в Scala: map возвращает список, foreach ничего не возвращает.

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

Таким образом, map возвращает список, полученный в результате применения функции f к каждому элементу списка:

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 просто применяет f к каждому элементу:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

scala> sum
res2: Int = 6 // res1 is empty
irudyak
источник
2

Если вы говорите о Javascript в частности, разница в том, что mapфункцияforEach итератор.

Используйте, mapесли вы хотите применить операцию к каждому члену списка и вернуть результаты в виде нового списка, не затрагивая первоначальный список.

Используйте, forEachкогда вы хотите что- то сделать на основе каждого элемента списка. Вы можете добавлять вещи на страницу, например. По сути, это здорово, когда вам нужны «побочные эффекты».

Другие отличия: forEachничего не возвращает (поскольку это действительно функция потока управления), и переданная функция получает ссылки на индекс и весь список, тогда как map возвращает новый список и передает только текущий элемент.

хорошо относиться к своим модам
источник
0

ForEach пытается применить функцию, такую ​​как запись в db и т. Д., К каждому элементу СДР, не возвращая ничего обратно.

Но map()применяется некоторая функция над элементами rdd и возвращает rdd. Поэтому, когда вы запустите приведенный ниже метод, он не завершится с ошибкой в ​​строке 3, но при сборе rdd после применения foreach он завершится ошибкой и выдаст ошибку, которая говорит:

Файл "<stdin>", строка 5, в <module>

AttributeError: у объекта 'NoneType' нет атрибута '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
источник