【问题标题】:Clojure, Agent, lack side-effectsClojure,代理,没有副作用
【发布时间】:2011-10-02 18:01:25
【问题描述】:

我正在使用代理来操纵结构,但我没有所有的副作用。

所有消息都已发送(我已经打印并计算了它们),但有时我没有所有的副作用。好像不是我的所有功能都应用于代理的状态,或者如果最后一次发送应用于先前的状态..

我尝试了 doall、dorun 但没有找到解决方案,感谢任何帮助。

;; aux function for adding an element to a hashmap 
(defn extend-regs [reg s o]
  (let [os (get reg s)]
    (if (nil? os)
      (assoc reg s [o])
      (assoc reg s (conj os o)))))

;; the agent's altering function - adding an element to the :regs field(a hashmap)
(defn add-reg! [d s o]
  (send d (fn [a] (assoc a :regs (extend-regs (:regs a) s o)))))



;; Creating the agents, dct/init returns an agent
;; pds: data for fields
(defn pdcts->init-dcts! [pds]
  (doall (map dct/init (map :nam pds) (repeat nil))))

;; Altering one agent's state,  dct/add-reg  sends an assoc message to the agent
;; d: agent,  pd: data for fields
(defn dct->add-regs! [d pd]
  (dorun (map (fn [s r] (dct/add-reg! d s r))
          (:syms pd)
          (:regs pd)))
  d)

;; Going through all agents
;; ds: agents,  pds: datas
(defn dcts->add-regs! [ds pds]
  (dorun (map (fn [d pd] (dct->add-regs! d pd))
          ds
          pds))
  ds)

编辑:============================================= ========

好吧,事实证明我只是没有等待足够的时间让我的线程完成他们的任务。现在的问题是如何监控我的代理。我怎么知道队列中有未完成的线程?我只找到了 swank.core/active-threads 和类似的,但它们不是解决方案。

【问题讨论】:

  • 我没有测试你的代码,但是当你的函数被代理执行时你可能会遇到异常。之后,代理将不接受任何新命令并保持其状态。您需要在代理上调用 clojure.core/agent-error 以返回异常,然后在异常上调用 clojure.repl/pst 以获取堆栈跟踪。
  • 感谢您的回复!我在失败的情况下尝试了代理错误,但我得到了 nils,所以没有例外。无论如何谢谢!

标签: clojure agent


【解决方案1】:

使用awaitawait-for 等待代理完成其当前工作队列:

(await agent1 agent2 agent3)

(apply await list-of-agents)

add-reg 的小改进:

(defn extend-regs [reg s o]
  (update-in reg [s] conj o))

这是因为

(conj nil :b)               ; => [:b] 

因此

(update-in {} [:a] conj :b) ; => {:a [:b]}

终于有了。

(defn add-reg! [d s o]
  (send d update-in s [:regs] conj o)

【讨论】:

    【解决方案2】:

    我没有解决您的问题的方法,但我忍不住建议对前两个功能进行一些改进:

    (defn extend-regs [reg s o]
      (let [os (get reg s)]
        (if (nil? os)
          (assoc reg s [o])
          (assoc reg s (conj os o)))))
    ;; => place the 'if inside the assoc:
    (defn extend-regs [reg s o]
      (let [os (get reg s)]
        (assoc reg s (if (nil? os) [o] (conj os o)))))
    ;; => this (if (nil? x) ...) is the pattern of function fnil, so ...
    (defn extend-regs [reg s o]
      (let [os (get reg s)]
        (assoc reg s ((fnil conj []) os o))))
    ;; with update-in, this will be even clearer, and we can remove the let altogether:
    (defn extend-regs [reg s o]
      (update-in reg [s] (fnil conj []) o))
    

    至于第二个:

    (defn add-reg! [d s o]
      (send d (fn [a] (assoc a :regs (extend-regs (:regs a) s o)))))
    ;; => We can, again, use update-in instead of assoc:
    (defn add-reg! [d s o]
      (send d (fn [a] (update-in a [:regs] extend-regs s o))))
    ;; or, if you can get rid of extend-regs:
    (defn add-reg! [d s o]
      (send d (fn [a] (update-in a [:regs s] (fnil conj []) o)))
    

    最后,作为风格问题,我将 add-reg 放在一个单独的函数中,并在客户端代码中直接使用发送到代理的习惯用法(或者有一个简化的 add-reg! 函数):

    (defn add-reg [v s o] (update-in v [:regs s] (fnil conj []) o))
    
    (defn add-reg! [d s o] (send d add-reg))
    

    我知道这并不能回答你最初的问题,但写这个逐步重构很有趣

    【讨论】:

    • 是的,我不知道这些,谢谢指出!事实上,我刚刚发现我的状态没有改变,因为不是我所有的线程都终止了,我没有等待足够的时间..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-29
    • 2015-11-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 2018-08-06
    • 2013-06-18
    相关资源
    最近更新 更多