2014-09-10 2 views
12

, o daScala: "harita" vs "foreach" - uygulamada "foreach" kullanmak için herhangi bir sebep var mı? biri (yani sonuçlarını dönen koleksiyon her eleman üzerinde bir yan etkisi yapmadan) topluluğu üzerinde yineleme istiyorsa Scala koleksiyonlarında

final def foreach(f: (A) ⇒ Unit): Unit 

veya

final def map[B](f: (A) ⇒ B): SomeCollectionClass[B] 
ile yapılabilir

myCollection.foreach { element => 
    doStuffWithElement(element); 
} 

myCollection.map { element => 
    doStuffWithElement(element); 
} 
: bir son kullanıcı açısından mümkün tembel haritalama (*), haricinde

, ben bu çağrılardan sıfır farklılıkları görmekverilen harita çıktılarını görmezden gelebilirim. foreach'un tüm işlevselliğini içerdiği düşünüldüğünde iki farklı yöntemin neden olması gerektiğine dair belirli bir neden düşünemiyorum ve aslında bir intelligent derleyici & VM çalışmıyorsa oldukça etkilendim Herhangi bir şeye atanmamış, okunmamış veya herhangi bir yerde kullanılmadığı düşünüldüğünde, koleksiyon nesnesinin oluşturulmasını optimize edin.

Öyleyse, soru - doğru muyum - ve foreach'u birinin kodunda herhangi bir yere aramak için bir neden yok mu?

Notlar:

(*) tembel haritalama kavramı, as throughly illustrated in this question, işleri biraz değiştirip foreach kullanımını haklı, ama bildiğim kadarıyla gördüğünüz gibi, bir spesifik bir LazyMap takılmaları gerekiyor olabilir, Normal

(**) daha sonra bir hızla for anlama sözdizimi sözdizimi üreten bir sözdizimi şeker aslında olmasından denk olur, tek bir koleksiyon kullanmayan , ama bir yazma halinde tek for sözdizimi ile ilgili koleksiyon sınıfını kullanan diğer insanlara değer veriyor ise

for (element <- myCollection) { doStuffWithElement(element); } 
myCollection.foreach { element => doStuffWithElement(element); } 

Yani, biri hala foreach yöntemi uygulamak isteyebilirsiniz: "foreach" çağrısı, yani bu iki satır tamamen eşdeğer kodu oluşturmak.

+11

Yan etki ve yan etki oluşturma işlevleri arasında ayrım yapmak için 'map' yerine 'foreach' kullanmak çok güzel. Derleyici diğeri için optimize ederse umurumda değil. Fark, kodun amacını daha açık hale getirir. –

+1

Bir adım daha ileri gitmek için LimbSoup, '' for .. 'yerine' for .. '(sans verimi) yerine '' foreach '' i kullanmayı tercih ederim (bana göre) iterasyon * için * yan etkiler ve bir dönüşüm arasındaki ayrımı gösterir. dizi için. Ayrıca, yinelenen dizinin tembel olmadığını varsaymak tehlikeli olabilir. – user2864740

+0

Aksine, eğer mevcut Scala derleyicisi ve/veya JVM, eğer sonuç kullanılmazsa, 'map'' foreach' koduna göre optimize edebilecekse, * I * çok etkilenir. Bu bana bir ** aşırı ** önemsiz olmayan optimizasyon olarak görünüyor. – ziggystar

cevap

10

Birkaç motivasyonları düşünebilirsiniz:

  1. foreach sizin derleyici bir uyarı vermeyecektir Unit tipte bir yöntemin son satırı olduğunda ancak map (ve üzerinde -Ywarn-value-discard gerekecektir ile). Bazen map kullanarak warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses alırsınız, ancak foreach ile olmaz.
  2. Genel okunabilirliği - Bir okuyucu senin bir bakışta bir şey dönmeden bazı devlet mutasyona biliyoruz, ancak map ayrıca türü olabilir
  3. Dahası 1. kullanıldıysa fazla bilişsel kaynakları aynı çalışmasını anlamak için gerekli olacaktır map içine, sonra etrafında adlandırılmış işlevleri geçen ve oluşturucu makine uzağa optimize edilip edilemeyeceğini foreach
6
scala> (1 to 5).iterator map println 
res0: Iterator[Unit] = non-empty iterator 

scala> (1 to 5).iterator foreach println 
1 
2 
3 
4 
5 
+0

Tam olarak, (*) 'de bahsettiğim şey budur - tüm yineleyicilerde varsayılan olan tembel haritalamadır. – GreyCat

+0

Öyleyse, bu sorudaki iddianız, 'foreach' ve 'map' nin, olmadıkları durumlar dışında aynı olmalarıdır? –

+0

Benim iddiam, "her ikisi de, benim düşünceme göre, ortalama son kullanıcı pratiğinde oldukça nadir görülen, açıkça belirtilmiş durumlar (*) ve (**)" hariç aynıdır. – GreyCat

3

ben etkilendim olurdu zaman kontrol ediyorum.

scala> :pa 
// Entering paste mode (ctrl-D to finish) 

implicit val cbf = new collection.generic.CanBuildFrom[List[Int],Int,List[Int]] { 
def apply() = new collection.mutable.Builder[Int, List[Int]] { 
val b = new collection.mutable.ListBuffer[Int] 
override def +=(i: Int) = { println(s"Adding $i") ; b +=(i) ; this } 
override def clear() =() ; override def result() = b.result() } 
def apply(from: List[Int]) = apply() } 

// Exiting paste mode, now interpreting. 

cbf: scala.collection.generic.CanBuildFrom[List[Int],Int,List[Int]] = [email protected] 

scala> List(1,2,3) map (_ + 1) 
Adding 2 
Adding 3 
Adding 4 
res1: List[Int] = List(2, 3, 4) 

scala> List(1,2,3) foreach (_ + 1) 
+0

Görebildiğim kadarıyla, 'CanBuildFrom' ile ilgili örtük nesneler, burada, sonuçta atılacak bir değişken oluşturmanın yanı sıra aslında bir şey yapan '+ =' yöntemini içerecek şekilde dahil olmak üzere, JVM düzeyinde bir sınıf hiyerarşisi oluşturur. uzakta (ve böylece tüm bu kod güvenli bir şekilde atlanabilir). Elbette, * bir şeyin * bir şey yaptığını, özellikle de bir şeyin 'invokevirtual' denen bir şey olduğu zaman, optimize edilmeyeceği bir yöntem. – GreyCat

İlgili konular