Apa hasil dari Scala?

Jawaban:

205

Ini digunakan dalam urutan pemahaman (seperti daftar-pemahaman dan generator Python, di mana Anda dapat menggunakan yieldjuga).

Itu diterapkan dalam kombinasi dengan fordan 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 yieldmemiliki efek yang berbeda.

Dario
sumber
57
Jadi mengapa saya menggunakan hasil alih-alih peta? Kode peta ini setara dengan val res = args.map (_. ToUpperCase), bukan?
Geo
4
Jika Anda menyukai sintaks lebih baik. Juga, seperti yang ditunjukkan oleh alexey, pemahaman juga menyediakan sintaks yang bagus untuk mengakses flatMap, filter dan foreach.
Nathan Shively-Sanders
22
Baik. Jika Anda hanya memiliki peta sederhana - satu generator tanpa jika - saya pasti akan mengatakan peta panggilan lebih mudah dibaca. Jika Anda memiliki beberapa generator yang bergantung satu sama lain, dan / atau filter, Anda dapat memilih untuk ekspresi.
Alexey Romanov
13
Harap perhatikan bahwa contoh yang diberikan tidak setara dengan ekspresi peta: sama saja. A untuk pemahaman diterjemahkan ke panggilan untuk memetakan, flatMap dan filter.
Daniel C. Sobral
9
Jawabannya dimulai seperti ini: "Ini digunakan dalam urutan pemahaman (seperti daftar-pemahaman dan generator Python, di mana Anda dapat menggunakan hasil juga)." Ini keliru membuat orang berpikir bahwa hasil dalam Scala mirip dengan hasil di Python. Ini bukan kasusnya. Dalam Python, hasil digunakan dalam konteks coroutine (atau kelanjutan) sementara itu tidak terjadi di Scala. Untuk klarifikasi lebih lanjut, silakan kunjungi utas ini: stackoverflow.com/questions/2201882/…
Richard Gomes
817

Saya pikir jawaban yang diterima bagus, tetapi tampaknya banyak orang gagal memahami beberapa poin mendasar.

Pertama, forpemahaman Scala setara dengan donotasi 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 ... :-)

forPemahaman Scala adalah gula sintaksis untuk komposisi beberapa operasi dengan peta, flatMapdan 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:

  1. Ini

    for(x <- c1; y <- c2; z <-c3) {...}

    diterjemahkan ke dalam

    c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
  2. Ini

    for(x <- c1; y <- c2; z <- c3) yield {...}

    diterjemahkan ke dalam

    c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
  3. 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 withFiltertidak tersedia tetapi filter. Silakan lihat bagian di bawah ini untuk informasi lebih lanjut tentang ini.

  4. Ini

    for(x <- c; y = ...) yield {...}

    diterjemahkan ke dalam

    c.map(x => (x, ...)).map((x,y) => {...})

Ketika Anda melihat forpemahaman yang sangat sederhana , map/ foreachalternatif terlihat, memang, lebih baik. Namun, begitu Anda mulai menyusunnya, Anda dapat dengan mudah tersesat di dalam kurung dan level sarang. Ketika itu terjadi, forpemahaman 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 filtermetode 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 filtersegera diterapkan dengan List, kembali daftar peluang - karena foundadalah false. Hanya kemudian foreachdieksekusi, tetapi, pada saat ini, perubahan foundtidak ada artinya, seperti yang filtertelah dieksekusi.

Dalam hal ini Stream, kondisi tersebut tidak segera diterapkan. Sebagai gantinya, karena setiap elemen diminta oleh foreach, filtermenguji kondisi, yang memungkinkan foreachuntuk 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 ifuntuk 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 Listdengan 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 filterberperilaku. Sebagai catatan tambahan, Rangediubah dari non-ketat menjadi ketat antara Scala 2.7 dan Scala 2.8.

Daniel C. Sobral
sumber
2
Ada metode baru denganFilter di scala 2.8. untuk (x <- c; jika cond) hasil {...} diterjemahkan ke c.withFilter (x => cond) .map (x => {...}) di scala2.8.
Eastsun
2
@ Eastsun Benar, meskipun ada juga fallback otomatis. withFilterseharusnya tidak ketat juga, bahkan untuk koleksi ketat, yang pantas mendapat penjelasan. Saya akan mempertimbangkan ini ...
Daniel C. Sobral
2
@Aniel: Ada perlakuan luar biasa terhadap subjek ini dalam "Programming in Scala", oleh Odersky, dkk. (Saya yakin Anda sudah tahu itu). +1 untuk menunjukkannya.
Ralph
2 poin pertama benar dengan: 1. 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 => {...})))
Dominik
Apakah ini 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?
prostynick
23

Ya, seperti yang dikatakan Earwicker, ini hampir sama dengan LINQ selectdan 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:

  • Jika tipe Anda hanya mendefinisikan map, itu memungkinkan for-expressions yang terdiri dari satu generator.
  • Jika mendefinisikan flatMapserta map, memungkinkan for-expressions terdiri dari beberapa generator.
  • Jika ia mendefinisikan foreach, ia memungkinkan- forloop tanpa hasil (baik dengan generator tunggal dan banyak).
  • Jika itu didefinisikan filter, ini memungkinkan forekspresi-filter yang dimulai dengan if dalam forekspresi.
Alexey Romanov
sumber
2
@Eldritch Conundrum - Yang cukup menarik adalah urutan yang sama di mana spesifikasi SQL asli menguraikan. Di suatu tempat sepanjang bahasa SQL membalikkan urutan, tetapi masuk akal untuk pertama-tama menggambarkan apa yang Anda tarik dari diikuti oleh apa yang Anda harapkan untuk keluar dari itu.
Jordan Parmer
13

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).

Daniel Earwicker
sumber
1
seharusnya "var doubled = for (n <- original) menghasilkan n * 2".
Russel Yang
11

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) idan tambahkan ke daftar A."

Dalam hal notasi set-builder matematis , pemahaman di atas adalah analog dengan

set-notation

yang dapat dibaca sebagai

" Untuk setiap bilangan bulat saya, jika lebih besar dari 3, maka itu adalah anggota himpunan SEBUAH."

atau sebagai alternatif

" SEBUAHAdalah himpunan semua bilangan bulat saya, sehingga masing-masing sayalebih besar dari 3."

Mario Galic
sumber
2

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

Manasa Chada
sumber
Saat menjawab pertanyaan yang sudah lama ini (lebih dari 9 tahun yang lalu), ada baiknya untuk menunjukkan bagaimana jawaban Anda berbeda dari semua jawaban lain yang sudah dikirimkan.
jwvh
Saya pikir mengklarifikasi keraguan itu penting dan tidak memberikan jawaban yang berbeda karena saya juga seorang pemula yang mempelajari bahasa ini. Terima kasih untuk sarannya.
Manasa Chada
0
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.

dotnetN00b
sumber
-3

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.

Michael Peng
sumber
4
Perbandingan itu salah. Anda membandingkan dua hal yang berbeda. Ekspresi dalam hasil sama sekali tidak melakukan hal yang sama dengan ekspresi dalam peta. Juga, itu tidak menunjukkan "fleksibilitas" hasil dibandingkan dengan peta sama sekali.
dotnetN00b