Scala Streams. Iterators, and Memoization

Posted on Sunday, February 14, 2016



Here is what I am trying to do...

I want to take an immutable Array, for example.




   val arr = Array(3, 7, 2)


I want a method to get the next element in the Array.  But, I want it to reset the index after it reads the last element in the Array.





I could do this


  val arr = Array(3, 7, 2)
 
val itr = arr.iterator

  println(itr.next)
  println(itr.next)
  println(itr.next)





And I would get back this.


3
7
2



But if I call .next one more time I get a NoSuchElement Exception, as the iterator runs out.

Iterators are designed to run through the list, they can't be reset.


What about using a Stream to solve my problem?





Stream solution


What if I made an infinite stream that gives me the next index I want.  For example, if I have an Array of length 3 I want the stream to output

0, 1, 2, 0, 1, 2, 0, 1, 2……

Here is a bit of code that does that (well gets the index as a Stream)




    val arr = Array(3, 7, 2)

    lazy val s1: Stream[Int] = 0 #:: s1.map(n => (n+1) % arr.length)
    println(s1.take(10).toList)



Here is the output



List(0, 1, 2, 0, 1, 2, 0, 1, 2, 0)



That gives me the sequence I want.  But there is one problem with the stream as is.   
Memoization!   As is it keeps track of all the values of the stream.



Here is some code that test this (Causes an out of memory error)



    val arr = Array(3, 7, 2)

    //This runs of of memory if I set memory to -Xmx64m -Xms64m
   
lazy val s1: Stream[Int] = 0 #:: s1.map(n => (n+1) % arr.length)
   
println("Last " + s1.drop(100e6.toInt).take(1).toList)



(I also reduced my memory)

 


In Intellij I set it to 64MiB and ran it.




Out of Memory!

There is simple way around this. 





Stream to Iterator


This code fixes my memoization problem


    val arr = Array(3, 7, 2)
   
val index:Iterator[Int] = Stream.iterate(0)(i => (i+1)%arr.length).iterator

    println(index.take(
10).toList)
    println(index.next)
    println(index.next)
    println(index.next)
    println(index.next)
    println(index.next)
    println(
"No Memoization " + index.drop(100e6.toInt).next)
    println(index.next)
    println(index.next)
    println(index.next)



Here is the output




Now let me update my code just to show it is getting the correct results from the array.


    val arr = Array(3, 7, 2)

   
val index:Iterator[Int] = Stream.iterate(0)(i => (i+1)%arr.length).iterator

   
println(arr(index.next))
    println(arr(index.next))
    println(arr(index.next))
    println(arr(index.next))
    println(arr(index.next))
    println(arr(index.next))
    println(arr(index.next))


And here is my output.

 


That is working the way I want it to J.

Maybe there is a better way to do this but for now this works and does not give me the Memoization memory leak.

References

[1]        Stream vs Views vs Iterators    http://stackoverflow.com/questions/5159000/stream-vs-views-vs-iterators
                Accessed 02/2016
[2]        Why iterator doesn't have any reset method?   http://stackoverflow.com/questions/7689261/why-iterator-doesnt-have-any-reset-method
                Accessed 06/2014












No comments:

Post a Comment