Is it possible to use 'yield' to generate 'Iterator' instead of a list in Scala?

倾然丶 夕夏残阳落幕 提交于 2019-12-03 03:08:05

问题


Is it possible to use yield as an iterator without evaluation of every value?

It is a common task when it is easy to implement complex list generation, and then you need to convert it into Iterator, because you don't need some results...


回答1:


Sure. Actually, there are three options for non-strictness, which I list below. For the examples, assume:

val list = List.range(1, 10)
def compute(n: Int) = {
    println("Computing "+n)
    n * 2
}
  1. Stream. A Stream is a lazily evaluated list. It will compute values on demand, but it will not recompute values once they have been computed. It is most useful if you'll reuse parts of the stream many times. For example, running the code below will print "Computing 1", "Computing 2" and "Computing 3", one time each.

    val stream = for (n <- list.toStream) yield compute(n)
    val third = stream(2)
    println("%d %d" format (third, stream(2)))
    
  2. A view. A view is a composition of operations over a base collection. When examining a view, each element examined is computed on-demand. It is most useful if you'll randomly access the view, but will never look but at a small part of it. For example, running the code below will print "Computing 3" two times, and nothing else (well, besides the result).

    val view = for (n <- list.view) yield compute(n)
    val third = view(2)
    println("%d %d" format (third, view(2)))
    
  3. Iterator. An Iterator is something that is used to lazily walk through a collection. One can think of it as a "one-shot" collection, so to speak. It will neither recompute nor store any elements -- once an element has been "computed", it cannot be used again. It is a bit more tricky to use because of that, but it is the most efficient one given these constraints. For example, the following example needs to be different, because Iterator does not support indexed access (and view would perform badly if written this way), and the code below prints "Computing 1", "Computing 2", "Computing 3", "Computing 4", "Computing 5" and "Computing 6". Also, it prints two different numbers at the end.

    val iterator = for (n <- list.iterator) yield compute(n)
    val third = iterator.drop(2).next
    println("%d %d" format (third, iterator.drop(2).next))
    



回答2:


Use views if you want lazy evaluation, see Views.

The Scala 2.8 Collections API is a fantastic read if you're going to use the Scala collections a lot.




回答3:


I have a List...

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

And a function...

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

And now I'll use a for-comprehension with an Iterator...

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

You can use a for comprehension on any type with flatMap, map and filter methods. You could also use the views:

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

Evaluation is non-strict in either case...

scala> res3.head
Eval: 1
res4: String = 1Foo


来源:https://stackoverflow.com/questions/4525435/is-it-possible-to-use-yield-to-generate-iterator-instead-of-a-list-in-scala

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!