【问题标题】:Understanding flatMap declaration in List了解 List 中的 flatMap 声明
【发布时间】:2017-01-05 08:56:53
【问题描述】:

我刚刚看了List.flatMap 声明,对此感到有些惊讶。

final override def flatMap[B, That](f: A => GenTraversableOnce[B])
                 (implicit bf: CanBuildFrom[List[A], B, That]): That

object List 定义的地方:

implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, List[A]] =
    ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]

所以,如果我们在List 上调用flatMap,我们将得到List,如果它总是被推导出为List[B],我看不到That 类型的任何意义(因为implicit)。

【问题讨论】:

  • implicit 可以被更本地化的覆盖,例如,Vector,或者作为参数显式传递
  • 查看stackoverflow.com/a/1728140/5344058 以了解何时将That 设置为与原始集合不同的类型很有用
  • 你有没有见过 collection.breakout 带有 flatMap 或 Map...这是覆盖这个隐式的一个例子。

标签: scala collections implicit


【解决方案1】:

所以,如果我们在 List[A] 上调用 flatMap,我们将得到 List[A] 而我没有 查看That 类型中的任何点,如果它总是被推断为List[B]

您缺少的一件事是 flatMap 实际上并未在 List[+A] 上定义。它继承自 TraversableLike,这是大多数 Scalas 集合使用的特征。它们中的每一个都可以提供隐含的CanBuildFrom,它可以被覆盖以提供不同的结果集合。

如果您想了解一下自定义 CanBuildFrom 可以做什么:

scala> :pa
// Entering paste mode (ctrl-D to finish)

import scala.collection.generic.CanBuildFrom
import scala.collection.immutable._
import scala.collection.mutable
import scala.{List, Vector}

implicit val listToVectorCBF = new CanBuildFrom[List[Int], Int, Vector[Int]] {
  override def apply(from: List[Int]): mutable.Builder[Int, Vector[Int]] = this.apply()
  override def apply(): mutable.Builder[Int, Vector[Int]] = Vector.newBuilder
}

// Exiting paste mode, now interpreting.

scala> List(1,2,3).flatMap(List(_))
res6: Vector[Int] = Vector(1, 2, 3)

【讨论】:

  • 但是我可以提供我自己的CanBuildFrom 以获得甚至不是 Seq 的东西吗?
  • @user3663882 没有什么能阻止您提供自己的。它使派生类更容易简单地提供自己的隐式 CBF,而不是重新实现方法。
  • @user3663882 我添加了一个小例子来演示。
【解决方案2】:

嗯... implicit CanBuildFrom 可用于直接构建不同类型的结构,而不是 List,从而节省了一个额外的步骤。让我们看看下面的例子,

val list = List(List(1, 2, 3), List(4, 5, 6))

// now if we do a flatmap withtout overriding the implicit CanBuildFrom
val newList = list.flatMap(l => l.map(i => (i,i)))

// new list will be a List[(Int, Int)]
// but what if you wanted a map

val newMap = newList.toMap

// But you actually needed to traverse the list twice in this case

// But we can avoid the second traversal if we chose to override the implicit
val newMap2 = list.flatMap(l => l.map(i => (i,i)))(collection.breakout)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-22
    • 2012-03-05
    • 2014-05-22
    • 1970-01-01
    • 1970-01-01
    • 2015-06-20
    • 2018-07-24
    相关资源
    最近更新 更多