4

エージェントと送信機能で非常に奇妙な動作に遭遇しました。動作を少量のコードに要約しました。

このコード:

(defn weird-behavior
  []
  (let [my-agent (agent {:entries nil})]
    (doseq [entry [1 2 3]]
      (send my-agent assoc :entries (conj (@my-agent :entries) entry)))
    (Thread/sleep 100) ;; Allow time for the sends to finish
    (println my-agent)))

出力:

#<Agent@222e8b4: {:entries (3)}>

ただし、送信する各呼び出しの間に 10 ミリ秒を与えると、次のようになります。

(defn weird-behavior
  []
  (let [my-agent (agent {:entries nil})]
    (doseq [entry [1 2 3]]
      (send my-agent assoc :entries (conj (@my-agent :entries) entry))
      (Thread/sleep 10)))
    (Thread/sleep 100) ;; Allow time for the sends to finish
    (println my-agent)))

出力は期待どおりです。

#<Agent@6211e63b: {:entries (3 2 1)}>

なぜこうなった?複数のアイテムを連続してエージェントに送信するにはどうすればよいですか?

ありがとう。

4

3 に答える 3

3

問題はderef、アクションの外でエージェントを強化していることです。そのため、エージェントの現在の値を引数として に渡すと、エージェントのsendキューに入れられたすべてのアクションが同じ値 (つまりnil) を受け取ります。

値を設定する必要update-inを回避するために使用できます。deref

(defn weird-behavior
  []
  (let [my-agent (agent {:entries nil})]
    (doseq [entry [1 2 3]]
      (send my-agent update-in [:entries] conj entry))
    (Thread/sleep 100) ;; Allow time for the sends to finish
    (println my-agent)))

;= #<Agent@1a69a9c: {:entries (3 2 1)}>
于 2013-07-10T21:27:56.880 に答える
1

この問題は、エージェントに送信する関数に追加する値を出力させることで説明できます。呼び出すだけでなくassoc、値を出力するように println でこれを構成すると、スリープがある場合とない場合の関数の違いがはっきりとわかります。

(let [my-agent (agent {:entries nil})]                                                                                                                                    
  (doseq [entry [1 2 3]]                                                                                                                                                  
    (send my-agent (comp #(do (println "adding entry" %) %)                                                                                                               
                         assoc)                                                                                                                                           
          :entries (conj (@my-agent :entries) entry)))                                                                                                                    
  (Thread/sleep 100) ;; Allow time for the sends to finish                                                                                                                
  (println my-agent)) 

adding entry {:entries (1)}                                                                                                                                               
adding entry {:entries (2)}                                                                                                                                               
adding entry {:entries (3)}                                                                                                                                               
#<Agent@75bee6fc: {:entries (3)}> 

vsスリープのあるバージョン:

(let [my-agent (agent {:entries nil})]                                                                                                                                    
  (doseq [entry [1 2 3]]                                                                                                                                                  
    (send my-agent (comp #(do (println "adding entry" %) %)                                                                                                               
                         assoc)                                                                                                                                           
          :entries (conj (@my-agent :entries) entry))                                                                                                                     
    (Thread/sleep 10))                                                                                                                                                    
  (Thread/sleep 100) ;; Allow time for the sends to finish                                                                                                                
  (println my-agent)) 

adding entry {:entries (1)}                                                                                                                                               
adding entry {:entries (2 1)}                                                                                                                                             
adding entry {:entries (3 2 1)}                                                                                                                                           
#<Agent@1c36ee92: {:entries (3 2 1)}>  

これにより、3 つの呼び出しすべてがエージェントの状態を読み取り、3 つのすべての呼び出しが互いに独立して読み取った値に加算されていることが明確になります。

于 2013-07-10T21:39:36.423 に答える
1

のディスパッチ アクションはsend、エージェントの現在の状態の関数です。これを明示的にすると、逆参照を自分で試みたくなるのを防ぐのに役立つ場合があります。

(defn weird-behavior []
  (let [my-agent (agent {:entries nil})]
    (doseq [entry [1 2 3]]
      (send my-agent 
            (fn [state] 
              (merge-with conj state {:entries entry}))))
      (await my-agent)
      (println my-agent)))
于 2013-07-10T21:34:33.307 に答える