Turn a List of Iterators into an Iterator of Lists in Scala 3

69 views Asked by At

Ahoy! I am learning Scala and making my way through the book Functional Programming in Scala. I have been playing around and found myself in the situation where I want to be able to turn a List of Iterators into an Iterator of Lists, where I start with an inital List[T] and have a function that creates an Iterator of T => Iterator[T]. I can map that across the initial list and get a List[Iterator[T]], but I ultimately want to turn that into an Iterator[List[T]].

I am certain that I have not explained that well, so here is an example:

def collatz(n: Int): Int =
  n % 2 match
    case 0 => n / 2
    case 1 => 3 * n + 1

def collatzes(n: Int): Iterator[Int] =
  Iterator.iterate(n)(collatz)


@main def main: Unit =
  val inits = List(1, 2)
  val iters = inits.map(collatzes)
  val iter = ???
  // want to turn this list of iterators into an iterator of lists,
  // that gives me a list of the _.next of each iterator on each
  // call of iter.next

I'm sure I could easily do what I need with a loop of some kind, but would love to know if there is an idiomatic functional way to do this type transform in Scala 3.

Cheers!

2

There are 2 answers

0
stefanobaghino On

Hoping not to be too simplistic, I would probably just do the following:

val iter = iters.iterator.map(_.toList)

The iterator method is defined on all Iterables, including Lists, and as you can imagine it returns an Iterator of the original collection. The map function applies a function to each nested Iterator, turning each one in a concrete list. Since the Iterator is lazy, the nested Iterators will become a concrete List only when you actually use iter.

A word of warning: Iterators are great, but you need to handle them with care if you wish to use a purely functional approach, since Iterators are designed to be stateful, resulting in behavior that doesn't lend itself to make function referentially transparent, e.g.:

// The iterator needs to be traversed for the size to be known
// but the process of traversing the iterator moves its internal
// cursor to the end, resulting in the first it.size to be == 1
// and the second one to be == 0
val it = Iterator.iterate(0)(_ + 1).takeWhile(_ < 1)
assert(it.size != it.size)

For a lazy, memoized, built-in alternative to Iterators you might want to have a look at LazyLists or Views.

1
Dima On

Not sure I understand correctly what you are asking here ... It sounds like you want to "transpose" your list, so that you can iterate over all the iterators at once?

Something like this perhaps?

Iterator.continually(iters.map(_.next))