Clojure函数(第n个[coll index])和组合(last(take index coll))之间有什么区别

时间:2011-12-15 15:32:28

标签: clojure functional-programming out-of-memory lazy-sequences

我正在尝试通过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)以某种方式坚持序列的头部?

谢谢!

3 个答案:

答案 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])))