Câu trả lời:
Nó được sử dụng để hiểu theo trình tự (như hiểu và liệt kê danh sách của Python, nơi bạn cũng có thể sử dụng yield
).
Nó được áp dụng kết hợp với for
và viết một phần tử mới vào chuỗi kết quả.
Ví dụ đơn giản (từ 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)
}
}
Biểu thức tương ứng trong F # sẽ là
[ for a in args -> a.toUpperCase ]
hoặc là
from a in args select a.toUpperCase
ở Linq.
Ruby yield
có một hiệu ứng khác.
Tôi nghĩ rằng câu trả lời được chấp nhận là tuyệt vời, nhưng có vẻ như nhiều người đã không nắm bắt được một số điểm cơ bản.
Đầu tiên, sự for
hiểu biết của Scala tương đương với do
ký hiệu của Haskell , và nó không có gì khác hơn là một cú pháp cú pháp cho thành phần của nhiều hoạt động đơn nguyên. Vì tuyên bố này rất có thể sẽ không giúp được ai cần giúp đỡ, chúng ta hãy thử lại LỚN :-)
Sự for
hiểu biết của Scala là đường cú pháp cho thành phần của nhiều hoạt động với bản đồ, flatMap
và filter
. Hoặc foreach
. Scala thực sự chuyển một for
biểu hiện thành các cuộc gọi đến các phương thức đó, vì vậy bất kỳ lớp nào cung cấp chúng, hoặc một tập hợp con của chúng, đều có thể được sử dụng để hiểu.
Đầu tiên, hãy nói về các bản dịch. Có những quy tắc rất đơn giản:
Điều này
for(x <- c1; y <- c2; z <-c3) {...}
được dịch sang
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
Điều này
for(x <- c1; y <- c2; z <- c3) yield {...}
được dịch sang
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
Điều này
for(x <- c; if cond) yield {...}
được dịch trên Scala 2.7 thành
c.filter(x => cond).map(x => {...})
hoặc, trên Scala 2.8, vào
c.withFilter(x => cond).map(x => {...})
với một dự phòng trước đây nếu phương thức withFilter
không có sẵn nhưng filter
là. Vui lòng xem phần bên dưới để biết thêm thông tin về điều này.
Điều này
for(x <- c; y = ...) yield {...}
được dịch sang
c.map(x => (x, ...)).map((x,y) => {...})
Khi bạn nhìn vào những for
hiểu biết rất đơn giản , map
/ foreach
cái nhìn thay thế, thực sự, tốt hơn. Tuy nhiên, khi bạn bắt đầu soạn chúng, bạn có thể dễ dàng bị lạc trong các mức ngoặc đơn và lồng nhau. Khi điều đó xảy ra, for
sự hiểu biết thường rõ ràng hơn nhiều.
Tôi sẽ chỉ ra một ví dụ đơn giản và cố tình bỏ qua mọi lời giải thích. Bạn có thể quyết định cú pháp nào dễ hiểu hơn.
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
hoặc là
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
Scala 2.8 đã giới thiệu một phương thức được gọi withFilter
, với sự khác biệt chính là, thay vì trả về một bộ sưu tập mới, được lọc, nó sẽ lọc theo yêu cầu. Các filter
phương pháp có hành vi của nó được xác định căn cứ vào tính nghiêm minh của bộ sưu tập. Để hiểu rõ hơn về điều này, chúng ta hãy xem một số Scala 2.7 với List
(nghiêm ngặt) và Stream
(không nghiêm ngặt):
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
Sự khác biệt xảy ra vì filter
ngay lập tức được áp dụng với List
, trả lại một danh sách các tỷ lệ cược - kể từ khi found
là false
. Chỉ sau đó foreach
được thực thi, nhưng, đến lúc này, thay đổi found
là vô nghĩa, như filter
đã thực hiện.
Trong trường hợp Stream
, điều kiện không được áp dụng ngay lập tức. Thay vào đó, vì mỗi yếu tố được yêu cầu bởi foreach
, filter
kiểm tra điều kiện, cho phép foreach
ảnh hưởng đến nó found
. Nói rõ hơn, đây là mã tương đương để hiểu:
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)
Điều này gây ra nhiều vấn đề, bởi vì mọi người dự kiến sẽ if
được xem xét theo yêu cầu, thay vì được áp dụng cho toàn bộ bộ sưu tập trước đó.
Scala 2.8 được giới thiệu withFilter
, luôn luôn không nghiêm ngặt, bất kể sự nghiêm ngặt của bộ sưu tập. Ví dụ sau đây cho thấy List
với cả hai phương thức trên 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
Điều này tạo ra kết quả mà hầu hết mọi người mong đợi, mà không thay đổi cách filter
cư xử. Là một lưu ý phụ, Range
đã được thay đổi từ không nghiêm ngặt sang nghiêm ngặt giữa Scala 2.7 và Scala 2.8.
withFilter
được cho là không nghiêm ngặt, ngay cả đối với các bộ sưu tập nghiêm ngặt, xứng đáng được giải thích. Tôi sẽ xem xét điều này ...
for(x <- c; y <- x; z <-y) {...}
được dịch thành c.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
được dịch thànhc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
thực sự được dịch sang c.map(x => (x, ...)).map((x,y) => {...})
? Tôi nghĩ rằng nó được dịch sang c.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
hoặc tôi đang thiếu một cái gì đó?
Vâng, như Earwicker đã nói, nó khá giống với LINQ select
và có rất ít liên quan đến Ruby và Python yield
. Về cơ bản, nơi C # bạn sẽ viết
from ... select ???
trong Scala bạn có thay thế
for ... yield ???
Điều quan trọng nữa là phải hiểu rằng - các giải for
pháp không chỉ hoạt động với các chuỗi, mà với bất kỳ loại nào xác định các phương thức nhất định, giống như LINQ:
map
, nó cho phép các for
biểu hiện bao gồm một trình tạo duy nhất.flatMap
cũng như map
, nó cho phép các for
biểu hiện bao gồm một số máy phát điện.foreach
, nó cho phép for
-loop mà không mang lại năng suất (cả với máy phát đơn và nhiều máy phát).filter
, nó cho phép các for
biểu thức lọc bắt đầu bằng một if
trong for
biểu thức.Trừ khi bạn nhận được câu trả lời tốt hơn từ người dùng Scala (mà tôi không biết), đây là sự hiểu biết của tôi.
Nó chỉ xuất hiện như một phần của biểu thức bắt đầu bằng for
, trong đó nêu cách tạo danh sách mới từ danh sách hiện có.
Cái gì đó như:
var doubled = for (n <- original) yield n * 2
Vì vậy, có một mục đầu ra cho mỗi đầu vào (mặc dù tôi tin rằng có một cách để loại bỏ trùng lặp).
Điều này khác hoàn toàn với "các mệnh lệnh bắt buộc" được kích hoạt bởi năng suất trong các ngôn ngữ khác, nơi nó cung cấp một cách để tạo một danh sách có độ dài bất kỳ, từ một số mã mệnh lệnh với hầu hết mọi cấu trúc.
(Nếu bạn quen thuộc với C #, nó gần với nhà điều hành của LINQ select
hơn là với nó yield return
).
Từ khóa yield
trong Scala chỉ đơn giản là đường cú pháp có thể dễ dàng thay thế bằng a map
, như Daniel Sobral đã giải thích chi tiết.
Mặt khác, yield
hoàn toàn sai lệch nếu bạn đang tìm kiếm các trình tạo (hoặc tiếp tục) tương tự như trong Python . Xem chủ đề SO này để biết thêm thông tin: Cách ưa thích để thực hiện 'năng suất' trong Scala là gì?
Hãy xem xét những điều sau đây để hiểu
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
Có thể hữu ích để đọc thành tiếng như sau
" Đối với mỗi số nguyên i
, nếu nó lớn hơn 3
, thì sản lượng (sản xuất) i
và thêm nó vào danh sách A
."
Về mặt ký hiệu của trình xây dựng tập hợp toán học , cách hiểu ở trên tương tự như
có thể được đọc là
" Đối với mỗi số nguyên , nếu nó lớn hơn , thì nó là thành viên của tập hợp ."
hoặc cách khác là
" là tập hợp của tất cả các số nguyên , sao cho mỗi số nguyên lớn hơn ."
Năng suất tương tự như vòng lặp có bộ đệm mà chúng ta không thể nhìn thấy và với mỗi mức tăng, nó tiếp tục thêm mục tiếp theo vào bộ đệm. Khi vòng lặp for kết thúc chạy, nó sẽ trả về tập hợp tất cả các giá trị mang lại. Năng suất có thể được sử dụng như các toán tử số học đơn giản hoặc thậm chí kết hợp với các mảng. Đây là hai ví dụ đơn giản để bạn hiểu rõ hơn
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = Vector (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)] = Danh sách ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
Hi vọng điêu nay co ich!!
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 )
Hai đoạn mã này là tương đương.
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
Hai đoạn mã này cũng tương đương.
Bản đồ linh hoạt như năng suất và ngược lại.
năng suất linh hoạt hơn so với map (), xem ví dụ bên dưới
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 )
năng suất sẽ in kết quả như: Danh sách (5, 6), tốt
while map () sẽ trả về kết quả như: List (false, false, true, true, true), có lẽ không phải là điều bạn dự định.