gpt4 book ai didi

Clojure 和 ClojureScript REPL 产生不同的输出

转载 作者:行者123 更新时间:2023-12-02 10:33:30 26 4
gpt4 key购买 nike

使用以下深度优先搜索的递归定义,Clojure (JVM) 和 ClojureScript(使用连接的 repl 和 lumo 的浏览器进行测试)REPL 产生两个不同的输出,即节点打印的顺序不同,并且 Clojure REPL 产生一个重复的 :f。 ClojureScript 顺序是我期望的行为。这是为什么?

代码:

(defn dfs
([g v] (dfs g v #{}))
([g v seen]
(println v)
(let [seen (conj seen v)]
(for [n (v g)]
(if-not (contains? seen n)
(dfs g n seen))))))

(def graph {:a [:b :c :e]
:b [:d :f]
:c [:g]})

(dfs graph :a)

Clure REPL 输出:

:a
:b
:c
:e
:d
:f
:g
:f
;; => ((() ()) (()) (()))

CLojureScript REPL 输出:

:a
:b
:d
:f
:c
:g
:e
;; => ((() ()) (()) ())

最佳答案

Clojure 的 for 生成一个惰性序列。所有循环 dfs 调用的实际评估仅由 REPL 触发,因为它需要打印函数的输出,即 ((() ()) (()) ()).如果你计算 (do (dfs graph :a) nil),你只会得到 :a 打印。

现在,Clojure 的惰性序列是 evaluated in chunks of size 32为了效率。因此,当 REPL(通过 str 函数)计算顶级 for 的第一个元素延迟序列(应打印 :b)时,该 seq 的其他元素也会被评估,并且在评估子节点的序列之前会打印 :c:e (这也是惰性的)。

相比之下,Clojurescript 的惰性序列不会分块(LazySeq does not implement IChunkedSeq),而是逐一求值,因此当返回值递归转换为字符串时,所有内容都按深度优先顺序求值。

为了说明这一点 - 在 Clojure 和 CLJS 的 REPL 中尝试 (first (for [i (range 300)] (do (println "printing:"i) i))) - 你将得到 clojure 中打印的 32 个数字,而 CLJS 中仅打印 1 个数字。

如果您想更好地保证计算顺序,可以使用 doseq 代替 for 或将 for 包装在 中全部

希望这有帮助。

旁注:就像@Josh一样,我在 Clojure 1.8 中最后没有得到 :f ,并且括号与 cljs 输出相同 - 这真的很奇怪......

我不确定我是否了解您当前想要如何使用 DFS 结果。如果你想使用副作用,i。 e.将所有节点打印到控制台,使用 doseq 确保它们被遍历:

(defn dfs-eager
([g v] (dfs-eager g v #{}))
([g v seen]
(println v)
(let [seen (conj seen v)]
(doseq [n (v g)]
(if-not (contains? seen n)
(dfs-eager g n seen))))))

这会将所有节点打印到控制台,深度优先。如果您想将遍历作为返回值,请使用 for 但确保您实际上返回一个有意义的值:

(defn dfs-lazy
([g v] (dfs-lazy g v #{}))
([g v seen]
(cons v
(let [seen (conj seen v)]
(for [n (v g)]
(if-not (contains? seen n)
(dfs-lazy g n seen)))))))

您将得到一个嵌套列表 (:a (:b (:d) (:f)) (:c (:g)) (:e)) - 然后您可以将其展平来进行遍历。您还将获得懒惰的好处。

关于Clojure 和 ClojureScript REPL 产生不同的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42482950/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com