Целочисленные переполнения, используя ленивые последовательности в Clojure


Я только учусь использовать ленивые последовательности в Clojure, и я не уверен, что я делаю неправильно в следующем коде:

(defn sum [seqn]
  (reduce + seqn))

(defn fib
  ([] (concat [0 1] (fib 0 1)))
  ([a b] (lazy-seq (cons (+ a b) (fib b (+ a b))))))

(defn up-to [n seqn]
  (filter (fn [x] (< x n)) seqn))

(sum (up-to 100 (fib))) => ArithmeticException integer overflow  clojure.lang.Numbers.throwIntOverflow (Numbers.java:1388)

Суммируемые числа не должны быть больше 100, так что же вызывает переполнение целого числа?

2 8

2 ответа:

Фильтрация бесконечного seq производит бесконечный seq и уменьшение по этому поводу заставляет фильтр продолжать искать другой соответствующий элемент даже после того, как предикат перестает возвращать true.

Заменить filter на take-while. Бесконечная последовательность, порожденная (fib), заставит filter бежать вечно, но перед этим она сломается из-за ArithmeticException, которое вы испытываете. take-while остановит дальнейшее вычисление списка после того, как предикат (fn [x] (< x n)) примет значение false.

(defn up-to [n seqn]
  (take-while (fn [x] (< x n)) seqn))

(sum (up-to 100 (fib))) ;; => 232

Начиная с clojure 1.3.0 числа не автоматически повышаются до bigInt / bigDecimal.

Чтобы исправить это используйте +' вместо

Ваше 100-е число Фибоначчи слишком велико для целого числа

user> (nth (fib) 100)
354224848179261915075N