ฉันเข้าใจผลผลิตของ Ruby และ Python ผลผลิตของสกาลาทำอะไร?
ฉันเข้าใจผลผลิตของ Ruby และ Python ผลผลิตของสกาลาทำอะไร?
คำตอบ:
มันถูกใช้ในการทำความเข้าใจตามลำดับ (เช่น list-comprehensions และ Python ที่คุณอาจใช้yield
ด้วย)
มันถูกนำมาใช้ร่วมกับfor
และเขียนองค์ประกอบใหม่ลงในลำดับที่เกิดขึ้น
ตัวอย่างง่าย ๆ (จาก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)
}
}
การแสดงออกที่สอดคล้องกันใน F # จะเป็น
[ for a in args -> a.toUpperCase ]
หรือ
from a in args select a.toUpperCase
ใน Linq
ทับทิมyield
มีผลแตกต่างกัน
ฉันคิดว่าคำตอบที่ยอมรับนั้นยอดเยี่ยม แต่ดูเหมือนว่าหลายคนล้มเหลวที่จะเข้าใจประเด็นพื้นฐานบางอย่าง
ประการแรกความfor
เข้าใจของสกาล่าเทียบเท่ากับของแฮสเคลล์do
สัญกรณ์และไม่มีอะไรมากไปกว่าน้ำตาลซินแทกติกสำหรับองค์ประกอบของการปฏิบัติการแบบ monadic หลายอย่าง เนื่องจากข้อความนี้มักจะไม่ช่วยใครก็ตามที่ต้องการความช่วยเหลือลองอีกครั้ง… :-)
Scala ของfor
comprehensions เป็นน้ำตาลประโยคสำหรับองค์ประกอบของการดำเนินงานหลายแผนที่, และflatMap
หรือfilter
foreach
สกาล่าจะแปลคำว่าfor
-expression เป็นการเรียกไปยังวิธีการเหล่านั้นดังนั้นคลาสใดก็ตามที่ให้พวกมันหรือเซตย่อยของพวกมันสามารถใช้กับความเข้าใจได้
ก่อนอื่นเรามาพูดถึงการแปล มีกฎง่าย ๆ :
นี้
for(x <- c1; y <- c2; z <-c3) {...}
ถูกแปลเป็น
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
นี้
for(x <- c1; y <- c2; z <- c3) yield {...}
ถูกแปลเป็น
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
นี้
for(x <- c; if cond) yield {...}
มีการแปลใน Scala 2.7 เป็น
c.filter(x => cond).map(x => {...})
หรือบน Scala 2.8 เป็น
c.withFilter(x => cond).map(x => {...})
ด้วยทางเลือกในอดีตถ้าวิธีการwithFilter
ไม่สามารถใช้ได้ แต่filter
เป็น โปรดดูส่วนด้านล่างสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้
นี้
for(x <- c; y = ...) yield {...}
ถูกแปลเป็น
c.map(x => (x, ...)).map((x,y) => {...})
เมื่อคุณดูความfor
เข้าใจที่ง่ายมาก ๆทางเลือกmap
/ foreach
ดูดีกว่าจริง ๆ เมื่อคุณเริ่มเขียนมันคุณสามารถหลงทางในวงเล็บและระดับการซ้อน เมื่อสิ่งนั้นเกิดขึ้นfor
ความเข้าใจมักจะชัดเจนกว่ามาก
ฉันจะแสดงตัวอย่างง่ายๆอย่างหนึ่งและจงใจละเว้นคำอธิบายใด ๆ คุณสามารถตัดสินใจได้ว่ารูปแบบใดที่เข้าใจได้ง่ายขึ้น
l.flatMap(sl => sl.filter(el => el > 0).map(el => el.toString.length))
หรือ
for {
sl <- l
el <- sl
if el > 0
} yield el.toString.length
withFilter
Scala 2.8 แนะนำวิธีการที่เรียกว่าwithFilter
มีความแตกต่างที่สำคัญคือแทนที่จะส่งคืนคอลเลกชันใหม่ที่กรองแล้วจะกรองตามความต้องการ filter
วิธีการได้กำหนดพฤติกรรมของมันขึ้นอยู่กับความเข้มงวดของคอลเลกชัน เพื่อให้เข้าใจสิ่งนี้ดีขึ้นลองดูที่ Scala 2.7 บางส่วนกับList
(เข้มงวด) และStream
(ไม่เข้มงวด):
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
ความแตกต่างที่เกิดขึ้นเพราะfilter
ถูกนำไปใช้ทันทีที่มีList
กลับรายการอัตราต่อรอง - ตั้งแต่เป็นfound
false
เท่านั้นแล้วforeach
จะถูกดำเนินการ แต่คราวนี้เปลี่ยนfound
เป็นความหมายตามที่filter
ได้ดำเนินการอยู่แล้ว
ในกรณีของStream
เงื่อนไขจะไม่ถูกนำมาใช้ทันที แต่ตามที่แต่ละองค์ประกอบถูกร้องขอโดยforeach
ให้filter
ทดสอบเงื่อนไขซึ่งทำให้foreach
สามารถมีอิทธิพลต่อมันfound
ได้ เพียงเพื่อให้ชัดเจนนี่คือรหัสเทียบเท่าสำหรับความเข้าใจ:
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)
สิ่งนี้ทำให้เกิดปัญหามากมายเพราะคนคาดว่าif
จะได้รับการพิจารณาตามความต้องการแทนที่จะนำไปใช้กับการเก็บรวบรวมทั้งหมดก่อน
แนะนำ Scala 2.8 withFilter
ซึ่งไม่เข้มงวดเสมอไม่ว่าจะมีความเข้มงวดในการสะสมอย่างไร ตัวอย่างต่อไปนี้แสดงList
ด้วยวิธีทั้งสองใน 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
สิ่งนี้สร้างผลลัพธ์ที่คนส่วนใหญ่คาดหวังโดยไม่เปลี่ยนfilter
พฤติกรรม ในฐานะที่เป็นข้อความด้านRange
ถูกเปลี่ยนจากไม่เข้มงวดเป็นเข้มงวดระหว่าง Scala 2.7 และ Scala 2.8
withFilter
ควรจะไม่เข้มงวดเช่นกันแม้กระทั่งคอลเลกชันที่เข้มงวดซึ่งสมควรได้รับคำอธิบายบางอย่าง ฉันจะพิจารณาเรื่องนี้ ...
for(x <- c; y <- x; z <-y) {...}
แปลเป็นc.foreach(x => x.foreach(y => y.foreach(z => {...})))
2. for(x <- c; y <- x; z <- y) yield {...}
แปลเป็นc.flatMap(x => x.flatMap(y => y.map(z => {...})))
for(x <- c; y = ...) yield {...}
แปลออกมาจริงๆc.map(x => (x, ...)).map((x,y) => {...})
เหรอ? ฉันคิดว่ามันแปลc.map(x => (x, ...)).map(x => { ...use x._1 and x._2 here...})
เป็
ใช่อย่างที่ Earwicker พูดมันเหมือนกับของ LINQ select
และมีส่วนเกี่ยวข้องกับ Ruby และ Python เพียงเล็กน้อยyield
เท่านั้น โดยทั่วไปใน C # คุณจะเขียน
from ... select ???
ในสกาล่าคุณมีแทน
for ... yield ???
สิ่งสำคัญคือต้องเข้าใจว่า - ความเข้าใจfor
ไม่เพียง แต่ทำงานกับลำดับเท่านั้น แต่ด้วยประเภทใด ๆ ที่กำหนดวิธีการบางอย่างเช่น LINQ:
map
มันจะช่วยให้for
-expressions ประกอบด้วยเครื่องกำเนิดไฟฟ้าเดียวflatMap
เช่นเดียวกับmap
มันจะช่วยให้for
-expressions ประกอบด้วยเครื่องกำเนิดไฟฟ้าหลายforeach
จะอนุญาตให้for
-loops โดยไม่มีผลตอบแทน (ทั้งที่มีเครื่องกำเนิดเดี่ยวและหลายเครื่อง)filter
จะช่วยให้for
การแสดงออก -filter เริ่มต้นด้วยif
ในfor
การแสดงออกถ้าคุณไม่ได้รับคำตอบที่ดีกว่าจากผู้ใช้ Scala (ซึ่งฉันไม่ใช่) นี่คือความเข้าใจของฉัน
มันจะปรากฏขึ้นเป็นส่วนหนึ่งของการแสดงออกเริ่มต้นด้วยfor
ซึ่งระบุวิธีการสร้างรายการใหม่จากรายการที่มีอยู่
สิ่งที่ต้องการ:
var doubled = for (n <- original) yield n * 2
ดังนั้นจึงมีรายการเอาท์พุทหนึ่งรายการสำหรับแต่ละอินพุต (แม้ว่าฉันเชื่อว่ามีวิธีการทำสำเนาที่ซ้ำกัน)
นี่ค่อนข้างแตกต่างจาก "ความต่อเนื่องที่จำเป็น" ที่เปิดใช้งานโดยผลผลิตในภาษาอื่นซึ่งให้วิธีการสร้างรายการความยาวใด ๆ จากรหัสจำเป็นบางอย่างที่มีโครงสร้างเกือบทุกชนิด
(หากคุณคุ้นเคยกับ C # จะใกล้กับผู้ให้บริการของ LINQ select
มากกว่าที่จะเป็นyield return
)
คำหลักyield
ใน Scala เป็นเพียงน้ำตาลประโยคซึ่งสามารถแทนที่ได้ง่ายโดย a map
เนื่องจากDaniel Sobral ได้อธิบายรายละเอียดไว้แล้ว
บนมืออื่น ๆ ที่yield
เป็นอย่างที่ทำให้เข้าใจผิดถ้าคุณกำลังมองหาเครื่องกำเนิดไฟฟ้า (หรือต) คล้ายกับผู้ที่อยู่ในหลาม ดูเธรด SO นี้สำหรับข้อมูลเพิ่มเติม: วิธีที่เหมาะสมในการใช้ 'yield' ใน Scala คืออะไร
พิจารณาความเข้าใจต่อไปนี้
val A = for (i <- Int.MinValue to Int.MaxValue; if i > 3) yield i
มันอาจจะเป็นประโยชน์ในการอ่านออกเสียงดังต่อไปนี้
" สำหรับแต่ละจำนวนเต็มi
, ถ้ามันเป็นมากกว่า3
นั้นผลผลิต (การผลิต) i
และเพิ่มเข้าไปในรายการA
".
ในแง่ของสัญกรณ์ set-builderคณิตศาสตร์ข้างต้นสำหรับความเข้าใจนั้นคล้ายคลึงกับ
ซึ่งอาจจะอ่านว่า
" สำหรับแต่ละจำนวนเต็ม, ถ้ามันเป็นมากกว่านั้นก็เป็นสมาชิกคนหนึ่งของชุด."
หรืออีกวิธีหนึ่งคือ
" คือชุดของจำนวนเต็มทั้งหมดซึ่งแต่ละชุดมีค่ามากกว่า"
Yield นั้นคล้ายกับลูปที่มีบัฟเฟอร์ซึ่งเราไม่สามารถมองเห็นและสำหรับการเพิ่มทีละแต่ละมันยังคงเพิ่มรายการถัดไปในบัฟเฟอร์ เมื่อการวนรอบสำหรับเสร็จสิ้นการทำงานก็จะส่งกลับคอลเลกชันของค่าที่ได้ทั้งหมด Yield สามารถใช้เป็นตัวดำเนินการทางคณิตศาสตร์อย่างง่ายหรือแม้แต่ใช้ร่วมกับอาร์เรย์ ต่อไปนี้เป็นสองตัวอย่างง่ายๆเพื่อความเข้าใจที่ดีขึ้นของคุณ
scala>for (i <- 1 to 5) yield i * 3
res: scala.collection.immutable.IndexedSeq [Int] = เวกเตอร์ (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)] = รายการ ((1, a), (1, b), (1, c), (2, a), (2, b), (2, c), ( 3, a), (3, b), (3, c))
หวังว่านี่จะช่วยได้ !!
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 )
โค้ดสองชิ้นนี้เทียบเท่ากัน
val res3 = for (al <- aList) yield al + 1 > 3
val res4 = aList.map( _+ 1 > 3 )
println( res3 )
println( res4 )
โค้ดสองชิ้นนี้เทียบเท่ากัน
แผนที่มีความยืดหยุ่นเท่ากับผลผลิตและในทางกลับกัน
ผลผลิตมีความยืดหยุ่นมากกว่าแผนที่ () ดูตัวอย่างด้านล่าง
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 )
ผลผลิตจะพิมพ์ผลเช่น: รายชื่อ (5, 6) ซึ่งเป็นสิ่งที่ดี
ในขณะที่ map () จะแสดงผลลัพธ์ดังนี้: รายการ (false, false, true, true, true) ซึ่งอาจไม่ใช่สิ่งที่คุณตั้งใจ