Jawaban:
Ini digunakan dalam urutan pemahaman (seperti daftar-pemahaman dan generator Python, di mana Anda dapat menggunakan yield
juga).
Itu diterapkan dalam kombinasi dengan for
dan menulis elemen baru ke dalam urutan yang dihasilkan.
Contoh sederhana (dari scala-lang )
/** 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)
}
}
Ekspresi yang sesuai dalam F # adalah
[ for a in args -> a.toUpperCase ]
atau
from a in args select a.toUpperCase
dalam Linq.
Ruby yield
memiliki efek yang berbeda.
Saya pikir jawaban yang diterima bagus, tetapi tampaknya banyak orang gagal memahami beberapa poin mendasar.
Pertama, for
pemahaman Scala setara dengan do
notasi Haskell , dan itu tidak lebih dari gula sintaksis untuk komposisi beberapa operasi monadik. Karena pernyataan ini kemungkinan besar tidak akan membantu siapa pun yang membutuhkan bantuan, mari kita coba lagi ... :-)
for
Pemahaman Scala adalah gula sintaksis untuk komposisi beberapa operasi dengan peta, flatMap
dan filter
. Atau foreach
. Scala sebenarnya menerjemahkan for
-expression menjadi panggilan ke metode-metode tersebut, sehingga setiap kelas yang menyediakannya, atau sebagian dari mereka, dapat digunakan untuk pemahaman.
Pertama, mari kita bicara tentang terjemahannya. Ada aturan yang sangat sederhana:
Ini
for(x <- c1; y <- c2; z <-c3) {...}
diterjemahkan ke dalam
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
Ini
for(x <- c1; y <- c2; z <- c3) yield {...}
diterjemahkan ke dalam
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
Ini
for(x <- c; if cond) yield {...}
diterjemahkan pada Scala 2.7 ke
c.filter(x => cond).map(x => {...})
atau, pada Scala 2.8, ke
c.withFilter(x => cond).map(x => {...})
dengan mundur ke yang pertama jika metode withFilter
tidak tersedia tetapi filter
. Silakan lihat bagian di bawah ini untuk informasi lebih lanjut tentang ini.
Ini
for(x <- c; y = ...) yield {...}
diterjemahkan ke dalam
c.map(x => (x, ...)).map((x,y) => {...})
Ketika Anda melihat for
pemahaman yang sangat sederhana , map
/ foreach
alternatif terlihat, memang, lebih baik. Namun, begitu Anda mulai menyusunnya, Anda dapat dengan mudah tersesat di dalam kurung dan level sarang. Ketika itu terjadi, for
pemahaman biasanya jauh lebih jelas.
Saya akan menunjukkan satu contoh sederhana, dan sengaja menghilangkan penjelasan apa pun. Anda dapat memutuskan sintaks mana yang lebih mudah dimengerti.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
atau
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
Scala 2.8 memperkenalkan metode yang disebut withFilter
, yang perbedaan utamanya adalah, alih-alih mengembalikan koleksi baru yang difilter, ia memfilter sesuai permintaan. The filter
metode memiliki perilaku didefinisikan berdasarkan ketatnya koleksi. Untuk memahami ini lebih baik, mari kita lihat beberapa Scala 2.7 dengan List
(ketat) dan Stream
(tidak ketat):
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
Perbedaannya terjadi karena filter
segera diterapkan dengan List
, kembali daftar peluang - karena found
adalah false
. Hanya kemudian foreach
dieksekusi, tetapi, pada saat ini, perubahan found
tidak ada artinya, seperti yang filter
telah dieksekusi.
Dalam hal ini Stream
, kondisi tersebut tidak segera diterapkan. Sebagai gantinya, karena setiap elemen diminta oleh foreach
, filter
menguji kondisi, yang memungkinkan foreach
untuk memengaruhinya found
. Untuk memperjelasnya, berikut ini adalah kode setara untuk pemahaman:
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)
Hal ini menyebabkan banyak masalah, karena orang-orang berharap if
untuk dianggap sesuai permintaan, alih-alih diterapkan ke seluruh koleksi sebelumnya.
Scala 2.8 diperkenalkan withFilter
, yang selalu tidak ketat, tidak masalah ketatnya koleksi. Contoh berikut menunjukkan List
dengan kedua metode pada Scala 2.8:
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
Ini menghasilkan hasil yang kebanyakan orang harapkan, tanpa mengubah cara filter
berperilaku. Sebagai catatan tambahan, Range
diubah dari non-ketat menjadi ketat antara Scala 2.7 dan Scala 2.8.
withFilter
seharusnya tidak ketat juga, bahkan untuk koleksi ketat, yang pantas mendapat penjelasan. Saya akan mempertimbangkan ini ...
for(x <- c; y <- x; z <-y) {...}
diterjemahkan ke c.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
diterjemahkan kec.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
benar-benar diterjemahkan c.map(x => (x, ...)).map((x,y) => {...})
? Saya pikir itu diterjemahkan ke dalam c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
atau saya kehilangan sesuatu?
Ya, seperti yang dikatakan Earwicker, ini hampir sama dengan LINQ select
dan sangat sedikit hubungannya dengan Ruby dan Python yield
. Pada dasarnya, di mana dalam C # Anda akan menulis
from ... select ???
di Scala yang Anda miliki sebagai gantinya
for ... yield ???
Penting juga untuk memahami bahwa for
-pengertian tidak hanya bekerja dengan urutan, tetapi dengan jenis apa pun yang mendefinisikan metode tertentu, seperti LINQ:
map
, itu memungkinkan for
-expressions yang terdiri dari satu generator.flatMap
serta map
, memungkinkan for
-expressions terdiri dari beberapa generator.foreach
, ia memungkinkan- for
loop tanpa hasil (baik dengan generator tunggal dan banyak).filter
, ini memungkinkan for
ekspresi-filter yang dimulai dengan if
dalam for
ekspresi.Kecuali jika Anda mendapatkan jawaban yang lebih baik dari pengguna Scala (yang bukan saya), inilah pemahaman saya.
Itu hanya muncul sebagai bagian dari ekspresi yang dimulai dengan for
, yang menyatakan bagaimana membuat daftar baru dari daftar yang ada.
Sesuatu seperti:
var doubled = for (n <- original) yield n * 2
Jadi ada satu item keluaran untuk setiap input (walaupun saya percaya ada cara untuk menjatuhkan duplikat).
Ini sangat berbeda dari "kelanjutan imperatif" yang dimungkinkan oleh hasil dalam bahasa lain, di mana ia menyediakan cara untuk menghasilkan daftar panjang, dari beberapa kode imperatif dengan hampir semua struktur.
(Jika Anda terbiasa dengan C #, itu lebih dekat ke operator LINQ select
daripada itu yield return
).
Kata kunci yield
dalam Scala hanyalah gula sintaksis yang dapat dengan mudah diganti dengan a map
, seperti yang sudah dijelaskan oleh Daniel Sobral secara rinci.
Di sisi lain, yield
benar-benar menyesatkan jika Anda mencari generator (atau kelanjutan) yang mirip dengan yang ada di Python . Lihat utas SO ini untuk informasi lebih lanjut: Apa cara yang disukai untuk menerapkan 'hasil' di Scala?
Pertimbangkan hal berikut untuk pemahaman
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Mungkin bermanfaat untuk membacanya dengan lantang sebagai berikut
" Untuk setiap bilangan bulat i
, jika lebih besar dari 3
, maka hasilkan (hasilkan) i
dan tambahkan ke daftar A
."
Dalam hal notasi set-builder matematis , pemahaman di atas adalah analog dengan
yang dapat dibaca sebagai
" Untuk setiap bilangan bulat , jika lebih besar dari , maka itu adalah anggota himpunan ."
atau sebagai alternatif
" Adalah himpunan semua bilangan bulat , sehingga masing-masing lebih besar dari ."
Yield mirip dengan untuk loop yang memiliki buffer yang tidak bisa kita lihat dan untuk setiap kenaikan, itu terus menambahkan item berikutnya ke buffer. Ketika for loop selesai berjalan, itu akan mengembalikan koleksi semua nilai yang dihasilkan. Hasil dapat digunakan sebagai operator aritmatika sederhana atau bahkan dalam kombinasi dengan array. Berikut adalah dua contoh sederhana untuk pemahaman Anda yang lebih baik
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vektor (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)] = Daftar ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
Semoga ini membantu!!
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 )
Kedua kode ini setara.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Kedua kode ini juga setara.
Peta sefleksibel hasil dan sebaliknya.
hasil lebih fleksibel daripada peta (), lihat contoh di bawah ini
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 )
hasil akan mencetak hasil seperti: Daftar (5, 6), yang bagus
sedangkan map () akan mengembalikan hasil seperti: Daftar (false, false, true, true, true), yang mungkin bukan yang Anda inginkan.