我正在尝试通过Stuart Halloway的书“Programming Clojure”。这整个功能对我来说都很新鲜。
我明白了
(defn fibo[]
(map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
懒洋洋地生成斐波纳契数列。我不明白为什么
(last (take 1000000 (fibo)))
有效,而
(nth (fibo) 1000000)
抛出OutOfMemoryError。有人可以解释这两个表达方式有何不同?是(第n)以某种方式坚持序列的头部?
谢谢!
答案 0 :(得分:4)
我认为您正在讨论google group中讨论过的问题,Rich Hickey提供了解决问题的patch。而后来发表的这本书并未涵盖这一主题。
在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)
成功(如果你有足够的记忆)
答案 1 :(得分:1)
你使用的是什么Clojure版本?在repl上尝试(clojure-version)。对于1.3.0中的两个表达式,我得到相同的结果,即整数溢出。
有关
(defn fibo[]
(map first (iterate (fn [[a b]] [b (+ a b)]) [(bigint 0) 1])))
我得到两个表达式的正确结果(一个非常大的整数......)。
答案 2 :(得分:0)
我认为您可能会为您的机器达到特定的内存限制,而不是真正的功能差异。
查看https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java中第n个的源代码,它看起来不像是n或者是保留头部。
但是,nth使用从零开始的索引,而不是按项目编号计数。具有nth的代码选择序列的1000001st元素(索引为1000000的元素)。使用take的代码返回1000000元素序列中的最后一个元素。这是指数999999的项目。考虑到纤维增长速度有多快,最后一项可能就是那个打破骆驼背部的项目。
另外,我正在检查1.3.0源代码。也许早期版本有不同的实现。为了让您的fibo在1.3.0中正常工作,您需要使用将数字提升为bignums的算术函数:
(defn fibo[]
(map first (iterate (fn [[a b]] [b (+' a b)]) [0 1])))