在尝试复制一些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地图上有默认的同步化?
答案 0 :(得分:8)
map
很懒。这意味着它不会计算任何结果,直到从它重新运行的数据结构中访问结果。这意味着如果不使用它的结果,它将不会运行任何东西。
当你从repl使用map
时,repl的打印阶段访问数据,这会导致调用映射函数中的任何副作用。在函数内部,如果不调查返回值,则不会发生映射函数中的任何副作用。
您可以使用doall
强制完全评估延迟序列。如果您不需要结果值但想要确保调用所有副作用,则可以使用dorun
。你也可以使用不是懒惰的mapv
(因为向量永远不会延迟),并为你提供一个关联数据结构,这通常是有用的(更好的随机访问性能,优化附加而不是预先添加)。
编辑:关于你问题的第二部分(从评论中移到这里)。
不,doseq
没有任何内容会挂起您的执行,请尝试检查代理的agent-error状态以查看是否存在异常,因为代理停止执行并停止接受新任务如果他们遇到错误情况则默认。您还可以使用set-error-model和set-error-handler!来自定义代理的错误处理行为。