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
, в то время как почти то же самое с использованием потока ... Что здесь происходит?
См. Мой комментарий в другом ответе (также здесь). Также: Я хотел бы избежать общей реализации факториала с использованием TCR, поскольку это подразумевается как упражнение для использования ленивых диапазонов. – kaoD
@kaoD - ему не нужен диапазон и не начинается с последнего элемента. См. Пример (вставить в REPL). –