答案在以下定义中找到map:
def map[B, That](f : (A) => B)(implicit bf : CanBuildFrom[Repr, B, That]) : That
请注意,它有两个参数。第一个是您的函数,第二个是隐式的。如果您不提供该隐式选项,Scala将选择最具体的选项。
关于 breakOut
那么,目的是breakOut什么?考虑针对该问题给出的示例:您获取一个字符串列表,将每个字符串转换为一个元组(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]。我们已经知道所有这些类型,因此可以确定我们需要type的隐式类型CanBuildFrom[Nothing,(Int,String),Map[Int,String]]。但是有这样的定义吗?
让我们看一下CanBuildFrom的定义:
trait CanBuildFrom[-From, -Elem, +To]
extends AnyRef
因此CanBuildFrom其第一类型参数是反变量。因为Nothing是底层类(即它是所有内容的子类),所以可以使用任何类代替Nothing。
由于存在这样的构建器,因此Scala可以使用它来生成所需的输出。
关于建筑商
Scala集合库中的许多方法包括获取原始集合,以某种方式对其进行处理(对于map,转换每个元素)以及将结果存储在新集合中。
为了最大程度地重复使用代码,结果的存储是通过构建器(scala.collection.mutable.Builder)完成的,该构建器基本上支持两种操作:附加元素和返回结果集合。此结果集合的类型将取决于构建器的类型。因此,List构建器将返回List,Map构建器将返回Map,依此类推。该map方法的实现不需要关心结果的类型:构建器会处理它。
另一方面,这意味着map需要以某种方式接收此构建器。设计Scala 2.8 Collections时面临的问题是如何选择最佳的构建器。例如,如果我要写Map('a' -> 1).map(_.swap),我想Map(1 -> 'a')找回。另一方面,a Map('a' -> 1).map(_._1)不能返回a Map(它返回Iterable)。
Builder通过这种CanBuildFrom隐式执行从表达式的已知类型中产生最佳结果的魔力。
关于 CanBuildFrom
为了更好地说明发生了什么,我将举一个示例,其中要映射的集合是a Map而不是List。我待会再回头List。现在,请考虑以下两个表达式:
Map(1 -> "one", 2 -> "two") map Function.tupled(_ -> _.length)
Map(1 -> "one", 2 -> "two") map (_._2)
第一个返回a Map,第二个返回a 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。
Scala将选择最具体的隐式可用。在第一种情况下,这是第一种CanBuildFrom。在第二种情况下,由于第一种不匹配,因此选择了第二种CanBuildFrom。
回到问题
让我们再看一遍问题的代码Lists和map的定义,以了解如何推断类型:
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],因此on 的类型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。