How to implement lazy sequence (iterable) in scala?

I want to implement a lazy iterator that gives the next element in every call in a three-level nested loop.

Is there something similar in scala for this C # snippet:

foreach (int i in ...) { foreach (int j in ...) { foreach (int k in ...) { yield return do(i,j,k); } } } 

Thanks Dudu

+7
source share
8 answers

If you join iterators with ++ , you get one iterator that runs both. And the reduceLeft method helps to consolidate the entire collection. Thus,

 def doIt(i: Int, j: Int, k: Int) = i+j+k (1 to 2).map(i => { (1 to 2).map(j => { (1 to 2).iterator.map(k => doIt(i,j,k)) }).reduceLeft(_ ++ _) }).reduceLeft(_ ++ _) 

will create the desired iterator. If you want this to be even more lazy, you can add .iterator after the first two (1 to 2) . (Replace each (1 to 2) your own more interesting collection or range, of course.)

+4
source

Scala sequence types have a .view method that gives the lazy equivalent of a collection. You can play with the following in REPL (after the release :silent to stop it from forcing the collection to print command results):

 def log[A](a: A) = { println(a); a } for (i <- 1 to 10) yield log(i) for (i <- (1 to 10) view) yield log(i) 

The first will print numbers from 1 to 10, the second will not, until you actually try to access these elements of the result.

There is nothing in Scala that is equivalent to a C # yield that pauses the execution of a loop. You can achieve similar effects with delimited sequels that were added for Scala 2.8.

+5
source

If your 3 iterators are usually small (i.e. you can completely rename them without taking into account memory or processor), and the expensive part calculates the result with data i, j and k, you can use the Scala Stream class.

 val tuples = for (i <- 1 to 3; j <- 1 to 3; k <- 1 to 3) yield (i, j, k) val stream = Stream(tuples: _*) map { case (i, j, k) => i + j + k } stream take 10 foreach println 

If your iterators are too large for this approach, you can extend this idea and create a stream of tuples that calculates the next value lazily, saving the state for each iterator. For example (although I hope someone has a more convenient way to define a product method):

 def product[A, B, C](a: Iterable[A], b: Iterable[B], c: Iterable[C]): Iterator[(A, B, C)] = { if (a.isEmpty || b.isEmpty || c.isEmpty) Iterator.empty else new Iterator[(A, B, C)] { private val aItr = a.iterator private var bItr = b.iterator private var cItr = c.iterator private var aValue: Option[A] = if (aItr.hasNext) Some(aItr.next) else None private var bValue: Option[B] = if (bItr.hasNext) Some(bItr.next) else None override def hasNext = cItr.hasNext || bItr.hasNext || aItr.hasNext override def next = { if (cItr.hasNext) (aValue get, bValue get, cItr.next) else { cItr = c.iterator if (bItr.hasNext) { bValue = Some(bItr.next) (aValue get, bValue get, cItr.next) } else { aValue = Some(aItr.next) bItr = b.iterator (aValue get, bValue get, cItr.next) } } } } } val stream = product(1 to 3, 1 to 3, 1 to 3).toStream map { case (i, j, k) => i + j + k } stream take 10 foreach println 

This approach fully supports infinitely sized inputs.

+1
source

I think the code below is what you are actually looking for ... I think the compiler is finishing translating it into the equivalent of the map code that Rex gave, but closer to the syntax of your original example:

 scala> def doIt(i:Int, j:Int) = { println(i + ","+j); (i,j); } doIt: (i: Int, j: Int)(Int, Int) scala> def x = for( i <- (1 to 5).iterator; j <- (1 to 5).iterator ) yield doIt(i,j) x: Iterator[(Int, Int)] scala> x.foreach(print) 1,1 (1,1)1,2 (1,2)1,3 (1,3)1,4 (1,4)1,5 (1,5)2,1 (2,1)2,2 (2,2)2,3 (2,3)2,4 (2,4)2,5 (2,5)3,1 (3,1)3,2 (3,2)3,3 (3,3)3,4 (3,4)3,5 (3,5)4,1 (4,1)4,2 (4,2)4,3 (4,3)4,4 (4,4)4,5 (4,5)5,1 (5,1)5,2 (5,2)5,3 (5,3)5,4 (5,4)5,5 (5,5) scala> 

You can see at the output that printing in "doIt" is not called until the next x value is iterated, and this style for the generator is a little easier to read / write than a bunch of nested cards.

+1
source

You can use Sequential Understanding over Iterators to get what you want:

 for { i <- (1 to 10).iterator j <- (1 to 10).iterator k <- (1 to 10).iterator } yield doFunc(i, j, k) 

If you want to create a lazy Iterable (instead of a lazy iterator), instead of Views :

 for { i <- (1 to 10).view j <- (1 to 10).view k <- (1 to 10).view } yield doFunc(i, j, k) 

Depending on how lazy you are, you may not need all the calls in iterator / view.

+1
source

Just read the 20 or so first linked links that appear on the side (and indeed where you are shown when you first wrote the name of your question).

0
source

Turn the problem upside down. Skip "do" as a closure. That the whole point of using a functional language

0
source

Iterator.zip will do this:

 iterator1.zip(iterator2).zip(iterator3).map(tuple => doSomething(tuple)) 
0
source

All Articles