我有一个Seq,x,和一个Stream,y,并且我希望将x预置到y以获得一个新的Stream。然而,静态类型的y导致流立即被评估,我很困惑为什么会这样。下面是一个示例:
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实例调用了++:方法,因此我希望得到一个流,但结果却得到了一个列表。有人能解释一下为什么会发生这种事吗?
发布于 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中):
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]。排成一行:
val b = bf(repr) // b is Builder[B, That]这里CanBuildFrom.apply调用,它返回Builder[B, That]
trait CanBuildFrom[-From, -Elem, +To] {
def apply(from: From): Builder[Elem, To]
}当我们在两个Seq[Int]上调用Seq[Int]时,对于序列(来自scala.collection.Seq)有默认的CanBuildFrom和newBuilder:
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
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],如下所示:
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, ?)或者,您可以从这两个序列生成流:
val x: Seq[Int] = Seq(1, 2, 3)
val y: Seq[Int] = Stream(4, 5, 6)
val z = x.toStream ++: y.toStream编译器将从CanBuildFrom对象中找到隐式Stream,这是惰性的:
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Stream[A]] = new StreamCanBuildFrom[A]https://stackoverflow.com/questions/68569510
复制相似问题