28

是否可以在不评估每个值的情况下使用 yield 作为迭代器?

当很容易实现复杂的列表生成时,这是一个常见的任务,然后你需要将它转换为Iterator,因为你不需要一些结果......

4

3 回答 3

51

当然。实际上,非严格性有三个选项,我在下面列出。对于示例,假设:

val list = List.range(1, 10)
def compute(n: Int) = {
    println("Computing "+n)
    n * 2
}
  1. Stream. AStream是一个惰性评估列表。它将按需计算值,但一旦计算出值,它就不会重新计算值。如果您将多次重复使用流的某些部分,则它是最有用的。例如,运行下面的代码将打印“计算 1”、“计算 2”和“计算 3”,各打印一次。

    val stream = for (n <- list.toStream) yield compute(n)
    val third = stream(2)
    println("%d %d" format (third, stream(2)))
    
  2. 一个视图。视图是对基本集合的操作的组合。检查视图时,检查的每个元素都是按需计算的。如果您将随机访问视图,但只会查看其中的一小部分,这将是最有用的。例如,运行下面的代码将打印两次“Computing 3”,没有其他内容(嗯,除了结果)。

    val view = for (n <- list.view) yield compute(n)
    val third = view(2)
    println("%d %d" format (third, view(2)))
    
  3. Iterator. AnIterator是用来懒洋洋地浏览一个集合的东西。可以说,可以将其视为“一次性”集合。它既不会重新计算也不会存储任何元素——一旦一个元素被“计算”过,就不能再次使用它。因此使用起来有点棘手,但考虑到这些限制,它是最有效的。比如下面的例子就需要有所不同,因为Iterator不支持索引访问(这样写的话视图性能会很差),下面的代码会打印出“Computing 1”、“Computing 2”、“Computing 3”、“计算 4”、“计算 5”和“计算 6”。此外,它最后会打印两个不同的数字。

    val iterator = for (n <- list.iterator) yield compute(n)
    val third = iterator.drop(2).next
    println("%d %d" format (third, iterator.drop(2).next))
    
于 2010-12-24T12:21:03.530 回答
3

如果您想要惰性评估,请使用视图,请参阅Views

如果您打算大量使用 Scala 集合,那么Scala 2.8 Collections API是一本很棒的读物。

于 2010-12-24T09:46:17.357 回答
2

我有一List...

scala>  List(1, 2, 3)
res0: List[Int] = List(1, 2, 3)

还有一个功能...

scala> def foo(i : Int) : String = { println("Eval: " + i); i.toString + "Foo" }
foo: (i: Int)String

现在我将使用一个用于理解Iterator...

scala> for { i <- res0.iterator } yield foo(i)
res2: Iterator[java.lang.String] = non-empty iterator

您可以使用flatMap,mapfilter方法对任何类型使用 for 理解。您还可以使用视图

scala> for { i <- res0.view } yield foo(i)
res3: scala.collection.SeqView[String,Seq[_]] = SeqViewM(...)

在任何一种情况下,评估都是非严格的......

scala> res3.head
Eval: 1
res4: String = 1Foo
于 2010-12-24T12:05:59.850 回答