有人可以帮助我解决我在这里失踪的问题:
def isDivisibleByRange(n: Int, r: Range) = {
r.forall(n % _ == 0)
}
def from(n: Int): Stream[Int] = n #:: from(n + 1)
现在,以下内容给了我一个OOM:
val o = from(1).find(isDivisibleByRange(_, Range(2, 21)))
答案 0 :(得分:2)
您的代码没有任何问题,问题在于Stream.find
,或者更确切地说是find
中继承该方法的LinearSeqOptimized
:
override /*IterableLike*/
def find(p: A => Boolean): Option[A] = {
var these = this
while (!these.isEmpty) {
if (p(these.head)) return Some(these.head)
these = these.tail
}
None
}
此方法已编写为使用while循环运行,而不是使用递归。对于非延迟序列,这不会使用任何额外的内存,并且比递归解决方案运行得更快。不幸的是,Stream
是懒惰的,当此方法与大(特别是无限)序列一起使用时,会导致内存消耗失控。发生这种情况是因为该方法始终将其this
指针保留在堆栈上,因此垃圾收集器永远不会收集Stream
的开头或任何其余内容。
可以通过编写递归工作的find
来解决问题:
import annotation.tailrec
@tailrec
def betterFind[A](s: Stream[A], p: A => Boolean): Option[A] = {
if (s.isEmpty)
None
else if (p(s.head))
Some(s.head)
else
betterFind(s.tail, p)
}
在实践中,使用Stream.iterator.find
而不是编写自己的方法可能更简单。 Stream.iterator
会在Iterator
的元素上返回Stream
,即使在无限流中也可以安全使用。
答案 1 :(得分:1)
让我们稍微介绍一下你的代码:
from(1) // 2, 3, 4, 5, 6 ...
isDivisibleByRange(1, Range(2, 21)
Range(2, 21).forall(1 % _ == 0) // false
isDivisibleByRange(2, Range(2, 21)
Range(2, 21).forall(2 % _ == 0) // false
isDivisibleByRange(3, Range(2, 21)
Range(2, 21).forall(3 % _ == 0) // false
... OOME
满足(2 to 21) mod == 0
的第一个数字是232792560
。所以,在到达232792560
之前,你会得到一个OOME。
由于stream只是一个惰性列表,因此您基本上创建了一个包含所有可能正整数的列表,这将占用您的所有内存。也许增加你的堆空间?请记住,流容器周围有一些额外的分配,而不是int的4个字节,所以也许-Xmx4G
。
<强>更新强>
使用迭代器方法,您可以在最短内存的情况下以适当的时间执行此操作(find
上的Range
通过Iterator
实现):
Range(1, Int.MaxValue).find(r => Range(2, 21).forall(r1 => r % r1 == 0))
//Some(232792560)