在Clojure编程语言中,为什么这段代码会以漂亮的颜色传递?
(let [r (range 1e9)] [(first r) (last r)])
虽然这个失败了:
(let [r (range 1e9)] [(last r) (first r)])
我知道这是关于“失去理智”的建议,但请你向我解释一下吗?我还没能消化它。
更新:
选择正确的答案真的很难,两个答案的内容非常丰富
注意:代码片段来自“Clojure的喜悦”。
答案 0 :(得分:29)
要详细说明dfan和Rafał的答案,我已花时间使用YourKit个分析器运行这两个表达式。
看到JVM正常运行真是令人着迷。第一个程序是GC友好的,JVM非常注重管理它的内存。
我画了一些图表。
这个程序的内存运行很低;总体而言,不到6兆字节。如前所述,它非常友好GC,它会产生很多集合,但是使用的CPU很少。
这个人非常渴望记忆。它高达300 MB的RAM,但这还不够,程序没有完成(JVM死不到一分钟)。 GC占用CPU时间的90%,这表明它拼命想要释放任何内存,但却找不到任何内存(收集的对象几乎没有)。
编辑第二个程序内存不足,触发堆转储。对此转储的分析表明,70%的内存是java.lang.Integer对象,无法收集。这是另一个截图:
答案 1 :(得分:25)
range
根据需要生成元素。
在(let [r (range 1e9)] [(first r) (last r)])
的情况下,它抓取第一个元素(0),然后生成十亿个 - 2个元素,随着它们抛出它们,然后抓取最后一个元素(999,999,999)。它永远不需要保留序列的任何部分。
在(let [r (range 1e9)] [(last r) (first r)])
的情况下,它生成十亿个元素以便能够评估(last r)
,但它还必须保持它生成的列表的开头以便以后评估(first r)
。所以它不能随意抛出任何东西,并且(我猜)会耗尽内存。
答案 2 :(得分:11)
这里真正关注的是序列与r
的绑定(不是已经评估过的(first r)
,因为你不能从它的值中评估整个序列。)
在第一种情况下,当(last r)
被评估时,绑定不再存在,因为没有更多的表达式需要r
进行评估。在第二种情况下,尚未评估的(first r)
的存在意味着评估者需要保持对r
的绑定。
要显示差异,请评估确定:
user> (let [r (range 1e8) a 7] [(last r) ((constantly 5) a)])
[99999999 5]
虽然失败了:
(let [r (range 1e8) a 7] [(last r) ((constantly 5) r)])
即使(last r)
后面的表达式忽略r
,评估者也不会 智能,并保持绑定到r
,从而保持整个序列。
编辑:我发现了一篇帖子,其中Rich Hickey解释了在上述情况下负责清除对头部引用的机制的详细信息。这是:Rich Hickey on locals clearing
答案 3 :(得分:2)
有关技术说明,请转到http://clojure.org/lazy。 Don't hang (onto) your head