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

Ste*_* B. 3 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)
 )
Run Code Online (Sandbox Code Playgroud)

如果我用地图运行这个我得到

execute
keys=  (a c)
execute
keys=  (a c)
...
Run Code Online (Sandbox Code Playgroud)

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

(map #(println %) '(1 2 3))
Run Code Online (Sandbox Code Playgroud)

它工作正常吗?

第二个问题 - 如果我使用doseq而不是map来运行它,我可以遇到执行代理停止的条件(我会在这里添加,但是很难隔离/重新创建).显然,我可能会遗漏一些可能与锁定地图键集有关的东西?我甚至能够将共享地图移出原子.我在clojure地图上是否有默认的同步化?

noi*_*ith 8

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

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

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

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

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