lazy-seq的分步示例

时间:2012-12-05 22:46:49

标签: clojure equals lazy-evaluation

我很难理解懒惰工作以及缓存是如何工作的。

我认为lazy-seq在工作中的分步示例在这里可以提供帮助。例如,我读过以下问题:

Clojure lazy sequence usage

但目前尚不清楚。

我的问题是呼叫如何确定另一个呼叫是否与缓存呼叫“相等”以及它在缓存中保留了多长时间?我尝试(source lazy-seq)但显然它在Java领域,所以我在这里运气不好。

对于一个简单的lazy-seq,只取一个参数(比如说两个幂的列表),如果我用5然后8调用它怎么办?这两个值只是缓存了吗?

创建和缓存无限列表以获得无限结构有什么意义如果我要通过缓存每个已经调用惰性函数的输入来破坏内存?

因为它表示它会在每次后续调用中缓存结果......使用's'。

  

1:参数结果为'1'缓存2:参数结果为   '2'缓存3:争论的结果为'3'缓存... 2 30:我   数到2 30这很好,因为我很懒,但是现在   内存中有一个2 ** 30的缓存,可以缓存以前所有的呼叫   随后的电话。

或者它只是最后一次缓存的调用?

如果我写一个以树为参数的懒函数怎么办?它是否在传递的参数上运行 equals?以了解是否需要进行新的评估?

可以在运行时以某种方式跟踪此行为吗?

2 个答案:

答案 0 :(得分:8)

懒惰序列中的“缓存”不是可变缓存,它会像在webapp中使用的那样过期,它是一个大小为1的缓存,并且列表中的每个单元格中都有一个。 'cache'要么包含一个值,要么包含计算值的代码,而不是两者。一旦它计算了它缓存值的值(在那个单元格/条目中),如果有人再次读取单元格,它会直接给它们值,而不是调用代码。

这是一个简化的虚构repl会话来说明这一点:

user> (def a (range))
a = [code-for-rest]
user> (first a)
a = [code-for-first, code-for-rest]
a = [0, code-for-rest]
result=> 0
user> (first a)
a = [0, code-for-rest]
result=> 0
user> (nth a 10)
a = [0]->[1]->[2]->[3]->[4]->[5]->[6]->[7]->[8]->[9, code-for-rest]
result=> 4

在这个例子中,每个单元格最初包含(这是一个简化来说明这一点)生成值的代码和生成列表其余部分的代码(如果这是列表的末尾则为nil) 。一旦该单元格被实现(使其变得不紧密),它就会用实际值替换它的内容,因此它现在包含生成序列其余部分的值和代码。当读取列表中的下一个单元时,它将首先通过代码进行生成(包含在单元中),然后新单元中的第n个代码将生成该单元的值。

答案 1 :(得分:1)

这里有一个玩具示例,显示运行时发生的情况:

(defn times-two[number]
 (print "- ")
 (* 2 number))

(def powers-of-two (lazy-cat [1 2] (map times-two (rest powers-of-two))))

(println (take 10 powers-of-two))
(println (take 12 powers-of-two))

输出应为:

(1 - 2 - 4 - 8 - 16 - 32 - 64 - 128 - 256 512)

(1 2 4 8 16 32 64 128 256 - 512 - 1024 2048)