首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >解释懒惰序列中的“迷失你的头”

解释懒惰序列中的“迷失你的头”
EN

Stack Overflow用户
提问于 2011-04-18 11:08:57
回答 4查看 1.5K关注 0票数 20

在Clojure编程语言中,为什么这段代码以优异的成绩通过?

代码语言:javascript
复制
(let [r (range 1e9)] [(first r) (last r)])

虽然这个失败了:

代码语言:javascript
复制
(let [r (range 1e9)] [(last r) (first r)])

我知道这是关于“失去理智”的建议,但你能给我解释一下吗?我还不能消化它。

更新:

真的很难选择正确的答案,两个答案提供了令人惊讶的信息。

注:代码片段来自“闭合的Joy”。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-04-18 11:15:01

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)。因此,它不能在运行过程中丢弃任何东西,并且(我假设)会耗尽内存。

票数 26
EN

Stack Overflow用户

发布于 2011-04-18 20:32:28

为了详细说明dfanRafał的答案,我花时间用YourKit分析器运行了这两个表达式。

看到JVM在工作是一件很有趣的事情。第一个程序对GC非常友好,JVM在管理内存方面表现出色。

我画了一些图表。

GC友好:(让r(范围1e9))

这个程序的内存非常低;总体来说,不到6兆字节。如前所述,它对GC非常友好,它进行了大量的收集,但只使用了很少的CPU。

头固定器:(字母r(范围1e9))

这是一个非常需要内存的版本。它会占用300MB的RAM,但这还不够,程序无法完成( JVM在不到一分钟后就死了)。GC占用了高达90%的CPU时间,这表明它拼命地尝试释放所有可以释放的内存,但找不到任何内存(收集的对象非常少甚至没有)。

编辑第二个程序内存不足,这触发了堆转储。对此转储的分析表明,70%的内存是无法收集的java.lang.Integer对象。这是另一个截图:

票数 31
EN

Stack Overflow用户

发布于 2011-04-18 20:19:27

这里真正关键的是序列到r的绑定(而不是已经计算过的(first r),因为您不能根据它的值来评估整个序列)。

在第一种情况下,当计算(last r)时,绑定不再存在,因为不再有包含r的表达式需要计算。在第二种情况下,尚未计算的(first r)的存在意味着计算器需要保持与r的绑定。

为了显示不同之处,计算结果为OK:

代码语言:javascript
复制
user> (let [r (range 1e8) a 7] [(last r) ((constantly 5) a)])

[99999999 5]

虽然这样做失败了:

代码语言:javascript
复制
(let [r (range 1e8) a 7] [(last r) ((constantly 5) r)])

尽管(last r)后面的表达式忽略了r,但求值器并不那么智能,它保留了与r的绑定,从而保持了整个序列。

编辑:我发现了一个帖子,里奇·希基解释了在上述情况下负责清除对头部的引用的机制的细节。这就是:Rich Hickey on locals clearing

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5698122

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档