Foreach ve harita arasında bir fark var mı?


218

Tamam, bu bir bilgisayar bilimi sorusundan daha çok, belirli bir dile dayalı bir sorudan daha fazlasıdır, ancak bir harita işlemi ve bir foreach işlemi arasında bir fark var mıdır? Yoksa aynı şey için farklı isimler mi?


Merakla, bir Iterator[String]alır scala.io.Source.fromFile("/home/me/file").getLines()ve .foreach(s => ptintln(s))ona başvurursam , tamam yazdırılır, ancak hemen sonra boşalır. Aynı zamanda .map(ptintln(_))ona başvurursam - sadece boşalır ve hiçbir şey yazdırılmaz.
Ivan

Yanıtlar:


294

Farklı.

foreach bir liste üzerinde yinelenir ve her liste üyesine yan etkiler içeren bir işlem uygular (örneğin her birini veritabanına kaydetmek gibi)

harita bir liste üzerinde yinelenir, bu listenin her bir üyesini dönüştürür ve dönüştürülen üyelerle aynı boyutta başka bir liste döndürür (bir dize listesini büyük harfe dönüştürme gibi)


Teşekkürler, bu çizgiler boyunca bir şey olduğunu düşündüm ama emin değildi!
Robert Gould

19
ancak harita işlevinde de yan etkiler olabilir. Bu yanıtı beğendim: stackoverflow.com/a/4927981/375688
TinyTimZamboni

6
Söz gereken önemli bir nokta (bu Scala özellikle doğrudur) yapılan bir çağrı olduğunu mapmu değil beklenen dönüştürülmüş liste davet edilmesi durumunda dek altında yatan mantık yürütme ile sonuçlanır. Buna karşılık, foreachoperasyonu hemen hesaplanır.
bigT

124

Aralarındaki önemli fark, maptüm sonuçları bir koleksiyonda biriktirirken, foreachhiçbir şey döndürmez. mapgenellikle bir işlev içeren bir öğe koleksiyonunu dönüştürmek istediğinizde kullanılırken, foreachher öğe için bir eylem yürütür.


Teşekkürler! Şimdi farkı anlıyorum. Oldukça uzun bir süre benim için puslu olmuştu
Robert Gould

Ayrıca çok önemli!
Мати Тернер

40

Kısacası, foreachbir elemanlar koleksiyonunun her elemanına bir işlem uygulamak, bir koleksiyonu mapdiğerine dönüştürmek içindir.

foreachVe arasında iki önemli fark vardır map.

  1. foreachbir öğeyi bağımsız değişken olarak kabul etmekten başka, uyguladığı işlem üzerinde hiçbir kavramsal kısıtlamaya sahip değildir. Yani, işlem hiçbir şey yapamaz, bir yan etkiye sahip olabilir, bir değer döndürebilir veya bir değer döndürmeyebilir. Tek foreachönemlisi, bir öğe koleksiyonu üzerinde yineleme yapmak ve işlemi her bir öğeye uygulamaktır.

    map, diğer taraftan, işlem üzerinde bir kısıtlama vardır: işlemin bir öğe döndürmesini bekler ve muhtemelen bir öğeyi bağımsız değişken olarak kabul eder. mapBu işlem, her eleman üzerinde işlem uygulanarak, ve son olarak da başka bir koleksiyona içine operasyon her çağırma sonucunu depolamak, elemanların bir toplama yineler. Başka bir deyişle, map dönüştüren başka bir bir koleksiyon.

  2. foreachtek bir eleman koleksiyonu ile çalışır. Bu girdi koleksiyonudur.

    map iki eleman koleksiyonuyla çalışır: giriş toplama ve çıkış toplama.

İki algoritmayı ilişkilendirmek bir hata değildir: aslında, ikisini hiyerarşik olarak görebilirsiniz, nerede mapuzmanlaştığı foreach. Yani, foreachişlemin argümanını dönüştürüp başka bir koleksiyona eklemesini sağlayabilirsiniz. Yani, foreachalgoritma algoritmanın bir soyutlaması, bir genellemesidir map. Aslında, foreachçalışmasıyla ilgili herhangi bir kısıtlama olmadığından foreach, bunun en basit döngü mekanizması olduğunu güvenle söyleyebiliriz ve bir döngünün yapabileceği her şeyi yapabilir. map, diğer daha özel algoritmaların yanı sıra, ifade için de var: bir koleksiyonu diğerine eşlemek (veya dönüştürmek) istiyorsanız, niyetiniz kullandığınızdan mapdaha açıktır foreach.

Bu tartışmayı daha da genişletebilir ve copyalgoritmayı düşünebiliriz : bir koleksiyonu klonlayan bir döngü. Bu algoritma da algoritmanın bir uzmanlığıdır foreach. Bir öğe verildiğinde, aynı öğeyi başka bir koleksiyona ekleyecek bir işlem tanımlayabilirsiniz. Bu foreachişlemle copykullanırsanız, daha az netlik, ifade veya açıklık olsa da, aslında algoritmayı gerçekleştirdiniz . Daha da ileri gidelim: Bunun mapbir uzmanlaşma olduğunu copy, kendi uzmanlaştığını söyleyebiliriz foreach. mapolabilir değiştirebilir elemanların herhangi bitti dolaşır. Eğer mapbu sadece o elemanların herhangi değişmez kopyalanan öğeleri ve kullanma kopyasını niyetini daha net ifade ederdi.

foreachAlgoritma kendisi ya da dile bağlı olarak bir geri dönüş değeri olabilir veya olmayabilir. Örneğin, C ++ ' foreachda ilk aldığı işlemi döndürür. Fikir, operasyonun bir durumu olabileceği ve o operasyonun elemanlar üzerinde nasıl geliştiğini kontrol etmesini isteyebilirsiniz. mapde bir değer döndürebilir veya döndürmeyebilir. C ++ 'da transform(bunun için eşdeğer map) çıkış kabının (toplama) sonuna bir yineleyici döndürülür. Ruby, dönüş değeri mapçıktı dizi (toplama) 'dir. Yani, algoritmaların dönüş değeri gerçekten bir uygulama detayıdır; etkileri, geri döndükleri şey olabilir veya olmayabilir.


Uygulamanın nasıl .forEach()kullanılabileceğine ilişkin bir örnek .map()için buraya bakın: stackoverflow.com/a/39159854/1524693
Chinoto Vokro

32

Array.protototype.mapyöntem ve Array.protototype.forEachher ikisi de oldukça benzer.

Aşağıdaki kodu çalıştırın: 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));
});

Kesin ditto sonucunu veriyorlar.

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

Ancak aşağıdaki kodu çalıştırdığınızda büküm gelir: -

Burada haritadan dönüş değerinin sonucunu ve forEach yöntemlerini atadım.

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();

Şimdi sonuç zor bir şey!

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

Sonuç

Array.prototype.mapbir dizi döndürür ancak Array.prototype.forEachvermez. Böylece, döndürülen diziyi map yöntemine iletilen geri arama işlevi içinde değiştirebilir ve sonra geri verebilirsiniz.

Array.prototype.forEach dizi yürürken malzeme yapabilirsiniz böylece sadece verilen dizi yürür.


11

en 'görünür' fark, haritanın yeni bir koleksiyonda sonucu biriktirmesi ve foreach yalnızca yürütmenin kendisi için yapılmasıdır.

ancak birkaç ekstra varsayım var: haritanın 'amacı' yeni değerler listesi olduğundan, yürütme sırası gerçekten önemli değildir. aslında, bazı yürütme ortamları paralel kod üretir, hatta bazılarını çağırmamak için tekrarlanan değerler veya tembellikten kaçınmak için bazı hatırlatmalar sunar.

Öte yandan foreach, özellikle yan etkiler için çağrılır; bu nedenle düzen önemlidir ve genellikle paralelleştirilemez.


11

Kısa cevap: map ve forEachfarklı. Ayrıca, gayri resmi olarak, mapkatı bir süper settir forEach.

Uzun cevap: İlk olarak, biri hat açıklamaları ile gelip izin forEachve map:

  • forEach her öğede verilen işlevi çağırarak tüm öğeleri yineler.
  • map her öğede verilen işlevi çağırarak tüm öğeleri yineler ve her işlev çağrısının sonucunu hatırlayarak dönüştürülmüş bir dizi üretir.

Birçok dilde, forEachgenellikle sadece denir each. Aşağıdaki tartışma JavaScript'i yalnızca başvuru amacıyla kullanmaktadır. Gerçekten başka bir dil olabilir.

Şimdi bu işlevlerin her birini kullanalım.

Kullanma forEach:

Görev 1: Bir printSquaressayı dizisini kabul eden arrve içindeki her öğenin karesini yazdıran bir işlev yazın .

Çözüm 1:

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

Kullanma map:

Görev 2: Bir selfDotsayı dizisini kabul eden arrve her öğenin karşılık gelen öğenin karesi olduğu bir dizi döndüren bir işlev yazın arr.

Bir kenara: Burada argo terimlerle, girdi dizisini kareye almaya çalışıyoruz. Resmi olarak, nokta ürününü kendisi ile hesaplamaya çalışıyoruz.

Çözüm 2:

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

mapBir üst küme nasıl forEach?

Görev 1 ve Görev 2 gibimap her iki görevi de çözmek için kullanabilirsiniz . Ancak, Görev 2'yi çözmek için kullanamazsınız .forEach

Gelen Çözüm 1 basitçe değiştirirseniz, forEachtarafından map, çözüm hala geçerli olacaktır. Gelen Çözüm 2 Ancak değiştirerek maptarafından forEachönceden çalışma çözümü kıracak.

Uygulama forEachaçısından map:

Hayata bir başka yolu map'ın üstünlüğünü uygulamaktır forEachaçısından map. İyi programcılar olduğumuz için isim alanı kirliliğine kapılmayacağız. Biz forEachsadece arayacağız each.

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

Şimdi, prototypesaçmalıklardan hoşlanmıyorsanız , işte gidiyorsunuz:

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

Ee, umm .. Neden var forEachbile?

Cevap verimliliktir. Bir diziyi başka bir diziye dönüştürmek istemiyorsanız, dönüştürülen diziyi neden hesaplamalısınız? Sadece atmak için mi? Tabii ki değil! Bir dönüşüm istemiyorsanız, bir dönüşüm yapmamalısınız.

Harita Görev 1'i çözmek için kullanılabilse de , muhtemelen olmamalıdır. Her biri için doğru aday.


Orijinal cevap:

Ben büyük ölçüde @madlep 'ın cevabı ile kabul ederken, ben işaret etmek istiyorum map()bir olan sıkı süper seti arasında forEach().

Evet, map()genellikle yeni bir dizi oluşturmak için kullanılır. Bununla birlikte, olabilir , aynı zamanda , mevcut dizisini değiştirmek için kullanılabilir.

İşte bir örnek:

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

Yukarıdaki örnekte, auygun bir şekilde ayarlandı a[i] === iiçin i < a.length. Buna rağmen, gücünü gösterir map().

İşte resmi açıklamasımap() . map()Hatta denilen diziyi bile değiştirebileceğini unutmayın ! Dolu map().

Umarım bu yardımcı olmuştur.


Düzenlendi 10-Kasım-2015: Detaylandırma eklendi.


-1, çünkü bu sadece javascript altında geçerlidir; soru özellikle dil uygulamalarından ve bilgisayar bilimi kavramlarından bahsediyor, sınırlı uygulamalar değil.
Javier

1
@Javier: Hmm .., cevabımın JavaScript'e özgü olduğu konusunda hemfikirim. Ama kendinize şunu sorun: Bir dilin yerel bir mapişlevi varsa ama hayır ise forEach; mapbunun yerine kullanamaz mıydın forEach? Flip tarafında, bir dil vardı forEachama hayır mapolsaydı, kendi dilinizi uygulamak zorunda kalacaksınız map. Bunun forEachyerine sadece kullanamazsınız map. Bana ne düşündüğünü söyle.
Sumukh Barve

3

İşte Scala'da listeleri kullanan bir örnek: map return list, foreach hiçbir şey döndürmez.

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

Böylece harita, her liste öğesine f işlevinin uygulanmasından kaynaklanan listeyi döndürür:

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 her öğeye f uygular:

scala> var sum = 0
sum: Int = 0

scala> list foreach (sum += _)

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

2

Özellikle Javascript hakkında konuşuyorsanız, fark bir yineleyici mapiken bir döngü işlevi forEacholmasıdır.

mapListenin her üyesine bir işlem uygulamak ve sonuçları orijinal listeyi etkilemeden yeni bir liste olarak almak istediğinizde kullanın .

Listenin her bir öğesi temelinde bir şeyler yapmakforEach istediğinizde kullanın . Örneğin, sayfaya bir şeyler ekliyor olabilirsiniz. Temel olarak, "yan etkiler" istediğinizde mükemmeldir.

Diğer farklar: forEachhiçbir şey döndürmez (gerçekten bir kontrol akışı işlevi olduğu için) ve aktarılan işlev dizine ve tüm listeye referans alırken, harita yeni listeyi döndürür ve yalnızca geçerli öğeye geçer.


0

ForEach, herhangi bir şey geri dönmeden RDD'nin her öğesinde db'ye yazma vb. Gibi bir işlevi uygulamaya çalışır.

Ancak map()rdd öğelerine bazı işlevler uygular ve rdd değerini döndürür. Bu nedenle, aşağıdaki yöntemi çalıştırdığınızda, line3'te başarısız olmaz, ancak foreach uygulandıktan sonra rdd toplanırken başarısız olur ve bir hata atar.

<module> içindeki "<stdin>" dosyası, satır 5

AttributeError: 'NoneType' nesnesinin 'collect' özelliği yok

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())
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.