n
1制限のない階乗関数を作成しようとしています(単なる好奇心からです)。大きい!)
(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