Câu trả lời được tìm thấy trên định nghĩa của map
:
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
Lưu ý rằng nó có hai tham số. Đầu tiên là chức năng của bạn và thứ hai là ẩn. Nếu bạn không cung cấp ẩn ý đó, Scala sẽ chọn một cái cụ thể nhất có sẵn.
Trong khoảng breakOut
Vậy, mục đích của nó là breakOut
gì? Xem xét ví dụ được đưa ra cho câu hỏi, Bạn lấy một danh sách các chuỗi, chuyển đổi từng chuỗi thành một tuple (Int, String)
và sau đó tạo ra một Map
chuỗi. Cách rõ ràng nhất để làm điều đó sẽ tạo ra một List[(Int, String)]
bộ sưu tập trung gian , và sau đó chuyển đổi nó.
Cho rằng map
sử dụng a Builder
để tạo ra bộ sưu tập kết quả, không thể bỏ qua trung gian List
và thu thập kết quả trực tiếp vào một Map
? Rõ ràng, vâng, nó là. Để làm như vậy, tuy nhiên, chúng ta cần phải vượt qua một thích hợp CanBuildFrom
để map
, và đó là chính xác những gì breakOut
không.
Sau đó, hãy nhìn vào định nghĩa của 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()
}
Lưu ý rằng breakOut
được tham số hóa và nó trả về một thể hiện của CanBuildFrom
. Khi nó xảy ra, các loại From
, T
và To
đã được suy luận, bởi vì chúng tôi biết rằng đó map
là mong đợi CanBuildFrom[List[String], (Int, String), Map[Int, String]]
. Vì thế:
From = List[String]
T = (Int, String)
To = Map[Int, String]
Để kết luận chúng ta hãy kiểm tra ngầm nhận được breakOut
. Nó là loại CanBuildFrom[Nothing,T,To]
. Chúng tôi đã biết tất cả các loại này, vì vậy chúng tôi có thể xác định rằng chúng tôi cần một loại ngầm địnhCanBuildFrom[Nothing,(Int,String),Map[Int,String]]
. Nhưng có một định nghĩa như vậy?
Hãy xem CanBuildFrom
định nghĩa của:
trait CanBuildFrom[-From, -Elem, +To]
extends AnyRef
Vì vậy, CanBuildFrom
biến thể contra trên tham số loại đầu tiên của nó. Bởi vì Nothing
là một lớp dưới cùng (nghĩa là nó là một lớp con của mọi thứ), điều đó có nghĩa là bất kỳ lớp nào cũng có thể được sử dụng thay thế Nothing
.
Vì một trình xây dựng như vậy tồn tại, Scala có thể sử dụng nó để tạo ra đầu ra mong muốn.
Giới thiệu về nhà xây dựng
Rất nhiều phương pháp từ thư viện bộ sưu tập của Scala bao gồm lấy bộ sưu tập gốc, xử lý nó bằng cách nào đó (trong trường hợp map
, biến đổi từng phần tử) và lưu trữ kết quả trong bộ sưu tập mới.
Để tối đa hóa việc sử dụng lại mã, việc lưu trữ kết quả này được thực hiện thông qua trình xây dựng ( scala.collection.mutable.Builder
), về cơ bản hỗ trợ hai thao tác: nối các phần tử và trả về bộ sưu tập kết quả. Loại của bộ sưu tập kết quả này sẽ phụ thuộc vào loại của người xây dựng. Do đó, một List
người xây dựng sẽ trả lại một List
, một Map
người xây dựng sẽ trả lại một Map
, v.v. Việc thực hiện map
phương thức không cần phải quan tâm đến loại kết quả: người xây dựng sẽ chăm sóc nó.
Mặt khác, điều đó có nghĩa là map
cần phải nhận được trình xây dựng này bằng cách nào đó. Vấn đề gặp phải khi thiết kế Bộ sưu tập Scala 2.8 là làm thế nào để chọn người xây dựng tốt nhất có thể. Ví dụ, nếu tôi viết Map('a' -> 1).map(_.swap)
, tôi muốn lấy Map(1 -> 'a')
lại. Mặt khác, Map('a' -> 1).map(_._1)
không thể trả về một Map
(nó trả về một Iterable
).
Phép thuật tạo ra thứ tốt nhất có thể Builder
từ các loại biểu thức đã biết được thực hiện thông qua CanBuildFrom
ẩn này .
Trong khoảng CanBuildFrom
Để giải thích rõ hơn những gì đang diễn ra, tôi sẽ đưa ra một ví dụ trong đó bộ sưu tập được ánh xạ Map
thay vì a List
. Tôi sẽ quay lại List
sau. Bây giờ, hãy xem xét hai biểu thức sau:
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
Cái đầu tiên trả về a Map
và cái thứ hai trả về một Iterable
. Sự kỳ diệu của việc trả lại một bộ sưu tập phù hợp là công việc của CanBuildFrom
. Hãy xem xét định nghĩa của map
một lần nữa để hiểu nó.
Phương pháp map
được kế thừa từ TraversableLike
. Nó được tham số hóa trên B
và That
, và sử dụng các tham số loại A
và Repr
, tham số hóa lớp. Chúng ta hãy xem cả hai định nghĩa cùng nhau:
Lớp TraversableLike
được định nghĩa là:
trait TraversableLike[+A, +Repr]
extends HasNewBuilder[A, Repr] with AnyRef
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
Để hiểu nơi A
và Repr
đến từ đâu, hãy xem xét định nghĩa của Map
chính nó:
trait Map[A, +B]
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
Bởi vì TraversableLike
được thừa hưởng tất cả những đặc điểm mà mở rộng Map
, A
và Repr
có thể được thừa hưởng từ ai trong số họ. Người cuối cùng được ưu tiên, mặc dù. Vì vậy, theo định nghĩa của bất biến Map
và tất cả các đặc điểm kết nối nó với TraversableLike
, chúng ta có:
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
Nếu bạn chuyển các tham số loại của Map[Int, String]
tất cả các chuỗi xuống, chúng tôi thấy rằng các loại được truyền cho TraversableLike
, và, do đó, được sử dụng bởi map
, là:
A = (Int,String)
Repr = Map[Int, String]
Quay trở lại ví dụ, bản đồ thứ nhất đang nhận chức năng loại ((Int, String)) => (Int, Int)
và bản đồ thứ hai đang nhận chức năng loại ((Int, String)) => String
. Tôi sử dụng dấu ngoặc đơn để nhấn mạnh rằng đó là một bộ dữ liệu được nhận, vì đó là loại A
như chúng ta đã thấy.
Với thông tin đó, hãy xem xét các loại khác.
map Function.tupled(_ -> _.length):
B = (Int, Int)
map (_._2):
B = String
Chúng ta có thể thấy rằng loại được trả về bởi cái đầu tiên map
là Map[Int,Int]
, và cái thứ hai là Iterable[String]
. Nhìn vào map
định nghĩa của nó, dễ dàng nhận thấy đây là những giá trị của That
. Nhưng họ đến từ đâu?
Nếu chúng ta nhìn vào bên trong các đối tượng đồng hành của các lớp liên quan, chúng ta sẽ thấy một số khai báo ngầm cung cấp chúng. Về đối tượng Map
:
implicit def canBuildFrom [A, B] : CanBuildFrom[Map, (A, B), Map[A, B]]
Và trên đối tượng Iterable
, có lớp được mở rộng bởi Map
:
implicit def canBuildFrom [A] : CanBuildFrom[Iterable, A, Iterable[A]]
Những định nghĩa này cung cấp cho các nhà máy tham số hóa CanBuildFrom
.
Scala sẽ chọn ẩn cụ thể nhất có sẵn. Trong trường hợp đầu tiên, đó là lần đầu tiên CanBuildFrom
. Trong trường hợp thứ hai, vì lần đầu tiên không khớp, nó đã chọn lần thứ hai CanBuildFrom
.
Quay lại câu hỏi
Chúng ta hãy xem các mã cho các câu hỏi, List
's và map
' s định nghĩa (một lần nữa) để xem cách các loại được suy ra:
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
Loại List("London", "Paris")
là List[String]
, vì vậy các loại A
và Repr
được xác định TraversableLike
là:
A = String
Repr = List[String]
Loại cho (x => (x.length, x))
là (String) => (Int, String)
, vì vậy loại B
là:
B = (Int, String)
Loại không xác định cuối cùng, That
là loại kết quả map
và chúng ta cũng đã có loại đó:
val map : Map[Int,String] =
Vì thế,
That = Map[Int, String]
Điều đó có nghĩa là breakOut
, nhất thiết phải trả về một loại hoặc kiểu con của CanBuildFrom[List[String], (Int, String), Map[Int, String]]
.
List
, mà làmap
.