Apakah ada perbedaan antara foreach dan map?

218

Ok ini lebih merupakan pertanyaan sains komputer, daripada pertanyaan berdasarkan bahasa tertentu, tetapi apakah ada perbedaan antara operasi peta dan operasi foreach? Atau apakah mereka hanya nama yang berbeda untuk hal yang sama?

Robert Gould
sumber
Anehnya, jika saya mendapatkan Iterator[String]dari scala.io.Source.fromFile("/home/me/file").getLines()dan menerapkannya .foreach(s => ptintln(s)), itu dicetak ok tapi menjadi kosong setelahnya. Pada saat yang sama jika saya menerapkannya .map(ptintln(_))- itu hanya menjadi kosong dan tidak ada yang dicetak.
Ivan

Jawaban:

294

Berbeda.

foreach iterates atas daftar dan menerapkan beberapa operasi dengan efek samping untuk setiap anggota daftar (seperti menyimpan masing-masing ke database misalnya)

peta iterasi di atas daftar, mengubah setiap anggota daftar itu, dan mengembalikan daftar lain dengan ukuran yang sama dengan anggota yang diubah (seperti mengonversi daftar string menjadi huruf besar)

madlep
sumber
Terima kasih, saya pikir itu adalah sesuatu seperti itu tetapi tidak yakin!
Robert Gould
19
tetapi Anda dapat memiliki efek samping yang terjadi pada fungsi peta juga. Saya suka jawaban ini: stackoverflow.com/a/4927981/375688
TinyTimZamboni
6
Satu hal penting untuk menyebutkan (hal ini terutama berlaku di Scala) adalah bahwa panggilan untuk maptidak tidak menghasilkan eksekusi logika yang mendasarinya sampai kapan daftar mengubah diharapkan dipanggil. Sebaliknya, foreachoperasi dihitung segera.
bigT
124

Perbedaan penting di antara mereka adalah bahwa mapsemua hasil terakumulasi menjadi koleksi, sedangkan foreachtidak mengembalikan apa pun. mapbiasanya digunakan ketika Anda ingin mengubah kumpulan elemen dengan fungsi, sedangkan foreachhanya menjalankan aksi untuk setiap elemen.

Henk
sumber
Terima kasih! Sekarang saya mengerti perbedaannya. Sudah agak kabur bagi saya untuk beberapa waktu
Robert Gould
Juga sangat penting!
Мати Тернер
40

Singkatnya, foreachadalah untuk menerapkan operasi pada setiap elemen dari kumpulan elemen, sedangkan mapuntuk mengubah satu koleksi menjadi yang lain.

Ada dua perbedaan signifikan antara foreachdan map.

  1. foreachtidak memiliki batasan konseptual pada operasi yang berlaku, selain dari mungkin menerima elemen sebagai argumen. Artinya, operasi mungkin tidak melakukan apa-apa, mungkin memiliki efek samping, dapat mengembalikan nilai atau mungkin tidak mengembalikan nilai. Yang perlu foreachdiperhatikan adalah beralih pada kumpulan elemen, dan menerapkan operasi pada setiap elemen.

    map, di sisi lain, memang memiliki batasan pada operasi: ia mengharapkan operasi untuk mengembalikan elemen, dan mungkin juga menerima elemen sebagai argumen. The mapOperasi iterates atas kumpulan elemen, menerapkan operasi pada setiap elemen, dan akhirnya menyimpan hasil setiap permintaan dari operasi menjadi koleksi lain. Dengan kata lain, map mengubah satu koleksi menjadi yang lain.

  2. foreachbekerja dengan satu kumpulan elemen. Ini adalah koleksi input.

    map bekerja dengan dua koleksi elemen: koleksi input dan koleksi output.

Ini bukan kesalahan untuk menghubungkan kedua algoritma: pada kenyataannya, Anda dapat melihat keduanya secara hierarkis, di mana mapada spesialisasi foreach. Artinya, Anda bisa menggunakan foreachdan meminta operasi mengubah argumennya dan memasukkannya ke koleksi lain. Jadi, foreachalgoritma adalah abstraksi, generalisasi, dari mapalgoritma. Faktanya, karena foreachtidak memiliki batasan pada operasinya, kita dapat dengan aman mengatakan bahwa itu foreachadalah mekanisme perulangan yang paling sederhana di luar sana, dan ia dapat melakukan apa saja yang dapat dilakukan perulangan. map, serta algoritme lain yang lebih terspesialisasi, tersedia untuk ekspresif: jika Anda ingin memetakan (atau mengubah) satu koleksi menjadi koleksi lain, niat Anda lebih jelas jika Anda gunakan mapdaripada jika Anda gunakan foreach.

Kami dapat memperluas diskusi ini lebih lanjut, dan mempertimbangkan copyalgoritma: loop yang mengkloning koleksi. Algoritma ini juga merupakan spesialisasi dari foreachalgoritma. Anda bisa mendefinisikan operasi yang, diberikan elemen, akan memasukkan elemen yang sama ke koleksi lain. Jika Anda gunakan foreachdengan operasi itu, Anda sebenarnya melakukan copyalgoritma, meskipun dengan kejelasan, ekspresif atau kejelasan yang berkurang. Mari kita bahas lebih jauh: Kita dapat mengatakan bahwa itu mapadalah spesialisasi copy, itu sendiri adalah spesialisasi foreach. mapdapat mengubah salah satu elemen yang diulangi. Jika maptidak mengubah salah satu elemen maka itu hanya menyalin elemen, dan menggunakan salinan akan mengekspresikan niat lebih jelas.

The foreachalgoritma itu sendiri mungkin atau mungkin tidak memiliki nilai kembali, tergantung pada bahasa. Di C ++, misalnya, foreachmengembalikan operasi yang semula diterima. Idenya adalah bahwa operasi tersebut mungkin memiliki keadaan, dan Anda mungkin ingin operasi itu kembali untuk memeriksa bagaimana itu berevolusi atas elemen. map, juga, mungkin atau mungkin tidak mengembalikan nilai. Dalam C ++ transform(setara dengan di mapsini) terjadi untuk mengembalikan iterator ke akhir wadah keluaran (koleksi). Di Ruby, nilai balik dari mapadalah urutan keluaran (koleksi). Jadi, nilai balik dari algoritma ini benar-benar detail implementasi; efeknya mungkin atau mungkin bukan apa yang mereka kembalikan.

wilhelmtell
sumber
Untuk contoh tentang bagaimana .forEach()dapat digunakan untuk mengimplementasikan .map(), lihat di sini: stackoverflow.com/a/39159854/1524693
Chinoto Vokro
32

Array.protototype.mapMetode & Array.protototype.forEachkeduanya sangat mirip.

Jalankan kode berikut: 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));
});

Mereka memberikan hasil yang tepat.

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

Namun twist datang ketika Anda menjalankan kode berikut: -

Di sini saya hanya menetapkan hasil dari nilai pengembalian dari peta dan metode 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();

Sekarang hasilnya adalah sesuatu yang rumit!

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

Kesimpulan

Array.prototype.mapmengembalikan array tetapi Array.prototype.forEachtidak. Jadi Anda dapat memanipulasi array yang dikembalikan di dalam fungsi callback yang diteruskan ke metode peta dan kemudian mengembalikannya.

Array.prototype.forEach hanya berjalan melalui array yang diberikan sehingga Anda dapat melakukan pekerjaan sambil berjalan di array.

abhisekp
sumber
11

perbedaan yang paling 'terlihat' adalah bahwa peta mengakumulasikan hasil dalam koleksi baru, sementara foreach hanya dilakukan untuk eksekusi itu sendiri.

tetapi ada beberapa asumsi tambahan: karena 'tujuan' peta adalah daftar nilai baru, itu tidak terlalu penting urutan eksekusi. pada kenyataannya, beberapa lingkungan eksekusi menghasilkan kode paralel, atau bahkan memperkenalkan beberapa memoizing untuk menghindari panggilan untuk nilai yang berulang, atau kemalasan, untuk menghindari panggilan sama sekali.

foreach, di sisi lain, disebut khusus untuk efek samping; oleh karena itu urutannya penting, dan biasanya tidak dapat diparalelkan.

Javier
sumber
11

Jawaban singkat: map dan forEachberbeda. Juga, berbicara secara informal, mapadalah superset ketat forEach.

Jawaban panjang: Pertama, mari kita buat dengan satu baris deskripsi forEachdan map:

  • forEach iterates atas semua elemen, memanggil fungsi yang disediakan pada masing-masing.
  • map iterates atas semua elemen, memanggil fungsi yang disediakan pada masing-masing, dan menghasilkan array yang diubah dengan mengingat hasil dari setiap panggilan fungsi.

Dalam banyak bahasa, forEachsering disebut adil each. Diskusi berikut menggunakan JavaScript hanya untuk referensi. Itu bisa benar-benar bahasa lain.

Sekarang, mari kita gunakan masing-masing fungsi ini.

Menggunakan forEach:

Tugas 1: Tulis fungsi printSquares, yang menerima array angka arr, dan mencetak kuadrat dari setiap elemen di dalamnya.

Solusi 1:

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

Menggunakan map:

Tugas 2: Tulis fungsi selfDot, yang menerima array angka arr, dan mengembalikan array di mana setiap elemen adalah kuadrat dari elemen yang sesuai di arr.

Selain: Di sini, dalam istilah slang, kami mencoba untuk menguadratkan array input. Secara formal, kami mencoba menghitung produk titik itu sendiri.

Solusi 2:

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

Bagaimana mapsuperset forEach?

Anda dapat menggunakan mapuntuk menyelesaikan kedua tugas, Tugas 1 dan Tugas 2 . Namun, Anda tidak dapat menggunakan forEachuntuk menyelesaikan Tugas 2 .

Di Solusi 1 , jika Anda hanya menggantinya forEachdengan map, solusi masih akan valid. Namun dalam Solusi 2 , mengganti mapdengan forEachakan merusak solusi Anda yang sebelumnya berfungsi.

Melaksanakan forEachdalam hal map:

Cara lain untuk mewujudkan mapkeunggulan adalah dengan mengimplementasikannya forEachdalam hal map. Karena kami adalah programmer yang baik, kami tidak akan terlibat dalam polusi namespace. Kami akan memanggil kami forEach, adil each.

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

Sekarang, jika Anda tidak suka prototypeomong kosong, ini dia:

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

Jadi, umm .. Kenapa forEachbahkan ada?

Jawabannya adalah efisiensi. Jika Anda tidak tertarik mengubah array menjadi array lain, mengapa Anda harus menghitung array yang diubah? Hanya membuangnya? Tentu saja tidak! Jika Anda tidak ingin transformasi, Anda tidak harus melakukan transformasi.

Jadi, sementara peta dapat digunakan untuk menyelesaikan Tugas 1 , mungkin seharusnya tidak. Untuk masing-masing adalah kandidat yang tepat untuk itu.


Jawaban asli:

Sementara saya sebagian besar setuju dengan jawaban @madlep 's, saya ingin menunjukkan bahwa map()adalah super-set yang ketat dari forEach().

Ya, map()biasanya digunakan untuk membuat array baru. Namun, ini juga dapat digunakan untuk mengubah array saat ini.

Ini sebuah contoh:

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

Dalam contoh di atas, amudah diatur sedemikian rupa a[i] === iuntuk i < a.length. Meski begitu, itu menunjukkan kekuatan map().

Berikut deskripsi resmi darimap() . Catatan yang map()bahkan dapat mengubah array yang disebut! Salam map().

Semoga ini bisa membantu.


Diedit 10-Nov-2015: Menambahkan elaborasi.

Sumukh Barve
sumber
-1, karena ini hanya berlaku di bawah javascript; sedangkan pertanyaannya secara khusus berbicara tentang agnosticy bahasa dan konsep ilmu komputer, bukan implementasi terbatas.
Javier
1
@Javier: Hmm .., saya harus setuju dengan Anda tentang jawaban saya yang khusus JavaScript. Tetapi tanyakan pada diri sendiri ini: Jika suatu bahasa memiliki mapfungsi asli tetapi tidak forEach; tidak bisa Anda tidak menggunakan mapbukan forEach? Di sisi lain, jika suatu bahasa memiliki forEachtetapi tidak map, Anda harus menerapkan bahasa Anda sendiri map. Anda tidak bisa hanya menggunakan forEachbukan map. Katakan padaku apa yang kau pikirkan.
Sumukh Barve
3

Berikut adalah contoh dalam Scala menggunakan daftar: daftar pengembalian peta, foreach mengembalikan apa-apa.

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

Jadi peta mengembalikan daftar yang dihasilkan dari penerapan fungsi f ke setiap elemen daftar:

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 hanya berlaku untuk setiap elemen:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

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

Jika Anda berbicara tentang Javascript secara khusus, perbedaannya adalah mapfungsi loop sedangkan forEachiterator.

Gunakan mapketika Anda ingin menerapkan operasi untuk setiap anggota daftar dan mendapatkan hasilnya kembali sebagai daftar baru, tanpa mempengaruhi daftar asli.

Gunakan forEachketika Anda ingin melakukan sesuatu berdasarkan setiap elemen daftar. Anda mungkin menambahkan hal-hal ke halaman, misalnya. Pada dasarnya, ini bagus untuk saat Anda menginginkan "efek samping".

Perbedaan lainnya: forEachtidak mengembalikan apa-apa (karena ini benar-benar fungsi kontrol aliran), dan fungsi pass-in mendapatkan referensi ke indeks dan seluruh daftar, sedangkan peta mengembalikan daftar baru dan hanya lewat di elemen saat ini.

perlakukan mod Anda dengan baik
sumber
0

ForEach mencoba menerapkan fungsi seperti menulis ke db dll pada setiap elemen RDD tanpa mengembalikan apa pun.

Tetapi map()menerapkan beberapa fungsi atas elemen rdd dan mengembalikan rdd. Jadi ketika Anda menjalankan metode di bawah ini tidak akan gagal di line3 tetapi saat mengumpulkan rdd setelah menerapkan foreach itu akan gagal dan melempar kesalahan yang mengatakan

File "<stdin>", baris 5, dalam <module>

AttributeError: objek 'NoneType' tidak memiliki atribut 'kumpulkan'

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())
pengguna2456047
sumber