TL; DR ไปที่ตัวอย่างสุดท้ายโดยตรง
ฉันจะพยายามสรุป
คำจำกัดความ
ความforเข้าใจเป็นทางลัดของไวยากรณ์ที่จะรวมเข้าด้วยกันflatMapและmapเป็นวิธีที่ง่ายต่อการอ่านและให้เหตุผล
Let 's สิ่งที่ง่ายบิตและคิดว่าทุกคนclassที่ให้ทั้งสองวิธีดังกล่าวสามารถเรียกmonadและเราจะใช้สัญลักษณ์M[A]หมายถึงกับชนิดภายในmonadA
ตัวอย่าง
พระที่พบเห็นได้ทั่วไป ได้แก่ :
List[String] ที่ไหน
M[X] = List[X]
A = String
Option[Int] ที่ไหน
Future[String => Boolean] ที่ไหน
M[X] = Future[X]
A = (String => Boolean)
แผนที่และ flatMap
กำหนดไว้ใน monad ทั่วไป M[A]
def map(f: A => B): M[B]
def flatMap(f: A => M[B]): M[B]
เช่น
val list = List("neo", "smith", "trinity")
val f: String => List[Int] = s => s.map(_.toInt).toList
list map f
>> List(List(110, 101, 111), List(115, 109, 105, 116, 104), List(116, 114, 105, 110, 105, 116, 121))
list flatMap f
>> List(110, 101, 111, 115, 109, 105, 116, 104, 116, 114, 105, 110, 105, 116, 121)
สำหรับการแสดงออก
แต่ละบรรทัดในนิพจน์โดยใช้<-สัญลักษณ์จะถูกแปลเป็นการflatMapเรียกยกเว้นบรรทัดสุดท้ายซึ่งแปลเป็นการmapเรียกสรุปโดยที่ "สัญลักษณ์ที่ถูกผูกไว้" ทางด้านซ้ายมือจะถูกส่งไปเป็นพารามิเตอร์ไปยังฟังก์ชันอาร์กิวเมนต์ (what เราโทรไปก่อนหน้านี้f: A => M[B]):
for {
bound <- list
out <- f(bound)
} yield out
list.flatMap { bound =>
f(bound).map { out =>
out
}
}
list.flatMap { bound =>
f(bound)
}
list flatMap f
for-expression ที่มีเพียงอันเดียว<-จะถูกแปลงเป็นการmapโทรโดยส่งผ่านนิพจน์เป็นอาร์กิวเมนต์:
for {
bound <- list
} yield f(bound)
list.map { bound =>
f(bound)
}
list map f
ตอนนี้ถึงจุด
อย่างที่คุณเห็นการmapดำเนินการจะรักษา "รูปร่าง" ของต้นฉบับmonadไว้ดังนั้นสิ่งเดียวกันจึงเกิดขึ้นกับyieldนิพจน์: Listยังคงเป็นListเนื้อหาที่เปลี่ยนโดยการดำเนินการในไฟล์yield.
ในทางกลับกันเส้นผูกแต่ละเส้นในนั้นforเป็นเพียงองค์ประกอบของการต่อเนื่องกันmonadsซึ่งจะต้อง "แบน" เพื่อรักษา "รูปทรงภายนอก" เพียงเส้นเดียว
สมมติว่าช่วงเวลาหนึ่งที่การเชื่อมโยงภายในแต่ละรายการได้รับการแปลเป็นmapสาย แต่ทางขวามือเป็นA => M[B]ฟังก์ชันเดียวกันคุณจะได้รับM[M[B]]สำหรับแต่ละบรรทัดในการทำความเข้าใจ
จุดประสงค์ของforไวยากรณ์ทั้งหมดคือการ "แบน" การต่อเนื่องกันของการดำเนินการ monadic ที่ต่อเนื่องกันอย่างง่ายดาย (เช่นการดำเนินการที่ "ยก" ค่าในรูปแบบ "monadic shape":) A => M[B]ด้วยการเพิ่มการmapดำเนินการขั้นสุดท้ายที่อาจดำเนินการแปลงแบบสรุป
ฉันหวังว่านี่จะอธิบายตรรกะที่อยู่เบื้องหลังการเลือกการแปลซึ่งนำไปใช้ในทางกลไกนั่นคือการn flatMapโทรที่ซ้อนกันสรุปโดยการmapโทรเพียงครั้งเดียว
ตัวอย่างภาพประกอบที่สร้างขึ้น
หมายถึงการแสดงความหมายของforไวยากรณ์
case class Customer(value: Int)
case class Consultant(portfolio: List[Customer])
case class Branch(consultants: List[Consultant])
case class Company(branches: List[Branch])
def getCompanyValue(company: Company): Int = {
val valuesList = for {
branch <- company.branches
consultant <- branch.consultants
customer <- consultant.portfolio
} yield (customer.value)
valuesList reduce (_ + _)
}
คุณสามารถเดาประเภทของvaluesList?
ในฐานะที่เป็นแล้วกล่าวว่ารูปร่างของที่monadจะยังคงผ่านความเข้าใจเพื่อให้เราเริ่มต้นด้วยListในและจะต้องจบลงด้วยการcompany.branches
ประเภทภายในจะเปลี่ยนไปแทนและถูกกำหนดโดยนิพจน์: ซึ่งคือList
yieldcustomer.value: Int
valueList ควรเป็น List[Int]