首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Stream预置返回列表而不是Stream

Stream预置返回列表而不是Stream
EN

Stack Overflow用户
提问于 2021-07-29 02:59:58
回答 1查看 118关注 0票数 2

我有一个Seq,x,和一个Stream,y,并且我希望将x预置到y以获得一个新的Stream。然而,静态类型的y导致流立即被评估,我很困惑为什么会这样。下面是一个示例:

代码语言:javascript
运行
复制
val x: Seq[Int] = Seq(1, 2, 3)
val y: Seq[Int] = Stream(4, 5, 6)
val z = x ++: y // z has dynamic type List instead of Stream

由于对Stream实例调用了++:方法,因此我希望得到一个流,但结果却得到了一个列表。有人能解释一下为什么会发生这种事吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-29 06:28:37

tl;dr

这是因为编译器的类型推断,当您在两个Seq上使用Seq时,它只是构造另一个Seq++:创建返回类型param是Seq的构建器,但是默认的Seq构建器是mutable.ListBuffer,它的返回类型是List[A],也是Seq。因此,默认情况下,它在构建器中阻止惰性,返回值为List[Int],但返回类型为Seq[Int]

问题调查

让我们观察一下++:签名(例如在Scala2.12.10中):

代码语言:javascript
运行
复制
def ++:[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
    val b = bf(repr)
    if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.size)
    b ++= that
    b ++= thisCollection
    b.result
  }

这里我们看到隐式论点:bf: CanBuildFrom[Repr, B, That]。排成一行:

代码语言:javascript
运行
复制
val b = bf(repr) // b is Builder[B, That]

这里CanBuildFrom.apply调用,它返回Builder[B, That]

代码语言:javascript
运行
复制
trait CanBuildFrom[-From, -Elem, +To] {
  def apply(from: From): Builder[Elem, To]
}

当我们在两个Seq[Int]上调用Seq[Int]时,对于序列(来自scala.collection.Seq)有默认的CanBuildFromnewBuilder

代码语言:javascript
运行
复制
object Seq extends SeqFactory[Seq] {
  /** $genericCanBuildFromInfo */
  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Seq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]

  def newBuilder[A]: Builder[A, Seq[A]] = immutable.Seq.newBuilder[A]
}

我们看到了,newBuilder从scala.collection.immutable.Seq调用immutable.Seq.newBuilder

代码语言:javascript
运行
复制
object Seq extends SeqFactory[Seq] {
  /** genericCanBuildFromInfo */
  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Seq[A]] = ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
  def newBuilder[A]: Builder[A, Seq[A]] = new mutable.ListBuffer
}

我们看到mutable.ListBuffer并不懒惰。

决定

因此,为了在连接过程中保持懒惰,您应该将自己的CanBuildFrom传递给Stream[Int],如下所示:

代码语言:javascript
运行
复制
import scala.collection.generic.CanBuildFrom
import scala.collection.mutable
import scala.collection.mutable.Builder

val x: Seq[Int] = Seq(1, 2, 3)
val y: Seq[Int] = Stream(4, 5, 6)
implicit val cbf = new CanBuildFrom[Seq[Int], Int, Stream[Int]] {
  override def apply(from: Seq[Int]): Builder[Int, Stream[Int]] =
    new mutable.LazyBuilder[Int, Stream[Int]] {
      override def result() = from.toStream
    }

  override def apply(): mutable.Builder[Int, Stream[Int]] = Stream.newBuilder[Int]
}
val z = x ++:(y) // not it will be Stream(1, ?)

或者,您可以从这两个序列生成流:

代码语言:javascript
运行
复制
val x: Seq[Int] = Seq(1, 2, 3)
val y: Seq[Int] = Stream(4, 5, 6)
val z = x.toStream ++: y.toStream

编译器将从CanBuildFrom对象中找到隐式Stream,这是惰性的:

代码语言:javascript
运行
复制
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Stream[A]] = new StreamCanBuildFrom[A]
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68569510

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档