Чи є різниця між foreach та map?


218

Гаразд, це скоріше питання інформатики, ніж питання, засноване на певній мові, але чи є різниця між операцією на карті та операцією foreach? Або вони просто різні назви для однієї речі?


Цікаво, що якщо я отримую Iterator[String]від scala.io.Source.fromFile("/home/me/file").getLines()і застосувати .foreach(s => ptintln(s))до нього, він це спрацює , але порожніє відразу після. У той же час, якщо я звертаюся .map(ptintln(_))до нього - він просто порожній і нічого не друкується.
Іван

Відповіді:


294

Інший.

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

карта повторює список, перетворює кожного члена цього списку і повертає інший список такого ж розміру з перетвореними членами (наприклад, перетворення списку рядків у великі регістри)


Дякую, я подумав, що це щось із цього боку, але не був впевнений!
Роберт Гулд

19
але ви можете мати побічні ефекти мають місце у функції карти too.I як ця відповідь: stackoverflow.com/a/4927981/375688
TinyTimZamboni

6
Один важливий момент , щоб згадати (це особливо вірно в Scala) є те , що виклик mapне чинить , НЕ призводить до виконання лежачих в його основі логіки , поки коли очікуваний трансформуються список викликається. На відміну від цього, foreachоперація обчислюється негайно.
bigT

124

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


Дякую! Тепер я розумію різницю. Для мене це було давно туманно
Роберт Гулд

Також дуже важливо!
Маті Тернер

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алгоритм може мати або не мати поверненого значення, залежно від мови. Наприклад, у C ++ foreachповертає операцію, яку він отримав. Ідея полягає в тому, що операція може мати стан, і ви, можливо, захочете, щоб ця операція була назад, щоб перевірити, як вона розвивалася над елементами. mapтакож може повернути значення або не може. У C ++ transform(еквівалент mapтут) відбувається повернення ітератора до кінця вихідного контейнера (колекції). У Ruby повернене значення map- це вихідна послідовність (колекція). Отже, повернене значення алгоритмів - це насправді деталь реалізації; їх ефект може бути, а може бути і не тим, що вони повертають.


Як приклад того , як .forEach()можна використовувати для реалізації .map(), дивіться тут: stackoverflow.com/a/39159854/1524693
Chinoto Vokro

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 проходить лише через заданий масив, щоб ви могли робити свої речі під час прогулянки по масиву.


11

Найбільш «видима» різниця полягає в тому, що карта накопичує результат у новій колекції, тоді як передбачення робиться лише для самого виконання.

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

З іншого боку, 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 «s, я хотів би зазначити, що 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
@Javier: Хм .., я повинен погодитися з вами, щоб моя відповідь була специфічною для JavaScript. Але запитайте себе так: Якщо мова мала рідну mapфункцію, але ні forEach; ви не могли просто не використовувати mapзамість цього forEach? З іншого боку, якби мова мала, forEachале ні map, вам доведеться реалізувати свою власну map. Ви не можете просто використовувати forEachзамість цього map. Скажи мені, що ти думаєш.
Сумух Барве

3

Ось приклад у Scala із використанням списків: карта повертає список, foreach нічого не повертає.

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

Отже карта повертає список, отриманий в результаті застосування функції 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

2

Якщо ви говорите саме про Javascript, різниця полягає в тому, що mapце циклічна функціяforEach це ітератор.

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

Використовуйте, forEachколи хочете зробити щось на основі кожного елемента списку. Ви можете, наприклад, додавати речі на сторінку. По суті, це чудово підходить, коли ви хочете "побічні ефекти".

Інші відмінності: forEachнічого не повертає (оскільки це дійсно функція потоку управління), а функція, що передається, отримує посилання на індекс та весь список, тоді як карта повертає новий список і передає лише поточний елемент.


0

ForEach намагається застосувати таку функцію, як запис у db тощо на кожен елемент RDD, не повертаючи нічого назад.

Але map()застосовує деяку функцію над елементами rdd і повертає rdd. Отже, коли ви запускаєте наведений нижче метод, він не вийде з рядка 3, але при збиранні rdd після застосування foreach він вийде з ладу і видасть помилку, яка говорить

Файл "<stdin>", рядок 5, в <module>

AttributeError: У об’єкта "NoneType" немає атрибута "збирати"

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())
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.