Clojure函数(n[coll index])和合成函数(last(take index coll))之间的区别是什么

Clojure函数(n[coll index])和合成函数(last(take index coll))之间的区别是什么,clojure,functional-programming,out-of-memory,lazy-sequences,Clojure,Functional Programming,Out Of Memory,Lazy Sequences,我正在努力阅读斯图尔特·哈洛韦的书《编程Clojure》。这些功能性的东西对我来说是很新的 我知道怎么做 (defn fibo[] (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))) 惰性地生成斐波那契序列。我不明白为什么 (last (take 1000000 (fibo))) 工作,而 (nth (fibo) 1000000) 抛出OutOfMemory错误。有人能解释一下这两种表达方式的区别吗?(n)是否以某种方式抓住了

我正在努力阅读斯图尔特·哈洛韦的书《编程Clojure》。这些功能性的东西对我来说是很新的

我知道怎么做

(defn fibo[]
    (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
惰性地生成斐波那契序列。我不明白为什么

(last (take 1000000 (fibo)))
工作,而

(nth (fibo) 1000000)
抛出OutOfMemory错误。有人能解释一下这两种表达方式的区别吗?(n)是否以某种方式抓住了序列的开头


谢谢

您使用的Clojure版本是什么?在repl上尝试(clojure版本)。对于1.3.0中的两个表达式,我得到了相同的结果,即整数溢出

为了


对于这两个表达式,我都得到了正确的结果(一个非常大的整数…。

我认为您讨论的是中讨论的问题,而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)

成功(如果你有足够的内存)

我认为你的机器可能达到了特定的内存限制,而不是功能上的真正差异

查看其中的第n个源代码,看起来第n个或take都没有保留头部

但是,N使用基于零的索引,而不是按项目编号计数。带有n的代码选择序列的1000001个元素(索引1000000处的元素)。使用take编写的代码将返回1000000元素序列中的最后一个元素。这是索引为999999的项目。考虑到小谎的增长速度,最后一个小谎可能会让骆驼背骨折

另外,我正在检查1.3.0源代码。也许早期版本有不同的实现。要使fibo在1.3.0中正常工作,您需要使用将数字提升为bignum的算术函数:

(defn fibo[]
    (map first (iterate (fn [[a b]] [b (+' a b)]) [0 1])))

在tryclj.com上,这两种方法对我都不起作用,因为这个数字太大了,会导致溢出。因为你没有提到任何东西,所以我不相信任何东西是“抓住头”。你确定这不仅仅是因为数字太大了吗?last的实现是一个直接的O(n)尾递归实现,它什么都不保留。nth是用Java实现的,我很确定它也不会保留任何东西。因此,这两个序列在理论上都可以正常工作。我能想到的唯一一件事,虽然我不清楚这会影响结果,是你的第n次通话实际上比你上次通话多计算1项。nth 1000000=1000000第1项(0索引)@vedang谢谢。。。我不会抓住那个重要的区别。这不是我问题的根源,尽管我没有意识到要获取的参数是序列的大小,而n的参数是索引。我使用的是随书分发的快照版本,1.1.0-alpha-snapshot。更改为1.3.0起作用。我猜我的版本中包含了你提到的bug。。。也就是说,“最近的一次优化尝试在RT.nth中引入了一个头部保留跃点”,虽然“M”不是必需的。Clojure似乎在需要时转换为BigInt,至少在1.3.0中是这样。@Josh,不是在我的机器上。看看我的答案。
(defn fibo[]
    (map first (iterate (fn [[a b]] [b (+' a b)]) [0 1])))