tl;博士
这是因为编译器类型推断,当你++:
在两个上使用它时,Seq
它只是构造另一个Seq
. ++:
创建返回类型 param 的构建器Seq
,但默认Seq
构建器是mutable.ListBuffer
,它的返回类型List[A]
也是Seq
。因此,默认情况下,它会在builder内部制动惰性,返回值将是但返回类型将是.List[Int]
Seq[Int]
问题调查
让我们看一下++:
签名(例如在 scala 2.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]
我们有 defaultCanBuildFrom
和newBuilder
for 序列(来自scala.collection.Seq
):
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]
}
我们看到,newBuilderimmutable.Seq.newBuilder
从以下位置调用scala.collection.immutable.Seq
:
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
for 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]