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。为什么会这样?我错过了什么吗?
答案 0 :(得分:4)
不,你没有遗漏任何东西。请查看Range中map
的签名。
def map[B](f: (A) ⇒ B): IndexedSeq[B]
这就是为什么它会产生你看到的价值。 Range
本身“是”IndexedSeq
。
为什么在讨论for-comprehension时我会谈论map
?理解是编译器转换的语法糖,它使用了map
,flatMap
和filter
(尤其是)。所以即使你只是屈服于你所投入的东西,你也是使用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.Inclusive
,Range.Exclusive
等。
map
返回IndexedSeq
的原因可能是编译器限制,因为它无法预测Range
操作产生的map
的类型。
答案 2 :(得分:2)
范围必须在其值之间有固定的步长。由于不可能推断出任何yield返回将是一个Range,所以进行集合以便map
被定义为返回一个IndexedSeq,即表现得像它覆盖的IndexedSeq。