在Scala 2.8 中,scala.collection.package.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()
}
我被告知这导致:
> import scala.collection.breakOut
> val map : Map[Int,String] = List("London", "Paris").map(x => (x.length, x))(breakOut)
map: Map[Int,String] = Map(6 -> London, 5 -> Paris)
这里发生了什么?为什么breakOut
被称为作为参数到我的List
?
答案 0 :(得分:321)
答案 1 :(得分:86)
我想以丹尼尔的答案为基础。这是非常彻底的,但正如评论中所指出的,它并没有解释什么是突破。
取自 Re: Support for explicit Builders (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.collection.mutable.Seq[Int] = ArrayBuffer(2, 3, 4)
scala> val set: Set[Int] = l.map(_ + 1)(breakOut)
seq: scala.collection.mutable.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)
答案 2 :(得分:6)
Daniel Sobral的答案很棒,应该与Architecture of Scala Collections一起阅读(Scala编程第25章)。
我只是想详细说明为什么称它为breakOut
:
breakOut
?因为我们希望 突破一种类型而进入另一种类型 :
突破什么类型到什么类型?让我们以map
上的Seq
函数为例:
Seq.map[B, That](f: (A) -> B)(implicit bf: CanBuildFrom[Seq[A], B, That]): That
如果我们想直接通过映射序列元素来构建Map,例如:
val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))
编译器会抱怨:
error: type mismatch;
found : Seq[(String, Int)]
required: Map[String,Int]
原因是Seq只知道如何构建另一个Seq(即有一个隐式CanBuildFrom[Seq[_], B, Seq[B]]
构建器工厂可用,但是从Seq到Map有 NO 构建器工厂)。
为了进行编译,我们需要以某种方式 breakOut
类型要求 ,并能够构建为{生成Map的构建器{1}}要使用的功能。
正如Daniel所解释的那样,breakOut具有以下特征:
map
def breakOut[From, T, To](implicit b: CanBuildFrom[Nothing, T, To]): CanBuildFrom[From, T, To] =
// can't just return b because the argument to apply could be cast to From in b
new CanBuildFrom[From, T, To] {
def apply(from: From) = b.apply()
def apply() = b.apply()
}
是所有类的子类,因此可以替换任何构建器工厂来代替Nothing
。如果我们使用breakOut函数来提供隐式参数:
implicit b: CanBuildFrom[Nothing, T, To]
它会编译,因为val x: Map[String, Int] = Seq("A", "BB", "CCC").map(s => (s, s.length))(collection.breakOut)
能够提供所需类型的breakOut
,而编译器能够找到CanBuildFrom[Seq[(String, Int)], (String, Int), Map[String, Int]]
类型的隐式构建器工厂,而不是{ {1}},用于breakOut用于创建实际构建器。
请注意,CanBuildFrom[Map[_, _], (A, B), Map[A, B]]
在Map中定义,只是启动使用基础Map的CanBuildFrom[Nothing, T, To]
。
希望这可以解决问题。
答案 3 :(得分:3)
了解breakOut
做什么的一个简单示例:
scala> import collection.breakOut
import collection.breakOut
scala> val set = Set(1, 2, 3, 4)
set: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
scala> set.map(_ % 2)
res0: scala.collection.immutable.Set[Int] = Set(1, 0)
scala> val seq:Seq[Int] = set.map(_ % 2)(breakOut)
seq: Seq[Int] = Vector(1, 0, 1, 0) // map created a Seq[Int] instead of the default Set[Int]