คำตอบคือคำจำกัดความของmap:
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
โปรดทราบว่ามันมีสองพารามิเตอร์ ที่แรกก็คือฟังก์ชั่นของคุณและที่สองคือความหมายโดยนัย หากคุณไม่ได้ระบุโดยนัย Scala จะเลือกเฉพาะที่มีอยู่มากที่สุด
เกี่ยวกับ breakOut
ดังนั้นวัตถุประสงค์ของbreakOutอะไร ลองพิจารณาตัวอย่างที่ให้มาสำหรับคำถามคุณนำรายการสตริงเปลี่ยนสตริงแต่ละตัวเป็น tuple (Int, String)จากนั้นสร้างสตริงMapขึ้นมา วิธีที่ชัดเจนที่สุดในการทำเช่นนั้นจะสร้างList[(Int, String)]คอลเลกชันตัวกลางและจากนั้นแปลง
เนื่องจากการmapใช้ a Builderในการสร้างคอลเลกชันที่เกิดขึ้นเป็นไปได้หรือไม่ที่จะข้ามคนกลางListและรวบรวมผลลัพธ์โดยตรงในMap? เห็นได้ชัดว่าใช่มันเป็น ต้องการทำเช่นนั้น แต่เราต้องผ่านที่เหมาะสมCanBuildFromไปmapและนั่นคือสิ่งที่breakOutไม่
ลองดูที่นิยามของbreakOut:
def breakOut[From, T, To](implicit b : CanBuildFrom[Nothing, T, To]) =
new CanBuildFrom[From, T, To] {
def apply(from: From) = b.apply() ; def apply() = b.apply()
}
ทราบว่าเป็นพารามิเตอร์และที่มันกลับตัวอย่างของbreakOut CanBuildFromมันเกิดขึ้นชนิดFrom, TและToได้รับการสรุปเพราะเรารู้ว่าเป็นที่คาดหวังว่าmap CanBuildFrom[List[String], (Int, String), Map[Int, String]]ดังนั้น:
From = List[String]
T = (Int, String)
To = Map[Int, String]
เพื่อสรุปขอตรวจสอบนัยโดยbreakOutตัวของมันเอง CanBuildFrom[Nothing,T,To]มันเป็นประเภท CanBuildFrom[Nothing,(Int,String),Map[Int,String]]เรารู้อยู่แล้วทุกประเภทเหล่านี้เราจึงสามารถระบุได้ว่าเราจำเป็นต้องมีนัยประเภท แต่มีคำจำกัดความดังกล่าวหรือไม่?
ลองดูCanBuildFromคำจำกัดความของ:
trait CanBuildFrom[-From, -Elem, +To]
extends AnyRef
ดังนั้นจึงCanBuildFromเป็นตัวแปรในตัวแปรชนิดแรก เพราะNothingเป็นชั้นล่าง (เช่นมันเป็น subclass ของทุกอย่าง) ซึ่งหมายถึงการใด ๆNothingในชั้นเรียนสามารถนำมาใช้ในสถานที่ของ
เนื่องจากผู้สร้างดังกล่าวมีอยู่ Scala สามารถใช้มันเพื่อสร้างผลลัพธ์ที่ต้องการ
เกี่ยวกับผู้สร้าง
วิธีการมากมายจากไลบรารีของคอลเลกชันสกาล่าประกอบด้วยการรวบรวมคอลเลกชันดั้งเดิมประมวลผลอย่างใด (ในกรณีของการmapเปลี่ยนองค์ประกอบแต่ละส่วน) และเก็บผลลัพธ์ในคอลเลกชันใหม่
เพื่อเพิ่มการใช้งานซ้ำของรหัสได้มากที่สุดการจัดเก็บผลลัพธ์นี้กระทำผ่านตัวสร้าง ( scala.collection.mutable.Builder) ซึ่งโดยทั่วไปรองรับการทำงานสองอย่าง: การต่อท้ายองค์ประกอบและการส่งคืนการรวบรวมผลลัพธ์ ประเภทของการรวบรวมผลลัพธ์นี้จะขึ้นอยู่กับประเภทของผู้สร้าง ดังนั้นการListสร้างจะกลับListเป็นMapผู้สร้างจะกลับMapและอื่น ๆ การใช้mapวิธีนี้ไม่จำเป็นต้องเกี่ยวข้องกับประเภทของผลลัพธ์: ตัวสร้างจะดูแลมัน
ในทางตรงกันข้ามนั่นหมายความว่าmapจำเป็นต้องได้รับการสร้างนี้อย่างใด ปัญหาที่ต้องเผชิญเมื่อออกแบบ Scala 2.8 กลุ่มคือวิธีการเลือกผู้สร้างที่ดีที่สุด ตัวอย่างเช่นถ้าฉันจะเขียนMap('a' -> 1).map(_.swap)ฉันต้องการMap(1 -> 'a')กลับไป ในทางกลับกัน a Map('a' -> 1).map(_._1)ไม่สามารถส่งคืน a Map(จะส่งคืน an Iterable)
ความมหัศจรรย์ของการผลิตที่ดีที่สุดBuilderจากประเภทที่รู้จักกันในการแสดงออกจะดำเนินการผ่านทางนี้CanBuildFromโดยปริยาย
เกี่ยวกับ CanBuildFrom
ที่ดีกว่าการอธิบายสิ่งที่เกิดขึ้นผมจะยกตัวอย่างที่คอลเลกชันที่ถูกแมปเป็นผู้มีแทนMap ListฉันจะกลับไปListทีหลัง สำหรับตอนนี้ให้ลองพิจารณานิพจน์ทั้งสองนี้:
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
ผลตอบแทนที่แรกและผลตอบแทนที่สองMap ความมหัศจรรย์ของการกลับมาคอลเลกชันที่เหมาะสมคือการทำงานของIterable CanBuildFromลองพิจารณานิยามของmapอีกครั้งเพื่อทำความเข้าใจ
วิธีการที่จะรับมาจากmap TraversableLikeมันเป็นพารามิเตอร์ในBและThatและใช้ประโยชน์จากพารามิเตอร์ประเภทAและReprซึ่งแปรสภาพชั้นเรียน ลองดูคำจำกัดความทั้งสองร่วมกัน:
คลาสTraversableLikeถูกกำหนดเป็น:
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
เพื่อให้เข้าใจถึงที่AและReprมาจากการขอพิจารณาความหมายของMapตัวเอง:
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
เพราะTraversableLikeมีการสืบทอดโดยทุกลักษณะที่ขยายMap, AและReprจะได้รับการสืบทอดมาจากส่วนใดของพวกเขา คนสุดท้ายได้รับการตั้งค่าแม้ว่า ดังนั้นตามคำจำกัดความของลักษณะที่เปลี่ยนไม่ได้Mapและคุณลักษณะทั้งหมดที่เชื่อมต่อกับTraversableLikeเรา:
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends MapLike[A, B, This]
trait MapLike[A, +B, +This <: MapLike[A, B, This] with Map[A, B]]
extends PartialFunction[A, B] with IterableLike[(A, B), This] with Subtractable[A, This]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
หากคุณผ่านพารามิเตอร์ประเภทของMap[Int, String]ห่วงโซ่ทั้งหมดเราจะพบว่าประเภทที่ส่งผ่านไปTraversableLikeและดังนั้นจึงถูกใช้โดยmap:
A = (Int,String)
Repr = Map[Int, String]
จะกลับไปตัวอย่างแผนที่แรกที่ได้รับการทำงานของประเภทและแผนที่ที่สองได้รับการทำงานของประเภท((Int, String)) => (Int, Int) ((Int, String)) => Stringฉันใช้เครื่องหมายวงเล็บคู่เพื่อเน้นว่าเป็นสิ่งอันดับที่ได้รับเนื่องจากเป็นประเภทAที่เราเห็น
ด้วยข้อมูลนั้นลองพิจารณาประเภทอื่น ๆ
map Function.tupled(_ -> _.length):
B = (Int, Int)
map (_._2):
B = String
เราจะเห็นว่าประเภทที่ส่งกลับโดยครั้งแรกmapเป็นและที่สองคือMap[Int,Int] Iterable[String]มองไปที่ความหมายของมันเป็นเรื่องง่ายที่จะเห็นว่าสิ่งเหล่านี้เป็นค่าของmap Thatแต่พวกเขามาจากไหน
ถ้าเราดูภายในวัตถุที่เป็นคู่หูของคลาสที่เกี่ยวข้องเราจะเห็นการประกาศโดยนัยที่ให้พวกมัน บนวัตถุMap:
implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]
และบนวัตถุIterableซึ่งคลาสถูกขยายโดยMap:
implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]
CanBuildFromคำนิยามเหล่านี้ให้โรงงานแปร
สกาล่าจะเลือกเฉพาะนัยที่สุดที่มีอยู่ CanBuildFromในกรณีแรกก็เป็นครั้งแรกที่ CanBuildFromในกรณีที่สองเป็นครั้งแรกที่ไม่ตรงกับมันเลือกที่สอง
กลับไปที่คำถาม
ลองดูรหัสสำหรับคำถามที่ว่าList'และmap' s นิยาม (อีกครั้ง) เพื่อดูว่าประเภทที่มีการอ้างถึง:
val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)
sealed abstract class List[+A]
extends LinearSeq[A] with Product with GenericTraversableTemplate[A, List] with LinearSeqLike[A, List[A]]
trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]]
extends SeqLike[A, Repr]
trait SeqLike[+A, +Repr]
extends IterableLike[A, Repr]
trait IterableLike[+A, +Repr]
extends Equals with TraversableLike[A, Repr]
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
ประเภทของList("London", "Paris")คือList[String]ดังนั้นประเภทAและReprกำหนดไว้TraversableLikeคือ:
A = String
Repr = List[String]
ประเภทของ(x => (x.length, x))คือ(String) => (Int, String)ดังนั้นประเภทBคือ:
B = (Int, String)
ประเภทที่ไม่รู้จักครั้งสุดท้ายThatเป็นประเภทของผลลัพธ์mapและเราก็มีเช่นกัน:
val map : Map[Int,String] =
ดังนั้น,
That = Map[Int, String]
ซึ่งหมายความว่าจะต้องจำเป็นต้องกลับมาเป็นประเภทหรือชนิดย่อยของbreakOutCanBuildFrom[List[String], (Int, String), Map[Int, String]]
Listmap