为了账号安全,请及时绑定邮箱和手机立即绑定

Scala 2.8突破

Scala 2.8突破

米脂 2019-06-19 16:59:29
Scala 2.8突破斯卡拉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?
查看完整描述

3 回答

?
撒科打诨

TA贡献1934条经验 获得超2个赞

答案是在…的定义上找到的。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?显然,是的。然而,要做到这一点,我们需要通过一个适当的CanBuildFrommap,这正是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..碰巧,类型FromTTo已经被推断了,因为我们知道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..它被参数化为BThat,并使用类型参数。ARepr,它将类参数化。让我们一起看看这两个定义:

全班TraversableLike定义为:

trait TraversableLike[+A, +Repr] extends HasNewBuilder[A, Repr] with AnyRefdef map[B, That](f : (A) => B)(implicit bf : CanBuildFrom
[Repr, B, That]) : That

才能了解ARepr来自,让我们考虑一下Map本身:

trait Map[A, +B] extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]

因为TraversableLike是由所有的性状继承的MapARepr可以从他们中的任何一个继承下来。不过,最后一个得到了优先考虑。因此,按照不可变的定义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

我们可以看到第一个返回的类型mapMap[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.

回到问题上

让我们看看这个问题的代码,Listmap的定义(再次)以查看如何推断类型:

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],所以类型ARepr定义在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]].


查看完整回答
反对 回复 2019-06-19
?
Smart猫小萌

TA贡献1911条经验 获得超7个赞

我想在丹尼尔回答的基础上再接再厉。这是非常彻底的,但正如评论中所指出的,它并没有解释什么是突破。

摘自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)


查看完整回答
反对 回复 2019-06-19
  • 3 回答
  • 0 关注
  • 502 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信