В чем разница между функцией Clojure (nth [coll index]) и композицией (последняя (возьмите индексный столбец))
Я пытаюсь работать через книгу Стюарта Халлоуэя "Программирование Clojure. Весь этот функциональный материал для меня очень новый.
Я понимаю, как
(defn fibo[]
(map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
генерирует последовательность Фибоначчи лениво. Я не понимаю, почему
(last (take 1000000 (fibo)))
работает, а
(nth (fibo) 1000000)
выбрасывает OutOfMemoryError. Может ли кто-нибудь объяснить, как эти два выражения отличаются? Является ли (nth) каким-то образом удерживающим головку последовательности?
Спасибо!
Ответы
Ответ 1
Я думаю, вы говорите о проблеме, которая обсуждалась в группе google, а Rich Hickey предоставил патч, который решил проблему. И книга, опубликованная позже, не охватывала эту тему.
В clojure
1.3 ваш пример nth
работает с незначительными улучшениями в функции fibo
. Теперь, из-за изменений в 1.3, мы должны явно помечать M
для использования произвольной точности, или она падает с throwIntOverflow
.
(defn fibo[]
(map first (iterate (fn [[a b]] [b (+ a b)]) [0M 1M])))
И с этими изменениями
(nth (fibo) 1000000)
преуспеть (если у вас достаточно памяти)
Ответ 2
Какую версию Clojure вы используете? Попробуйте (clojure -version) на repl. Я получаю одинаковые результаты для обоих выражений в 1.3.0, а именно: переполнение целых чисел.
Для
(defn fibo[]
(map first (iterate (fn [[a b]] [b (+ a b)]) [(bigint 0) 1])))
Я получаю правильные результаты для обоих выражений (действительно большое целое число...).
Ответ 3
Я думаю, что вы можете использовать определенный предел памяти для своего компьютера, а не реальную разницу в функциях.
Глядя на исходный код для nth в https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java, он не похож ни на n-ое, ни на взятие, сохраняя голову.
Однако n-й использует индексирование с нулевым индексом, а не число по числу. Ваш код с n-м выбирает элемент 1000001st последовательности (тот, который указан в индексе 1000000). Вы кодируете с помощью take, возвращает конечный элемент в последовательности элементов 1000000. То, что элемент с индексом 999999. Учитывая, как быстро растет волокно, этот последний элемент может быть тем, который сломал верблюда.
Кроме того, я проверял источник 1.3.0. Возможно, более ранние версии имели разные реализации. Чтобы ваше фибо было нормально работать в 1.3.0, вам необходимо использовать арифметические функции, которые будут способствовать присвоению чисел бонусам:
(defn fibo[]
(map first (iterate (fn [[a b]] [b (+' a b)]) [0 1])))