2

次のコードでは、チャネルをリッスンし、読み取った値をエージェントに書き込む go ループを使用しています。次に、同じループで、log-to-file 関数がエージェントの各項目をファイルに書き込むことになっています。ただし、[8 6 5 13] のエージェントの内容が 865n13 としてファイルに吐き出される可能性があるため、ファイルへの出力は異常です。ロガー関数からログからファイルへの呼び出しを削除して個別に呼び出すと、ファイル出力がうまくいく場合とそうでない場合があります。誰かが私に何が起こっているのかを説明し、一貫した動作を示す安全な方法を教えてもらえますか? 一貫した動作を実現するには、エージェントを使用しないことがおそらく最善の方法であることは承知しています。学習の精神で、エージェントで達成できることとできないことを知りたいだけです。よろしくお願いします!

(defn log-to-file [file] (for [i (deref log)] (spit file (str "\n" i) :append true)))

(defn logger [file]
  (go 
   (loop []
     (when-let [v (<! print-chan)]
       (send log conj v)
       (log-to-file file)
       (recur)))))

;; testing it out

(def log (agent []))
(>!! print-chan 5)
(logger "resources/test.txt")
(deref log)

編集:

これは、以下の受け入れられた回答のコメントに照らして書き直されたコードです。このコードを使用することはまったくお勧めしません。さらに言えば、副作用のある go ループでエージェントを使用することもお勧めしません。

(def ch (chan))
(def a (agent []))

(defn double-async-print []
    (go-loop []
       (when-let [x (<! ch)]
         (send a conj x)
         (print (clojure.string/join "\n" @a)))
     (recur)))

(go (doseq [n (range 10)] (>! ch n)))

(double-async-print) 

    ;=>jsync 0.1.0-SNAPSHOT[stdout]:    
    00
    10
    1
    20
    1
    2
    30
    1
    2
    3
    40
    1
   ...
4

1 に答える 1

3

あなたの混乱を引き起こすために相互作用している多くのことが起こっていると思います。それ自体の問題は、go-loop 内でのエージェントの使用ではありません。

sendは非同期であるため、 in への呼び出しがループ内で呼び出されたときに変更が表示されるという保証はありderefませんlog-to-filelog-to-file

forまた、副作用のあるコード (file io with )を誤って使用していますspit。他の言語の命令型 for-each ループと同様に使用しようとしているようです。doseq副作用のためにシーケンスを処理するための正しい clojure コンストラクトです ( Clojure における dosq と for の違いを参照)。

また、エージェントで構築されているベクトルのコンテンツを削除することも何もしないため、同期的に実行されていたとしても、たとえば、:a、:b、:c を一度に 1 つずつ追加して、次のようなファイルに出力します。

:a
:a
:b
:a
:b
:c

それがあなたのコードから期待される動作であるかどうかは、質問からは明らかではありません。


clojure.string/join の余談:

doseq一般的なポイントとして、上記のように spit を使用することは、ベクターまたはその他のコレクションを改行で区切られたファイルに出力したいだけの場合に正しく使用していたとしても、非常に非効率的です。clojure.string/joinたとえば、使用する方がはるかに優れています

user>  (print (clojure.string/join "\n" [:a :b :c :d]))
:a
:b
:c
:d

この関数を使用して、pprintprint に実際に渡された文字列がエスケープするとどのように見えるかを確認できます (pprintこれは、この使用よりもはるかに一般的であり、あらゆる種類の clojure データに優れた視覚的形式を提供します)。

user> (clojure.pprint/pprint (clojure.string/join "\n" [:a :b :c :d]))
":a\n:b\n:c\n:d"
于 2014-05-13T14:22:10.363 に答える