Clojure Atom在定义函数中包含时不会更新

Jes*_*son 2 clojure clojurescript

不确定这里发生了什么,但我有这个代码,map函数在我的repl中成功执行而不包含在已定义的函数中:

(def dogs '({:name "scout" :age 5} {:name "rux" :age 3} {:name "fenley" :age 2}))

(def ages (atom {:above-four '() :below-four '()}))

(map
    #(if (> (get-in % [:age]) 4)
         (swap! ages update-in [:above-four] merge %)
         (swap! ages update-in [:below-four] merge %)) dogs)

@ages
=> {:above-four ({:name "scout", :age 5}), :below-four ({:name "fenley", :age 2} {:name "rux", :age 3})}
Run Code Online (Sandbox Code Playgroud)

然而,当我定义map函数时:

(def ages (atom {:above-four '() :below-four '()}))

(def dogs '({:name "scout" :age 5} {:name "rux" :age 3} {:name "fenley" :age 2}))

(defn test-dogs []
    (map
    #(if (> (get-in % [:age]) 4)
         (swap! ages update-in [:above-four] merge %)
         (swap! ages update-in [:below-four] merge %)) dogs)
         @ages)
Run Code Online (Sandbox Code Playgroud)

我得到以下结果:

=> {:above-four (), :below-four ()}
Run Code Online (Sandbox Code Playgroud)

我很困惑,因为直接从Clojure文档中获取的这个函数工作得很好:

(def m1 (atom {:a "A" :b "B"}))

(defn update-m1 []
    (swap! m1 assoc :a "Aaay")
    @m1)

=> {:a "Aaay", :b "B"}
Run Code Online (Sandbox Code Playgroud)

Car*_*ate 5

因为test-dogs使用map,它返回一个懒惰的序列.延迟序列的元素直到需要时才会实现.

您设置的问题是您尝试使用map运行副作用(调用swap!;不纯的操作),并且从不实际使用结果map.因为您从不请求结果map,所以包含swap!从不运行的映射函数.

通过使用mapv(返回非惰性向量)或doseq(这意味着执行副作用):

(doseq [dog dogs]
  (let [k (if (> (:age dog) 4)
                :above-four
                :below-four)]

     (swap! ages update k merge dog))) 
Run Code Online (Sandbox Code Playgroud)

您可以强制运行副作用.

我稍微清理了一下代码.-in您使用的版本是不必要的; 就像打电话一样get-in.我也摆脱了多余的电话swap!.

请注意,至少在您的示例中,使用atoms是完全没必要的.即使您有一个更复杂的用例,请确保它们的使用是合理的.可变变量在Clojure等语言中并不常见.