gpt4 book ai didi

Scala 2.8 突破

转载 作者:行者123 更新时间:2023-12-03 04:08:12 27 4
gpt4 key购买 nike

在斯卡拉 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 的参数?

最佳答案

答案可在 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 的实例.碰巧,类型 From , TTo已经被推断出来了,因为我们知道 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 可以使用它来生成所需的输出。

关于 build 者

Scala 集合库中的许多方法包括获取原始集合,以某种方式处理它(在 map 的情况下,转换每个元素),并将结果存储在新集合中。

为了最大限度地重用代码,这种结果的存储是通过构建器 ( scala.collection.mutable.Builder ) 完成的,它基本上支持两种操作:附加元素和返回结果集合。此结果集合的类型将取决于构建器的类型。因此,一个 List builder 将返回 List , Map builder 将返回 Map , 等等。 map的执行方法不需要关心结果的类型:构建器会处理它。

另一方面,这意味着 map需要以某种方式接收这个构建器。设计 Scala 2.8 Collections 时面临的问题是如何选择最好的构建器。例如,如果我要写 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 ,并使用类型参数 ARepr , 参数化类。让我们一起看看这两个定义:

类(class) 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

了解哪里 ARepr来自,让我们考虑 Map 的定义本身:
trait Map[A, +B] 
extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]

因为 TraversableLike由扩展 Map 的所有特征继承, ARepr可以从他们中的任何一个继承。不过,最后一个获得了偏好。因此,遵循不可变 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]

回到这个例子,第一张 map 正在接收一个类型为 ((Int, String)) => (Int, Int) 的函数。第二张 map 正在接收类型为 ((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 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] ,所以类型 ARepr定义于 TraversableLike是:
A = String
Repr = List[String]
(x => (x.length, x))的类型是 (String) => (Int, String) ,所以 B 的类型是:
B = (Int, String)

最后一个未知类型, Thatmap 的结果类型,我们也已经有了:
val map : Map[Int,String] =

所以,
That = Map[Int, String]

这意味着 breakOut必须,必须,返回 CanBuildFrom[List[String], (Int, String), Map[Int, String]] 的类型或子类型.

关于Scala 2.8 突破,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1715681/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com