问题
I wrote a function that produces a Stream[Long] consisting of the products of x-digit numbers.
For example, 2 digit numbers is 1 to 99 (inclusive).
def getXDigitProducts(x: Int): Stream[Long] = {
val MAX = Math.pow(10, x)
@tailrec
def go(outer: Int, inner: Int, acc: Stream[Long]): Stream[Long] =
(outer, inner) match {
case (MAX, _) => acc
case (_, MAX) => go(outer + 1, 1, acc)
case (_, _) => go(outer, inner + 1, Stream[Long](outer * inner) ++ acc)
}
go(1, 1, Stream[Long]())
}
Waltkthrough:
> Example: getXDigitProducts(2) will produce a stream of:
>
> 1*1 1*2 1*3 ... 1*98 1*99
> 2*1 2*2 2*3 ... 2*98 2*99
> 99*1 99*2 99*3 ... 99*98 99*99
Using x = 2 works, but passing in x = 3 throws an OutOfMemory
exception on outer = 668
.
What is the cause of this exception?
When executing Stream[Long](outer * inner) ++ acc
, is there some intermediate Stream that isn't getting properly garbage collected?
回答1:
Floris is absolutely right. Once you call go
, it will only return once it reaches case (MAX, _)
. At that point, everything has been created and allocated in memory.
I see you tried to ensure tail recursion, but the trick with Stream
is to not have tail recursion. You want to build the start of a Stream
, and append the recursion call to it.
This won't cause stack overflow problems because, instead of recursing, the function will return at that point, and the "recursive" call will only be made once you access that element of the stream -- at a higher level in the stack.
So, get rid of acc
, and turn this
go(outer, inner + 1, Stream[Long](outer * inner) ++ acc)
into this
(outer * inner) #:: go(outer, inner + 1)
or something similar -- you might have to do a little exploring to find out how to ensure you don't evaluate the recursive calls strictly.
来源:https://stackoverflow.com/questions/22593362/outofmemory-exception-when-creating-stream-of-3-digit-products