4

次の再帰的な深さ定義を使用すると、Clojure(JVM)とClojureScript(ブラウザに接続されたreplとlumoの両方でテスト済み)REPLは2つの異なる出力を生成します。が異なり、Clojure REPLは複製:fを生成します。 ClojureScriptの順序は、私が期待する動作です。どうしてこれなの?ClojureとClojureScript REPLは異なる出力を生成します

コード:

(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) 

Cloure REPL出力:

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

CLojureScript REPL出力:

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

clojure 1.8と1.9-alpha14の両方で、最後に ':f'がなく、私のparensはcljs parensと同じです。 – Josh

+0

それは変です、私はClojure 1.8.0とClojureScript 1.9.229を使用しています。 – mac

答えて

6

の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)チャンクされず、一つ一つを評価します。

REPLのClojureとCLJSの両方で(first (for [i (range 300)] (do (println "printing:" i) i)))を試してみると、CLJSには32個の数字が表示され、CLJSには32個の数字が表示されます。

評価の順序をより確実にする場合は、forの代わりにdoseqを使用するか、doallforを入れてください。

これが役に立ちます。

サイドノート:ちょうど@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))を取得します。あなたはまた、怠惰の利益を得るでしょう。

+0

答えをありがとう。評価の順序はチャンクによって影響を受けることに私は夢中に思えます。また、 '' 'v'''だけを出力しているときに、なぜ他の要素がREPLに出力されるのでしょうか? '' println'''を削除しても何も表示されません。 – mac

+0

問題は、副作用のあるコードと遅延コードを混ぜていることです。あなたのケースでは、評価の順序(とその事実)は、チャンクだけでなく、REPLが関数 '(()())(())'の戻り値を出力するかどうかにも依存しますあなたがそれについて考えるならそれ自体が狂っているコンソール。結果を返す場合は、結果をコンソールに出力したい場合は必須のコードを使用し、戻り値として返す場合は機能/怠惰なコードを使用することができます(上記の私の投稿の編集例を参照してください)。しかし、2つのアプローチを混ぜることは、適切なClojureではありません。 –

関連する問題