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
, в то время как почти то же самое с использованием потока делает... Что здесь происходит?