Scala可遍历、可重用、序列、流和视图的语义?

Scala可遍历、可重用、序列、流和视图的语义?,scala,scala-collections,Scala,Scala Collections,还有其他一些问题,如和,部分回答了这个问题。我觉得一个在一个地方涵盖所有这些的问题是有意义的 可遍历的是集合层次结构的顶部。它的主要方法是“foreach”,因此它允许为集合的每个元素做一些事情 一个Iterable可以创建一个迭代器,在此基础上可以实现foreach。这定义了元素的一些顺序,尽管每个迭代器的顺序可能会改变 Seq(uence)是元素顺序固定的可数。因此,讨论元素的索引是有意义的 流是惰性序列。即,流的元素在被访问之前不能被计算。这使得可以处理无限序列,就像所有整数的序列一样 视

还有其他一些问题,如和,部分回答了这个问题。我觉得一个在一个地方涵盖所有这些的问题是有意义的

可遍历的是集合层次结构的顶部。它的主要方法是“foreach”,因此它允许为集合的每个元素做一些事情

一个Iterable可以创建一个迭代器,在此基础上可以实现foreach。这定义了元素的一些顺序,尽管每个迭代器的顺序可能会改变

Seq(uence)是元素顺序固定的可数。因此,讨论元素的索引是有意义的

是惰性序列。即,流的元素在被访问之前不能被计算。这使得可以处理无限序列,就像所有整数的序列一样


视图是集合的非严格版本。filter和map-on-view等方法仅在访问相应元素时执行传递的函数。因此,大型集合上的映射会立即返回,因为它只是在原始集合周围创建一个包装器。只有当一个人访问一个元素时,映射才会被实际执行(对于该元素)。请注意,View不是一个类,但有许多XxxView类用于各种集合。

我想添加一条关于streams vs.Iterator的注释。流和迭代器都可以用于实现长的、非严格的、可能无限的集合,这些集合在需要时才计算值

然而,在执行此操作时会出现一个棘手的“过早执行”问题,可以使用迭代器而不是流来避免这一问题,并且在此过程中指出了两者之间的一个重要语义差异。这一点也许可以用以下方式最清楚地说明:

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
  }
}
请仔细注意,计算第一个值所需的执行是如何在实际使用流值的位置之前发生的。例如,如果此初始执行涉及打开文件或internet连接,并且在创建流之后和使用任何值之前有很长的延迟,这可能是非常有问题的--最终会出现一个打开的文件描述符,更糟糕的是,您的internet连接可能会超时,导致整个事情失败

使用初始空流修复它的简单尝试无效:

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
  }
}
但是,您可以通过将流更改为具有初始空迭代器的迭代器来解决此问题,尽管情况远非如此:

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
  }
}

请注意,如果不添加初始的空迭代器,您将遇到与streams相同的过早执行问题。

我投票决定结束1),因为这不是一个具体的问题,而是一个非常笼统的问题,2)因为随着Scala的发展,信息可能会变得陈旧;3)因为一般案例已经由定义良好,这可能是进行此类广泛调查的更好资源(而Scala与许多其他语言不同,有相当广泛的文档,尽管有时相当具挑战性)。@ig0774 2)在我看来,scala collections的核心概念在可见的未来不会发生重大变化。@ig0774与您的三点不同,我投了更高的票,而不是结束投票,因为1)我觉得这个问题总体上是有价值的,并且足够明确,可以简洁地回答,2)因为它位于理解当前Scala collections库的核心附近,3)当前的文档虽然广泛,但非常分散;很难了解全局。@ig0774谢谢链接。我不知道这个文档。读得好。但是,我认为对于一个在so中寻找它的初学者来说,这个问题是有意义的。@om nom(上周)对此进行了理论讨论:视图不是懒惰的,只是不严格。懒惰需要缓存,而流需要缓存,而视图不需要缓存。@DanielC.Sobral“懒惰需要缓存”-嗯,什么?@DanBurton-这样,如果您下次请求相同的值,它就不会被重新计算。否则,它就不是那么“懒惰”:)也许“记忆化”会是一个更好的描述,因为这会阻止值被删除,这不是缓存的情况。@DanielC.Sobral,文档说“类流实现了懒惰列表……流类也采用了记忆化”。懒惰可以有记忆,但不需要。Scala的
lazy val
融合了这些概念,但它可能比
lazy memorized val
更好。您可以在流版本中将“val iter”更改为“def iter”。这样就行了。
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)