当前位置: 首页 > 知识库问答 >
问题:

懒惰和堆栈溢出

陈晟睿
2023-03-14

我写了以下内容:

(fn r [f xs]
  (lazy-seq
    (if (empty? xs)
    '()
    (cons (f (first xs)) (r f (rest xs))))))

解决4clojure.com的问题#118:http://www.4clojure.com/problem/118

当我询问(type...)时,不出所料,我会得到一个clojure.lang.lazyseq,但我不知道这与简单地删除lazy-seq“包装”有什么区别。

当然,现在如果删除lazy-seq,我会得到一个stackoverflow,为什么要执行这个:

(= [(int 1e6) (int (inc 1e6))]
   (->> (... inc (range))
        (drop (dec 1e6))
        (take 2)))

否则(也就是说:如果我让lazy-seq包装到位),它似乎工作得很好。

(defmacro dbg [x] `(let [x# ~x] (println "dbg: " '~x "=" x#) x#))

如果有人能用这个简单的函数和简单的测试来解释懒惰在这里是如何工作的,什么时候调用什么,等等,那就太好了。

共有1个答案

金亦
2023-03-14

整个魔力在于clojure.lang.LazySeq java类。它本身实现了ISeq接口,并将lazy-seq宏的s-expressions参数转换为不带任何参数的函数,并传递给clojure.lang.LazySeq的构造函数(传递给以IFN对象为参数的构造函数),而且由于最后您再次调用了R函数(返回的是ISeq而不是完整的列表),这使得LazySeq可以懒洋洋地计算项。

所以基本上流程是这样的:

  • LazySeq调用传递给它的Fn(即代码的rest主体)
  • 此Fn调用返回一个ISeq,因为列表实现ISeq。由于递归调用R,返回ISeq(list),第一个值作为具体值,第二个是LazySeq对象。返回的ISeq存储在类中的局部变量中。
  • LazySeq在调用next item时的ISeq实现会调用它在上面步骤中存储在本地类变量中的next of ISeq(列表),并检查它是否属于LazySeq类型(由于r调用,它将位于第二个item中),如果是LazySeq,则计算该类型并返回then item否则直接返回item(您传递给cons的第一个具体值)

我知道这是一件有点令人费解的事情:)。我刚才也浏览了Java代码,在我意识到这种神奇的方法是可能的之后,我终于明白了,因为对r的递归调用本身会返回一个惰性序列。所以就有了它,一种自定义的分隔符延续:)

 类似资料:
  • (使用Java 15.0+) 我正在用这些实现一个堆栈 如何检查堆栈是否下溢?从Overflow boolean变量中,我们知道如果一个数字不能用8位来表示,就会导致溢出。但是,如果数字不能以这种方式表示,我们如何检查呢?我还认为应该有更多的情况下堆栈溢出,像jumpz或jumpn导致通过指令的无限循环。

  • 我有一个执行快速排序的应用程序。在我开始给它一些更大的数字(我第一次得到它是10000000)之前,它工作得很好。我知道是由递归引起的,但我不明白为什么我的应用程序会因此而崩溃。如有任何建议,将不胜感激。这是我的密码:

  • 做这件事的正确方法是什么?

  • 问题内容: 下面给出的代码显示了运行时的Stackoverflow错误。但是,如果我使另一个类CarChange创建Car的对象,它将成功运行。我是一个初学者,请执行以下代码以了解在Java中进行向上转换的重要性。 问题答案: 一个stackoverflow通常意味着您有一个无限循环。 收到此消息的原因是因为您从testdrive方法调用驱动器,并且在该方法中再次调用drive。

  • 问题内容: 现在,Stack Overflow使用redis,它们是否以相同的方式处理缓存失效?即散列到查询字符串+名称的身份列表(我想这个名称是某种用途或对象类型的名称)。 也许他们然后直接通过id(从一堆数据库索引中绕过,而是使用效率更高的聚集索引)直接从缓存中检索缺少的单个项。那会很聪明(杰夫提到的补液?)。 现在,我正在努力寻找一种简洁地解决所有问题的方法。在我自己进行初次切割之前,是否有

  • 问题内容: 这有效:http : //play.golang.org/p/-Kv3xAguDR。 这导致堆栈溢出:http : //play.golang.org/p/1-AsHFj51O。 我不明白为什么。在这种情况下,使用接口的正确方法是什么? 问题答案: 这个 将呼叫您的,依次呼叫,等等。如果您需要解组JSON然后对其进行处理,那么一种巧妙的技术是声明一个本地类型,将数据解组到其中,然后转换