Yanıtlar:
Sekans kavrayışlarında kullanılır (Python'un liste kavrayışları ve siz de kullanabileceğiniz jeneratörler gibi yield
).
Kombinasyon ile birlikte uygulanır for
ve ortaya çıkan sıraya yeni bir eleman yazar.
Basit örnek ( scala-lang'den )
/** Turn command line arguments to uppercase */
object Main {
def main(args: Array[String]) {
val res = for (a <- args) yield a.toUpperCase
println("Arguments: " + res.toString)
}
}
F # 'daki karşılık gelen ifade
[ for a in args -> a.toUpperCase ]
veya
from a in args select a.toUpperCase
Linq.
Ruby'nin yield
farklı bir etkisi var.
Kabul edilen cevabın harika olduğunu düşünüyorum, ancak birçok insan bazı temel noktaları kavrayamadı.
Birincisi, Scala'nın for
kavrayışları Haskell'in do
notasyonuna eşdeğerdir ve çoklu monadik operasyonların kompozisyonu için sözdizimsel bir şekerden başka bir şey değildir. Bu ifade büyük olasılıkla yardıma ihtiyacı olan kimseye yardım etmeyeceğinden, tekrar deneyelim… :-)
Scala'nın for
kavrayışları, harita ile çoklu operasyonların kompozisyonu için sözdizimsel şekerdir flatMap
ve filter
. Veya foreach
. Scala aslında bir for
ifadeyi bu yöntemlere yapılan çağrılara çevirir , böylece bunları sağlayan herhangi bir sınıf ya da bir alt kümesi kavrama için kullanılabilir.
Önce çeviriler hakkında konuşalım. Çok basit kurallar var:
Bu
for(x <- c1; y <- c2; z <-c3) {...}
çevrildi
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
Bu
for(x <- c1; y <- c2; z <- c3) yield {...}
çevrildi
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
Bu
for(x <- c; if cond) yield {...}
Scala 2.7'ye çevrildi
c.filter(x => cond).map(x => {...})
veya Scala 2.8'de
c.withFilter(x => cond).map(x => {...})
yöntem withFilter
mevcut değilse ancak mevcutsa eski haline geri dönüş filter
. Bununla ilgili daha fazla bilgi için lütfen aşağıdaki bölüme bakın.
Bu
for(x <- c; y = ...) yield {...}
çevrildi
c.map(x => (x, ...)).map((x,y) => {...})
Çok basit for
kavrayışlara baktığınızda, map
/ foreach
alternatifleri gerçekten daha iyi görünür. Ancak bunları oluşturmaya başladığınızda, parantez ve yuvalama düzeylerinde kolayca kaybolabilirsiniz. Bu olduğunda, for
kavrayışlar genellikle çok daha açıktır.
Basit bir örnek göstereceğim ve kasten herhangi bir açıklamayı atlayacağım. Hangi sözdiziminin daha kolay anlaşılacağına karar verebilirsiniz.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
veya
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
Scala 2.8 withFilter
, ana farkı, yeni, filtrelenmiş bir koleksiyon döndürmek yerine, talep üzerine filtrelemek olduğu adlı bir yöntem sundu . filter
Yöntem koleksiyonun katılığından dayalı tanımlanan davranışını vardır. Bunu daha iyi anlamak için, List
(katı) ve Stream
(katı olmayan ) bazı Scala 2.7'ye bir göz atalım :
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> Stream.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Fark, olur filter
derhal uygulanır List
beri - oran listesini dönen, found
bir false
. Ancak o foreach
zaman yürütülür, ancak bu zamana kadar, değişiklik zaten yürütüldüğü found
gibi anlamsızdır filter
.
Durumunda, durum Stream
hemen uygulanmaz. Her eleman tarafından istenen Bunun yerine, foreach
, filter
sağlayan koşulu, test foreach
bunu yoluyla etkileme found
. Sadece açıklığa kavuşturmak için, eşdeğer anlama kodu şöyledir:
for (x <- List.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
for (x <- Stream.range(1, 10); if x % 2 == 1 && !found)
if (x == 5) found = true else println(x)
Bu, birçok soruna neden oldu, çünkü insanlar if
önceden tüm koleksiyona uygulamak yerine talep üzerine düşünülmesini beklediler .
Koleksiyonun katılığı ne olursa olsun withFilter
, her zaman katı olmayan Scala 2.8 piyasaya sürüldü . Aşağıdaki örnek List
Scala 2.8'de her iki yöntemle de gösterilmiştir :
scala> var found = false
found: Boolean = false
scala> List.range(1,10).filter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
7
9
scala> found = false
found: Boolean = false
scala> List.range(1,10).withFilter(_ % 2 == 1 && !found).foreach(x => if (x == 5) found = true else println(x))
1
3
Bu, çoğu insanın nasıl filter
davrandığını değiştirmeden beklediği sonucu üretir . Bir yan not olarak, Range
Scala 2.7 ve Scala 2.8 arasında katı olmayandan katıya değiştirildi.
withFilter
bazı açıklamaları hak eden katı koleksiyonlar için bile katı olmamalıdır. Bunu düşüneceğim ...
for(x <- c; y <- x; z <-y) {...}
çevrilir c.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
çevrilirc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
gerçekten çevrilmiş c.map(x => (x, ...)).map((x,y) => {...})
mi? Bence çevrilmiş c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
ya da bir şey eksik mi?
Evet, Earwicker'ın dediği gibi, LINQ'lara eşdeğerdir select
ve Ruby's ve Python'larla çok az ilgisi vardır yield
. Temel olarak, nerede C # yazmak istiyorsunuz
from ... select ???
Scala'da bunun yerine
for ... yield ???
for
-Anlamaların sadece dizilerle değil, aynı zamanda LINQ gibi belirli yöntemleri tanımlayan herhangi bir türle de çalıştığını anlamak da önemlidir :
map
, for
tek bir oluşturucudan oluşan-ifadelerine izin verir .flatMap
gibi map
, for
-bir kaç jeneratörden oluşan-ifadelerine izin verir .foreach
, for
verimsiz döngülere izin verir (hem tekli hem de çoklu jeneratörlerle).filter
, bu izin veren for
bir ile başlayan süzgeç ifadeleri if
de for
ifade.Bir Scala kullanıcısından daha iyi bir cevap alamadığınız sürece (ki ben değilim), işte benim anlayışım.
Yalnızca for
, mevcut bir listeden nasıl yeni bir liste oluşturulacağını belirten bir ifadenin parçası olarak görünür .
Gibi bir şey:
var doubled = for (n <- original) yield n * 2
Yani her giriş için bir çıktı öğesi var (her ne kadar kopyaları bırakmanın bir yolu olduğuna inanıyorum).
Bu, herhangi bir uzunlukta bir liste oluşturmanın neredeyse her yapıya sahip bazı zorunlu kodlardan bir yolunu sağladığı diğer dillerde verimle sağlanan "zorunlu sürekliliklerden" oldukça farklıdır.
(C # hakkında bilginiz varsa, LINQ select
operatörüne olduğundan daha yakındır yield return
).
yield
Scala'daki anahtar kelimemap
, Daniel Sobral'ın daha önce ayrıntılı olarak açıkladığı gibi, kolayca a ile değiştirilebilen sözdizimsel şekerdir .
Öte yandan, Python'dakilere benzer jeneratörler (veya devamlar) arıyorsanız yield
kesinlikle yanıltıcıdır . Daha fazla bilgi için bu SO iş parçacığına bakın: Scala'da 'verimi' uygulamanın tercih edilen yolu nedir?
Aşağıdaki anlayış için düşünün
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Aşağıdaki gibi yüksek sesle okumak yararlı olabilir
" İçin her bir tamsayı i
, eğer bu daha büyük 3
, daha sonra elde (üretim) i
ve listeye ekleyin A
."
Matematiksel küme oluşturucu gösterimi açısından , yukarıdaki kavrama aşağıdakilere benzerdir:
hangi olarak okunabilir
" İçin her bir tamsayı , eğer bu daha büyük , daha sonra bir üyesidir dizi ."
veya alternatif olarak
" tüm tamsayıların kümesidir , öyle ki her biri büyüktür ."
Verim, göremediğimiz bir arabelleğe sahip olan döngüye benzer ve her bir artış için arabelleğe bir sonraki öğe eklemeye devam eder. For döngüsü çalışmayı bitirdiğinde, elde edilen tüm değerlerin toplanmasını döndürür. Verim basit aritmetik operatörler olarak veya hatta dizilerle kombinasyon halinde kullanılabilir. Daha iyi anlamanız için iki basit örnek
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vektör (3, 6, 9, 12, 15)
scala> val nums = Seq(1,2,3)
nums: Seq[Int] = List(1, 2, 3)
scala> val letters = Seq('a', 'b', 'c')
letters: Seq[Char] = List(a, b, c)
scala> val res = for {
| n <- nums
| c <- letters
| } yield (n, c)
res: Seq [(Int, Char)] = Liste ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
Bu yardımcı olur umarım!!
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.filter(_ > 3).map(_ + 1)
println( res3 )
println( res4 )
Bu iki kod parçası eşdeğerdir.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Bu iki kod parçası da eşdeğerdir.
Harita verim kadar esnektir ve tersi de geçerlidir.
verim haritadan () daha esnektir, aşağıdaki örneğe bakın
val aList = List( 1,2,3,4,5 )
val res3 = for ( al <- aList if al > 3 ) yield al + 1
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
verim sonucu yazdırır: Liste (5, 6), ki bu iyi
map () sonucu şu şekilde döndürür: List (false, false, true, true, true), muhtemelen istediğiniz gibi değildir.