Czy istnieje różnica między Foreach a mapą?


218

Ok, jest to raczej pytanie informatyczne niż pytanie oparte na konkretnym języku, ale czy istnieje różnica między operacją mapowania a operacją foreach? A może są to po prostu różne nazwy dla tej samej rzeczy?


Co ciekawe, jeśli dostanę Iterator[String]od scala.io.Source.fromFile("/home/me/file").getLines()i zastosuję się .foreach(s => ptintln(s))do niego, zostanie wydrukowane dobrze, ale zaraz potem się opróżni. Jednocześnie, jeśli się .map(ptintln(_))o to ubiegam - po prostu robi się pusty i nic nie jest drukowane.
Ivan

Odpowiedzi:


294

Różne.

foreach wykonuje iterację po liście i stosuje pewne operacje z efektami ubocznymi do każdego członka listy (na przykład zapisując każdego z nich w bazie danych)

map iteruje listę, przekształca każdy element tej listy i zwraca kolejną listę o tym samym rozmiarze z transformowanymi elementami (np. konwersję listy ciągów na wielkie litery)


Dzięki, myślałem, że to coś w tym stylu, ale nie byłem pewien!
Robert Gould,

19
ale mogą wystąpić efekty uboczne również w funkcji mapy. Podoba mi się ta odpowiedź: stackoverflow.com/a/4927981/375688
TinyTimZamboni 10.10

6
Jeden ważny punkt wspomnieć (jest to szczególnie prawdziwe w Scala) jest to, że wywołanie mapma nie doprowadzić do realizacji swojej podstawowej logiki aż po przekształcona lista spodziewane jest wezwany. Natomiast foreachoperacja jest obliczana natychmiast.
bigT

124

Ważna różnica między nimi polega na tym, że mapkumuluje wszystkie wyniki w kolekcji, a foreachnic nie zwraca. mapjest zwykle używany, gdy chcesz przekształcić zbiór elementów za pomocą funkcji, podczas gdy foreachpo prostu wykonuje akcję dla każdego elementu.


Dzięki! Teraz rozumiem różnicę. To było dla mnie mętne od dłuższego czasu
Robert Gould

Również bardzo ważne!
Мати Тернер

40

W skrócie, foreachsłuży do zastosowania operacji na każdym elemencie zbioru elementów, podczas gdy mapsłuży do przekształcenia jednej kolekcji w drugą.

Istnieją dwie znaczące różnice między foreachi map.

  1. foreachnie ma pojęciowych ograniczeń dotyczących stosowanej operacji, poza tym, że może przyjąć element jako argument. Oznacza to, że operacja może nic nie robić, może mieć efekt uboczny, może zwrócić wartość lub może nie zwrócić wartości. Wszystkie foreachdba o to, aby iteracyjne nad zbiór elementów i zastosować operację na każdym elemencie.

    mapz drugiej strony ma ograniczenie operacji: oczekuje, że operacja zwróci element i prawdopodobnie również zaakceptuje element jako argument. mapDziałanie iteracje przez zbiór elementów, stosowanie operacji na każdym elemencie i na koniec przechowywanie wyniku każdego wywołania działania do innego zbioru. Innymi słowy, map przekształca jedną kolekcję w drugą.

  2. foreachwspółpracuje z pojedynczą kolekcją elementów. To jest kolekcja danych wejściowych.

    map współpracuje z dwoma kolekcjami elementów: kolekcją wejściową i kolekcją wyjściową.

Odwoływanie się do dwóch algorytmów nie jest błędem: w rzeczywistości można je zobaczyć hierarchicznie, gdzie mapjest specjalizacja foreach. Oznacza to, że możesz użyć foreachoperacji i przekształcić jej argument i wstawić go do innej kolekcji. Tak więc, foreachalgorytm jest abstrakcją, uogólnienie, z mapalgorytmu. W rzeczywistości, ponieważ foreachnie ma ograniczeń w jego działaniu, możemy śmiało powiedzieć, że foreachjest to najprostszy dostępny mechanizm zapętlania i może on wykonywać wszystko, co może zrobić pętla. map, podobnie jak inne bardziej wyspecjalizowane algorytmy, służy ekspresji: jeśli chcesz zmapować (lub przekształcić) jedną kolekcję w inną, twoja intencja jest wyraźniejsza, jeśli używasz, mapniż używasz foreach.

Możemy rozszerzyć tę dyskusję i rozważyć copyalgorytm: pętlę, która klonuje kolekcję. Ten algorytm też jest specjalizacją foreachalgorytmu. Możesz zdefiniować operację, która przy danym elemencie wstawi ten sam element do innej kolekcji. Jeśli korzystasz foreachz tej operacji, w rzeczywistości wykonałeś copyalgorytm, choć ze zmniejszoną jasnością, ekspresją lub wyraźnością. Przejdźmy jeszcze dalej: możemy powiedzieć, że mapjest to specjalizacja copysama w sobie foreach. mapmoże zmienić dowolny z iterowanych elementów. Jeśli mapnie zmienia żadnego z elementów, to po prostu skopiował elementy i używał kopiowania wyraźniej wyraziłby to zamiar.

Sam foreachalgorytm może, ale nie musi, mieć wartość zwracaną, w zależności od języka. Na przykład w C ++ foreachzwraca pierwotnie otrzymaną operację. Chodzi o to, że operacja może mieć stan i możesz chcieć, aby ta operacja wróciła, aby sprawdzić, jak ewoluowała nad elementami. map, również może, ale nie musi, zwrócić wartość. W C ++ transform(odpowiednik maptutaj) zdarza się, że zwraca iterator na końcu kontenera wyjściowego (kolekcji). W Ruby zwracaną wartością mapjest sekwencja wyjściowa (kolekcja). Zatem zwracana wartość algorytmów jest naprawdę szczegółem implementacji; ich efekt może, ale nie musi, być tym, co zwracają.


Przykład zastosowania tej metody .forEach()można .map()znaleźć tutaj: stackoverflow.com/a/39159854/1524693
Chinoto Vokro

32

Array.protototype.mapmetoda i Array.protototype.forEachoba są dość podobne.

Uruchom następujący kod: 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));
});

Dają dokładny wynik ditto.

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

Ale zwrot pojawia się po uruchomieniu następującego kodu:

Tutaj po prostu przypisałem wynik wartości zwracanej z mapy i metod 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();

Teraz wynik jest trudny!

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

Wniosek

Array.prototype.mapzwraca tablicę, ale Array.prototype.forEachnie. Możesz więc manipulować zwróconą tablicą wewnątrz funkcji wywołania zwrotnego przekazanej do metody map, a następnie zwrócić ją.

Array.prototype.forEach tylko przechodzi przez podaną tablicę, więc możesz robić swoje rzeczy podczas chodzenia po tablicy.


11

najbardziej „widoczną” różnicą jest to, że mapa akumuluje wynik w nowej kolekcji, podczas gdy foreach jest wykonywany tylko dla samego wykonania.

ale istnieje kilka dodatkowych założeń: ponieważ „celem” mapy jest nowa lista wartości, tak naprawdę nie ma znaczenia kolejność wykonywania. w rzeczywistości niektóre środowiska wykonawcze generują kod równoległy, a nawet wprowadzają pewne zapamiętywanie, aby uniknąć wywoływania powtarzających się wartości lub lenistwa, aby w ogóle nie wywoływać niektórych.

foreach, z drugiej strony, jest nazywany specjalnie dla skutków ubocznych; dlatego kolejność jest ważna i zwykle nie można jej sparaliżować.


11

Krótka odpowiedź: map i forEachsą różne. Ponadto, mówiąc nieformalnie, mapjest to ścisły nadzór forEach.

Długa odpowiedź: najpierw wymyślmy opisy jednej linii forEachi map:

  • forEach iteruje wszystkie elementy, wywołując dostarczoną funkcję na każdym z nich.
  • map iteruje wszystkie elementy, wywołując podaną funkcję na każdym z nich, i tworzy transformowaną tablicę zapamiętując wynik każdego wywołania funkcji.

W wielu językach forEachjest często nazywany po prostu each. Poniższa dyskusja używa JavaScript tylko w celach informacyjnych. To może być naprawdę dowolny inny język.

Teraz użyjmy każdej z tych funkcji.

Używanie forEach:

Zadanie 1: Napisz funkcję printSquares, która akceptuje tablicę liczb arri drukuje w niej kwadrat każdego elementu.

Rozwiązanie 1:

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

Używanie map:

Zadanie 2: Napisz funkcję selfDot, która akceptuje tablicę liczb arri zwraca tablicę, w której każdy element jest kwadratem odpowiedniego elementu w arr.

Poza tym: tutaj, w slangu, próbujemy wyrównać tablicę wejściową. Formalnie rzecz biorąc, staramy się obliczyć sam produkt kropkowy.

Rozwiązanie 2:

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

Jak działa mapnadzbiór forEach?

Możesz użyć mapdo rozwiązania obu zadań, zadania 1 i zadania 2 . Nie można jednak użyć forEachdo rozwiązania zadania 2 .

W roztworze 1 , jeśli po prostu zastąpić forEachprzez maproztwór będą nadal ważne. Jednak w rozwiązaniu 2 zastąpienie mapprzez forEachspowoduje uszkodzenie wcześniej działającego rozwiązania.

Realizacja forEachw zakresie map:

Innym sposobem uświadomienia sobie mapwyższości jest wdrożenie forEachw kategoriach map. Ponieważ jesteśmy dobrymi programistami, nie pozwolimy sobie na zanieczyszczenie przestrzeni nazw. Zadzwonimy do naszego forEach, tylko each.

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

Teraz, jeśli nie lubisz prototypebzdur, proszę:

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

Więc umm .. Dlaczego w forEachogóle istnieje?

Odpowiedzią jest wydajność. Jeśli nie jesteś zainteresowany transformacją tablicy w inną tablicę, dlaczego powinieneś obliczyć transformowaną tablicę? Tylko rzucić to? Oczywiście nie! Jeśli nie chcesz transformacji, nie powinieneś jej przekształcać.

Tak więc chociaż mapę można wykorzystać do rozwiązania zadania 1 , prawdopodobnie nie powinna. Każdy bowiem jest do tego odpowiednim kandydatem.


Oryginalna odpowiedź:

Podczas gdy w większości zgadzają się z @madlep „s odpowiedzi, chciałbym podkreślić, że map()jest to ścisłe super zestaw z forEach().

Tak, map()zwykle służy do tworzenia nowej tablicy. Można go jednak również użyć do zmiany bieżącej tablicy.

Oto przykład:

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

W powyższym przykładzie azostał dogodnie ustawiony tak, że a[i] === idla i < a.length. Mimo to pokazuje moc map().

Oto oficjalny opismap() . Zauważ, że map()może nawet zmienić tablicę, na której się nazywa! Grad map().

Mam nadzieję, że to pomogło.


Edytowano 10-lis-2015: Dodano opracowanie.


-1, ponieważ jest to poprawne tylko w javascript; podczas gdy pytanie dotyczy konkretnie agnostyk językowych i pojęć informatycznych, a nie ograniczonych implementacji.
Javier

1
@Javier: Hmm .., muszę się z tobą zgodzić, że moja odpowiedź jest specyficzna dla JavaScript. Ale zadaj sobie to pytanie: jeśli język miałby natywną mapfunkcję, ale nie forEach; nie możesz po prostu nie użyć mapzamiast forEach? Z drugiej strony, jeśli język miałby, forEachale nie map, musiałbyś wdrożyć własny map. Nie można po prostu użyć forEachzamiast map. Powiedz mi co myślisz.
Sumukh Barve

3

Oto przykład w Scali z wykorzystaniem list: mapa zwraca listę, foreach nic nie zwraca.

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

Zatem mapa zwraca listę wynikającą z zastosowania funkcji f do każdego elementu listy:

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 po prostu stosuje się do każdego elementu:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

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

2

Jeśli mówisz w szczególności o Javascript, różnica jest taka map jest to funkcja pętli, podczas forEachgdy iterator.

Posługiwać się map gdy chcesz zastosować operację do każdego członka listy i uzyskać wyniki z powrotem jako nową listę, bez wpływu na oryginalną listę.

Użyj, forEachkiedy chcesz zrobić coś na podstawie każdego elementu listy. Możesz na przykład dodawać elementy do strony. Zasadniczo jest to świetne, gdy chcesz uzyskać „skutki uboczne”.

Inne różnice: forEachpowraca nic (ponieważ jest to naprawdę funkcją przepływu sterowania), a przekazywana w funkcji staje się odniesienia do indeksu i całej listy, podczas gdy map Zwraca nową listę i przekazuje tylko w bieżącym elemencie.


0

ForEach próbuje zastosować funkcję, taką jak zapis do db itp. Na każdym elemencie RDD bez zwracania czegokolwiek.

Ale map()stosuje pewną funkcję do elementów rdd i zwraca rdd. Więc kiedy uruchomisz poniższą metodę, nie zawiedzie ona na linii 3, ale podczas zbierania RDD po ​​zastosowaniu Foreach nie powiedzie się i wyrzuci błąd, który mówi:

Plik „<stdin>”, wiersz 5, w <module>

AttributeError: Obiekt „NoneType” nie ma atrybutu „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())
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.