TL; DR ไปที่ตัวอย่างสุดท้ายโดยตรง
ฉันจะพยายามสรุป
คำจำกัดความ
ความfor
เข้าใจเป็นทางลัดของไวยากรณ์ที่จะรวมเข้าด้วยกันflatMap
และmap
เป็นวิธีที่ง่ายต่อการอ่านและให้เหตุผล
Let 's สิ่งที่ง่ายบิตและคิดว่าทุกคนclass
ที่ให้ทั้งสองวิธีดังกล่าวสามารถเรียกmonad
และเราจะใช้สัญลักษณ์M[A]
หมายถึงกับชนิดภายในmonad
A
ตัวอย่าง
พระที่พบเห็นได้ทั่วไป ได้แก่ :
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
yield
customer.value: Int
valueList
ควรเป็น List[Int]