Clojure混淆 - 多进程环境中map,doseq的行为

时间:2014-01-22 16:43:10

标签: clojure

在尝试复制一些websockets示例时,我遇到了一些我不理解的行为,似乎无法找到文档。简化,这是我在lein中运行的一个例子,它应该每秒为共享映射中的每个元素运行一次函数:

(def clients (atom {"a" "b" "c" "d" }))

(def ticker-agent (agent nil))

(defn execute [a]
        (println "execute")
        (let [ keys (keys @clients) ]   
            (println "keys= " keys )
            (doseq [ x keys ] (println x)))
            ;(map   (fn [k]  (println k)) keys)) ;; replace doseq with this?

        (Thread/sleep  1000)
        (send *agent* execute))


(defn -main [& args]
    (send ticker-agent execute)
 )

如果我用地图运行

execute
keys=  (a c)
execute
keys=  (a c)
...

第一个令人困惑的问题:我明白我可能会错误地使用地图,因为没有返回值,但这是否意味着内部println被优化了?特别是如果我在repl中运行它:

(map #(println %) '(1 2 3))

它运作正常吗?

第二个问题 - 如果我使用 doseq 而不是 map 运行此操作,我可能会遇到执行代理停止的情况(我会在此附加,但我有难以隔离/重建)。显然,我可能遗漏了一些可能与锁定地图键集有关的东西?我甚至可以将共享地图移出原子。是否在clojure地图上有默认的同步化?

1 个答案:

答案 0 :(得分:8)

map很懒。这意味着它不会计算任何结果,直到从它重新运行的数据结构中访问结果。这意味着如果不使用它的结果,它将不会运行任何东西。

当你从repl使用map时,repl的打印阶段访问数据,这会导致调用映射函数中的任何副作用。在函数内部,如果不调查返回值,则不会发生映射函数中的任何副作用。

您可以使用doall强制完全评估延迟序列。如果您不需要结果值但想要确保调用所有副作用,则可以使用dorun。你也可以使用不是懒惰的mapv(因为向量永远不会延迟),并为你提供一个关联数据结构,这通常是有用的(更好的随机访问性能,优化附加而不是预先添加)。

编辑:关于你问题的第二部分(从评论中移到这里)。

不,doseq没有任何内容会挂起您的执行,请尝试检查代理的agent-error状态以查看是否存在异常,因为代理停止执行并停止接受新任务如果他们遇到错误情况则默认。您还可以使用set-error-modelset-error-handler!来自定义代理的错误处理行为。