I'm very new to Scala, so forgive my ignorance! I'm trying to iterate of pairs of integers that are bounded by a maximum. For example, if the maximum is 5, then the iteration should return:
(0, 0), (0, 1), ..., (0, 5), (1, 0), ..., (5, 5)
I've chosen to try and tail-recursively return this as a Stream:
@tailrec
def _pairs(i: Int, j: Int, maximum: Int): Stream[(Int, Int)] = {
if (i == maximum && j == maximum) Stream.empty
else if (j == maximum) (i, j) #:: _pairs(i + 1, 0, maximum)
else (i, j) #:: _pairs(i, j + 1, maximum)
}
Without the tailrec annotation the code works:
scala> _pairs(0, 0, 5).take(11)
res16: scala.collection.immutable.Stream[(Int, Int)] = Stream((0,0), ?)
scala> _pairs(0, 0, 5).take(11).toList
res17: List[(Int, Int)] = List((0,0), (0,1), (0,2), (0,3), (0,4), (0,5), (1,0), (1,1), (1,2), (1,3), (1,4))
But this isn't good enough for me. The compiler is correctly pointing out that the last line of _pairs is not returning _pairs:
could not optimize @tailrec annotated method _pairs: it contains a recursive call not in tail position
else (i, j) #:: _pairs(i, j + 1, maximum)
^
So, I have several questions:
- directly addressing the implementation above, how does one tail-recursively return Stream[(Int, Int)]?
- taking a step back, what is the most memory-efficient way to iterate over bounded sequences of integers? I don't want to iterate over Range because Range extends IndexedSeq, and I don't want the sequence to exist entirely in memory. Or am I wrong? If I iterate over Range.view do I avoid it coming into memory?
In Python (!), all I want is:
In [6]: def _pairs(maximum):
...: for i in xrange(maximum+1):
...: for j in xrange(maximum+1):
...: yield (i, j)
...:
In [7]: p = _pairs(5)
In [8]: [p.next() for i in xrange(11)]
Out[8]:
[(0, 0),
(0, 1),
(0, 2),
(0, 3),
(0, 4),
(0, 5),
(1, 0),
(1, 1),
(1, 2),
(1, 3),
(1, 4)]
Thanks for your help! If you think I need to read references / API docs / anything else please tell me, because I'm keen to learn.
for
. The problem I have is thatpairs
, as you've suggested, returnsIndexedSeq
. Hence the whole result will exist in memory whenpairs
is called. Could you please elaborate on how to use views to avoid this? – Headman