Stream and LazyList have unfold method, but Iterator still doesn’t have it. I think it’s unfortunate as Stream and LazyList memoize all the values, while Iterator doesn’t. Consider the following code (works under Scala 2.13.0-M4):
object UnfoldIterator extends Unfold with App {
def unfoldIterator[A, S](init: S)(f: S => Option[(A, S)]): Iterator[A] = {
var currentState = init
var nextResultAndState: Option[(A, S)] = null
new Iterator[A] {
override def hasNext: Boolean = {
if (nextResultAndState == null) {
nextResultAndState = f(currentState)
}
nextResultAndState.isDefined
}
override def next(): A = {
assert(hasNext)
val (result, state) = nextResultAndState.get
currentState = state
nextResultAndState = null
result
}
}
}
run(unfoldIterator(Unfold.initialState)(Unfold.generator))
}
object UnfoldStream extends Unfold with App {
run(LazyList.unfold(Unfold.initialState)(Unfold.generator).iterator())
}
class Unfold {
sys.props.put("scala.time", "")
def run(input: Iterator[Long]): Unit = {
println("result: " + input.map(_.toString).map(_.length).sum)
}
}
object Unfold {
val initialState = 0.0
val generator: Double => Option[(Long, Double)] = num => {
if ((num * scale).toLong != (num * scale + 1).toLong) {
val nextNum = num + 1.3
if ((num * inverseDistance).toInt != (nextNum * inverseDistance).toInt) {
println(num.toLong)
}
Some((num.toLong, nextNum))
} else {
None
}
}
private def scale = 123456789.0
private def inverseDistance = 1.0 / 1234567
}
UnfoldIterator works happily even with -Xmx100m, while UnfoldStream grinds to a halt with -Xmx1g (it would probably fail with OoME after a while, but I don’t want to waste a lot of time waiting for it).
The iterator implementation I’ve written here is completely lazy, i.e. is lazy also in the first element. If you don’t use the iterator produced by unfoldIterator then the generator function will never be invoked. Is there a way to achieve that in little code using existing combinators (of course avoiding collection with memoization, like Stream or LazyList)?