คำตอบคือคำจำกัดความของ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]
ซึ่งหมายความว่าจะต้องจำเป็นต้องกลับมาเป็นประเภทหรือชนิดย่อยของbreakOut
CanBuildFrom[List[String], (Int, String), Map[Int, String]]
List
map