Chi*_*ron 19 functional-programming clojure jvm-languages lazy-evaluation
在Clojure编程语言中,为什么这段代码会以绚丽的色彩传递?
(let [r (range 1e9)] [(first r) (last r)])
Run Code Online (Sandbox Code Playgroud)
虽然这个失败了:
(let [r (range 1e9)] [(last r) (first r)])
Run Code Online (Sandbox Code Playgroud)
我知道这是关于"失去理智"的建议,但请你解释一下吗?我还没能消化它.
更新:
真的很难找到正确的答案,两个答案是惊人的信息.
注意:代码片段来自"Clojure的喜悦".
Leo*_*nel 29
为了详细说明dfan和Rafał的答案,我花时间用YourKit分析器运行这两个表达式.
看到JVM正常运行真是令人着迷.第一个程序是GC友好的,JVM真的很注重管理它的内存.
我画了一些图表.
这个程序的内存运行很低; 总体而言,不到6兆字节.如前所述,它非常友好GC,它会产生大量的集合,但是使用的CPU非常少.
这个人非常渴望记忆.它高达300 MB的RAM,但这还不够,程序没有完成(JVM死不到一分钟).GC占用CPU时间的90%,这表明它拼命地尝试释放任何内存,但却找不到任何内存(收集的对象几乎没有).
编辑第二个程序内存不足,触发堆转储.对此转储的分析表明,70%的内存是java.lang.Integer对象,无法收集.这是另一个截图:
dfa*_*fan 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)
.所以它不能随意抛出任何东西,并且(我认为)内存不足.
Raf*_*ird 11
这里真正关注的是序列的绑定r
(不是已经评估过的(first r)
,因为你不能从它的值中评估整个序列.)
在第一种情况下,(last r)
评估时绑定不再存在,因为没有更多的表达式r
需要评估.在第二种情况下,尚未评估的存在(first r)
意味着评估者需要保持绑定r
.
为了显示差异,这评估OK:
user> (let [r (range 1e8) a 7] [(last r) ((constantly 5) a)])
[99999999 5]
Run Code Online (Sandbox Code Playgroud)
虽然失败了:
(let [r (range 1e8) a 7] [(last r) ((constantly 5) r)])
Run Code Online (Sandbox Code Playgroud)
即使后面的表达式(last r)
忽略了r
,评估器也不是那么聪明并保持绑定r
,从而保持整个序列.
编辑:我发现了一个帖子,其中Rich Hickey解释了在上述情况下负责清除对头部的引用的机制的细节.这是:Rich Hickey在当地人清理