scala - 减少大数据流而不会导致堆栈溢出

标签 scala stream range stack-overflow reduce

1我正在尝试制作一个无极限的阶乘函数(只是出于好奇。)该函数适用于较大的n(尝试达到100000,并且似乎可以正常工作,尽管我无法检查输出值的正确性,因为,这是大!)

(BigInt(1) to n).reduceRight(_*_)

但是我担心整个BigInt(1) to n范围可能都在内存中,而对于reduceRight我只需要一个元素一个元素。看一下Scala的标准库代码,看起来BigInt(1) to n实际上输出了整个Range而不是一个懒惰的Stream,但是我发现了Stream.range可以像这样使用(注意n+1,流范围是排他的)
Stream.range[BigInt](BigInt(1), BigInt(n+1)).reduceRight(_*_)

它适用于n=10000(由于某种原因,它需要更长的时间!这使我认为正常范围实际上也可以是Stream吗?),但是对于n=100000,我会得到此堆栈溢出
java.lang.StackOverflowError
    at java.math.BigInteger.add(Unknown Source)
    at scala.math.BigInt.$plus(BigInt.scala:161)
    at scala.math.Numeric$BigIntIsIntegral$class.plus(Numeric.scala:28)
    at scala.math.Numeric$BigIntIsIntegral$.plus(Numeric.scala:40)
    at scala.math.Numeric$BigIntIsIntegral$.plus(Numeric.scala:40)
    at scala.math.Numeric$Ops.$plus(Numeric.scala:208)
    at scala.collection.immutable.Stream$$anonfun$range$1.apply(Stream.scala:695)
    at scala.collection.immutable.Stream$$anonfun$range$1.apply(Stream.scala:695)
    at scala.collection.immutable.Stream$Cons.tail(Stream.scala:634)
    at scala.collection.immutable.Stream$Cons.tail(Stream.scala:626)
    at scala.collection.LinearSeqOptimized$class.reduceRight(LinearSeqOptimized.scala:130)
    at scala.collection.immutable.Stream.reduceRight(Stream.scala:47)
    at scala.collection.LinearSeqOptimized$class.reduceRight(LinearSeqOptimized.scala:131)
    at scala.collection.immutable.Stream.reduceRight(Stream.scala:47)
    at scala.collection.LinearSeqOptimized$class.reduceRight(LinearSeqOptimized.scala:131)
    at scala.collection.immutable.Stream.reduceRight(Stream.scala:47)
    ...

很明显reduceRight像这样自称
reduceRight(reduceRight(first, second, op), third, op)...

因此,堆栈溢出。我认为它不是优化的尾部调用,因为它首先减小然后在返回值之前运行,因此无法优化。那我该如何解决这个问题呢?我是否应该放弃功能性方法,而瞄准定制的命令式代码以减少代码量?

令我感到奇怪的是,对于大(BigInt(1) to n).reduceRight(_*_)n不会溢出,而使用流几乎相同。

最佳答案

reduceLeft实现为在流上进行计算(并按顺序调用)。您可以验证如下:

Stream.range(1,10).map(i => print(i)).reduceLeft((a,b) => println("r"))

或者,您可以使用尾部递归:
final def fac(n: BigInt, prod: BigInt = BigInt(1)): BigInt = {
  if (n<2) prod else fac(n-1,prod*n)
}

(正如Travis指出的那样,先将小数字相乘会更快,这会花费额外的一行)。

关于scala - 减少大数据流而不会导致堆栈溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10230535/

相关文章:

c - 在子进程中操作流

r - 计算 R 中范围内的数值

scala - Spark Streaming 中的批处理大小

scala - 删除 DAO 模型中的可选字段

php - 继续在远程服务器上使用ssh2_exec显示bash长时间运行的结果

stream - 添加非数组格式的数字?或者如何过滤到数组以便我可以总结

scala - 将 groupByKey() 替换为 reduceByKey()

scala:私有(private)实用方法应该存在于伴随对象中吗?

.net - 范围数据注释属性不验证 .99?

javascript - 如何获取window.getSelection()的范围对象?