Clojure和ClojureScript REPL产生不同的输出

mac*_*mac 4 clojure depth-first-search read-eval-print-loop clojurescript

使用以下递归定义深度优先搜索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)
Run Code Online (Sandbox Code Playgroud)

Cloure REPL输出:

:a
:b
:c
:e
:d
:f
:g
:f
;; => ((() ()) (()) (()))
Run Code Online (Sandbox Code Playgroud)

CLojureScript REPL输出:

:a
:b
:d
:f
:c
:g
:e
;; => ((() ()) (()) ())
Run Code Online (Sandbox Code Playgroud)

Ale*_*eph 6

Clojure会for产生一个懒惰的序列.所有重复dfs调用的实际评估仅由REPL触发,因为它需要打印函数的输出,即((() ()) (()) ()).如果您评估(do (dfs graph :a) nil),则只会:a打印出来.

现在,Clojure的懒惰序列在大小为32的块进行评估以提高效率.因此,当REPL(通过str函数)评估第一个元素延迟序列顶层for(应该打印:b)时,该seq的其他元素也会被评估,:c并且:e在子节点的序列被评估之前得到并打印(这是懒惰的)太).

相比之下,Clojurescript的延迟序列不会被分块(LazySeq 不实现 IChunkedSeq)并且逐个进行评估,因此当返回值递归地转换为字符串时,所有内容都按深度优先顺序进行评估.

为了说明这一点 - (first (for [i (range 300)] (do (println "printing:" i) i)))在Clojure和CLJS中尝试使用REPL - 你将获得在clojure中打印的32个数字和在CLJS中只有一个数字.

如果你想评价的顺序更好的保证,您可以使用doseq替代for或包裹fordoall.

希望这可以帮助.

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

我不确定我是否正在关注您当前想要如何使用DFS的结果.如果要使用副作用,即将所有节点打印到控制台,请使用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))))))
Run Code Online (Sandbox Code Playgroud)

这会将所有节点打印到控制台,深度优先.如果要将遍历作为返回值,请使用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)))))))
Run Code Online (Sandbox Code Playgroud)

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