scala“yield”不会产生正确的类型

时间:2013-12-28 18:40:35

标签: scala

yield主要用于for-yield循环以生成新的同型集合。例如:

scala> val a = Array(2,3,5)
a: Array[Int] = Array(2, 3, 5)

scala> val result = for (elem <- a) yield 2 * elem
result: Array[Int] = Array(4, 6, 10)

这一切都很好,for循环接受一个数组并返回一个数组。

但后来我注意到了这一点:

scala> 1 to 10
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

这会生成范围类型集合,但是当您将此与for-yield循环结合使用时,会发生这种情况:

scala> for (i <- (1 to 10)) yield i + 2
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(3, 4, 5, 6, 7, 8, 9, 10, 11, 12)

进入的类型是范围,但它发出的类型是Vector。为什么会这样?我错过了什么吗?

3 个答案:

答案 0 :(得分:4)

不,你没有遗漏任何东西。请查看Rangemap的签名。

 def map[B](f: (A) ⇒ B): IndexedSeq[B]

这就是为什么它会产生你看到的价值。 Range本身“是”IndexedSeq

为什么在讨论for-comprehension时我会谈论map?理解是编译器转换的语法糖,它使用了mapflatMapfilter(尤其是)。所以即使你只是屈服于你所投入的东西,你也是使用map调用identity

另请注意,Vector部分为何会发生这种情况......

IndexedSeq是一个特质。如果您要查看此特征here的源代码,则随播对象会从Vector方法生成newBuilder[A]

object IndexedSeq extends SeqFactory[IndexedSeq] {
  override lazy val ReusableCBF  = 
      scala.collection.IndexedSeq.ReusableCBF.asInstanceOf[GenericCanBuildFrom[Nothing]]
  class Impl[A](buf: ArrayBuffer[A]) extends AbstractSeq[A] with IndexedSeq[A] with Serializable {
    def length = buf.length
    def apply(idx: Int) = buf.apply(idx)
  }
  def newBuilder[A]: Builder[A, IndexedSeq[A]] = Vector.newBuilder[A]
  implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, IndexedSeq[A]] =
    ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
}

答案 1 :(得分:4)

查看Range

class Range extends AbstractSeq[Int] with IndexedSeq[Int] with CustomParallelizable[Int, ParRange] with Serializable

然后是map的签名:

 def map[B](f: (A) ⇒ B): IndexedSeq[B]

这样做的原因是Range实际上是加糖的IndexedSeq,其中最重要的是范围特定的行为:

Range.InclusiveRange.Exclusive等。

map返回IndexedSeq的原因可能是编译器限制,因为它无法预测Range操作产生的map的类型。

答案 2 :(得分:2)

范围必须在其值之间有固定的步长。由于不可能推断出任何yield返回将是一个Range,所以进行集合以便map被定义为返回一个IndexedSeq,即表现得像它覆盖的IndexedSeq。