Scala 2.8 breakOut

时间:2009-11-11 14:53:23

标签: scala scala-2.8 scala-collections

在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

4 个答案:

答案 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]