欢迎!有关更多关于该功能的信息,请参阅关于页面。
关于上下文,开始于Slack:https://clojurians.slack.com/archives/C03S1L9DN/p1650480331191719
这段代码在CLJ上运行正常,但在CLJS上会因StackOverflowError失败
StackOverflowError
(def primes (remove (fn [x] (some #(zero? (mod x %)) primes)) (iterate inc 2)))
在此情况下,CLJ和CLJS之间重要的区别是,在前者中,lazy-seq会导致整个身体的函数包装^:once。
lazy-seq
^:once
它之所以这样工作,是因为在上面的代码中,内部primes是一个封闭的值,它变成了一个本地。当lazy-seq的体函数(带有^:once)第二次被调用时,其本地为nil,因此内部primes也是nil。迭代停止。在CLJS中,没有:once,也没有本地清除,因此内部primes永远不会是nil,在没有调用那个zero?的情况下,继续执行到那个fn中。
primes
nil
:once
zero?
fn
即使推理听起来不太合理,但关于是确实::once导致了差异的假设可以很容易地通过在CLJ中不带^:once实现lazy-seq并通过注意到这会导致StackOverflowError来证实。
::once
我不明白在这种情况下做什么,但也许至少应该在https://script.clojure.org/about/differences页面上对其做笔记。
在Slack讨论中链接的博客文章关于这一点是错误的。
我无法就cljs方面的事情进行评论,但在clj方面,在这个表达式中primes永远不会被封闭,引用全局变量的名称没有其值的封闭。
primes总是指向正在生成的延迟,而没有指向nil。这个工作原理是因为some的短路性质,以及任何可以整除一个数的数总是出现在序列中的数之前。