有一些类似的问题,比如Scala中集合框架中Traversable和Iterable特质的区别是什么? 和 如何在Scala中获取两个列表平方和的总和? 这些问题可以部分回答这个问题。我认为在一个地方涵盖所有这些问题是有意义的。
Traversable是集合层次结构的最顶端。它的主要方法是'foreach',因此允许对集合中的每个元素执行某些操作。
Iterable可以创建一个迭代器,基于该迭代器可以实现 foreach。这定义了元素的某种顺序,尽管对于每个迭代器,该顺序可能会发生变化。
Seq(uence)是具有固定元素顺序的 Iterable。因此,谈论元素的索引是有意义的。
Streams是惰性的 Sequence。即在访问之前可能不会计算流的元素。这使得可以处理无限序列,例如所有整数的序列。
Views是集合的非严格版本。在视图上执行筛选和映射等方法时,仅当相应元素被访问时才执行传递的函数。因此,在巨大的集合上进行映射会立即返回,因为它只是在原始集合周围创建一个包装器。只有在访问元素时,映射才会实际执行(对于该元素)。请注意,视图不是一个类,但是有很多针对各种集合的 XxxView 类。
lazy val
混淆了这些概念,但它可能比lazy memoized val
更好。 - Paul Draper我想补充一下有关流和迭代器的评论。两者都可用于实现长、非严格、潜在无限的集合,只有在需要时才计算一个值。
然而,这样做会出现“过早执行”的棘手问题,使用迭代器可以避免此问题,但使用流则不能,同时也突显了两者之间重要的语义差异。这可以用以下方式清晰地说明:
def runiter(start: Int) {
// Create a stream that returns successive integers on demand, e.g. 3, 4, 5, ....
val iter = {
def loop(v: Int): Stream[Int] = { println("I computed a value", v); v} #:: loop(v+1)
loop(start)
}
// Now, sometime later, we retrieve the values ....
println("about to loop")
for (x <- iter) {
if (x < 10) println("saw value", x) else return
}
}
这段代码创建了一个从给定值开始的无限流,返回连续的整数。它用作更复杂的代码的替代品,例如打开互联网连接并根据需要返回连接值。
结果:
scala> runiter(3)
(I computed a value,3)
about to loop
(saw value,3)
(I computed a value,4)
(saw value,4)
(I computed a value,5)
(saw value,5)
(I computed a value,6)
(saw value,6)
(I computed a value,7)
(saw value,7)
(I computed a value,8)
(saw value,8)
(I computed a value,9)
(saw value,9)
(I computed a value,10)
def runiter(start: Int) {
// Create a stream that returns successive integers on demand, e.g. 3, 4, 5, ....
val iter = {
def loop(v: Int): Stream[Int] = { println("I computed a value", v); v} #:: loop(v+1)
Stream[Int]() ++ loop(start)
}
// Now, sometime later, we retrieve the values ....
println("about to loop")
for (x <- iter) {
if (x < 10) println("saw value", x) else return
}
}
结果(与以前相同):
scala> runiter(3)
(I computed a value,3)
about to loop
(saw value,3)
(I computed a value,4)
(saw value,4)
(I computed a value,5)
(saw value,5)
(I computed a value,6)
(saw value,6)
(I computed a value,7)
(saw value,7)
(I computed a value,8)
(saw value,8)
(I computed a value,9)
(saw value,9)
(I computed a value,10)
然而,您可以通过将流更改为具有初始空迭代器的迭代器来解决此问题,尽管这并不明显:
def runiter(start: Int) {
// Create an iterator that returns successive integers on demand, e.g. 3, 4, 5, ....
val iter = {
def loop(v: Int): Iterator[Int] = { println("I computed a value", v); Iterator(v)} ++ loop(v+1)
Iterator[Int]() ++ loop(start)
}
// Now, sometime later, we retrieve the values ....
println("about to loop")
for (x <- iter) {
if (x < 10) println("saw value", x) else return
}
}
结果:
scala> runiter(3)
about to loop
(I computed a value,3)
(saw value,3)
(I computed a value,4)
(saw value,4)
(I computed a value,5)
(saw value,5)
(I computed a value,6)
(saw value,6)
(I computed a value,7)
(saw value,7)
(I computed a value,8)
(saw value,8)
(I computed a value,9)
(saw value,9)
(I computed a value,10)