撒科打诨
答案是在…的定义上找到的。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使用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是一个底层(即它是所有事物的子类),这意味着任何类可用于替换Nothing.由于存在这样的构建器,Scala可以使用它来生成所需的输出。关于建筑商Scala集合库中的许多方法包括获取原始集合,以某种方式处理它(在map,转换每个元素),并将结果存储在一个新集合中。为了最大限度地重用代码,这种结果的存储是通过建筑商(scala.collection.mutable.Builder),它基本上支持两个操作:追加元素和返回结果集合。此结果集合的类型将取决于构建器的类型。因此,List建设者将返回List..Map建设者将返回Map等等。“公约”的执行情况map方法本身不需要考虑结果的类型:构建器负责处理它。另一方面,这意味着map需要以某种方式接待这个建筑工人。在设计Scala2.8集合时面临的问题是如何选择最佳的构建器。例如,如果我要写Map('a' -> 1).map(_.swap),我想要一个Map(1 -> 'a')背。另一方面,Map('a' -> 1).map(_._1)不能返回Map(它返回一个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 AnyRefdef 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] w
ith IterableLike[(A, B), This] with Subtractable[A, This]trait IterableLike[+A, +Repr] extends Equals with TraversableLike[A, Repr]trait T
raversableLike[+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.回到问题上让我们看看这个问题的代码,List和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 TraversableL
ike[A, Repr]trait TraversableLike[+A, +Repr] extends HasNewBuilder[A, Repr] with AnyRefdef map[B, That](f : (A) => B)(implicit bf :
CanBuildFrom[Repr, B, That]) : That类型List("London", "Paris")是List[String],所以类型A和Repr定义在TraversableLike是:A = StringRepr = 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]].
Smart猫小萌
我想在丹尼尔回答的基础上再接再厉。这是非常彻底的,但正如评论中所指出的,它并没有解释什么是突破。摘自Re:支持显式构建器(2009-10-23年),以下是我认为的突破:它向编译器建议隐式选择哪个Builder(本质上它允许编译器选择它认为最适合这种情况的工厂)。例如,请参见以下内容:scala> import scala.collection.generic._import scala.collection.generic._
scala> import scala.collection._import scala.collection._
scala> import scala.collection.mutable._import scala.collection.mutable._
scala>scala> 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: [From, T, To]
| (implicit b: scala.collection.generic.CanBuildFrom[Nothing,T,To])
| java.lang.Object with
| scala.collection.generic.CanBuildFrom[From,T,To]scala> val l = List(1, 2, 3)l: List[Int] = List(1, 2, 3)scala>
val imp = l.map(_ + 1)(breakOut)imp: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4)scala>
val arr: Array[Int] = l.map(_ + 1)(breakOut)imp: Array[Int] = Array(2, 3, 4)scala> val stream: Stream[Int] = l.
map(_ + 1)(breakOut)stream: Stream[Int] = Stream(2, ?)scala> val seq: Seq[Int] = l.map(_ + 1)(breakOut)seq: scala.col
lection.mutable.Seq[Int] = ArrayBuffer(2, 3, 4)scala> val set: Set[Int] = l.map(_ + 1)(breakOut)seq: scala.collection.mutabl
e.Set[Int] = Set(2, 4, 3)scala> val hashSet: HashSet[Int] = l.map(_ + 1)(breakOut)seq: scala.collection.mutable.HashSet[Int]
= Set(2, 4, 3)您可以看到返回类型是由编译器隐式选择的,以最佳匹配预期类型。根据声明接收变量的方式,可以得到不同的结果。下面是指定构建器的等效方法。注在这种情况下,编译器将根据构建器的类型推断预期的类型:scala> def buildWith[From, T, To](b : Builder[T, To]) =
| new CanBuildFrom[From, T, To] {
| def apply(from: From) = b ; def apply() = b | }buildWith: [From, T, To]
| (b: scala.collection.mutable.Builder[T,To])
| java.lang.Object with
| scala.collection.generic.CanBuildFrom[From,T,To]scala> val a = l.map(_ + 1)(buildWith(Array.newBuilder[Int]))a:
Array[Int] = Array(2, 3, 4)