Сокращение большого потока без переполнения стека

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

9
задан kaoD 19 April 2012 в 14:53
поделиться