HackerRank比较三元组Clojure实现:atom引用inc值无法保留的问题咨询
理解Clojure中Atom无法保留inc值的问题
嘿,我来帮你拆解这个Atom的问题!先直接说核心原因,再给你解决方案,最后解释调试时的迷惑点~
为什么Atom没保留inc后的数值?
Clojure里的Atom是可变状态容器,但它的修改不是直接对@atom(deref出来的值)操作就行的。你可能写了类似(inc @alice-score)的代码——这行代码确实会计算出当前值+1的结果,但这个结果只是一个临时值,并没有写回Atom本身!Atom的状态只有通过swap!或者reset!这两个函数才能更新,直接操作deref出来的值不会改变Atom内部的状态。
问题1:怎么修正这个问题?
把直接的inc @atom替换成swap! atom inc就可以了。swap!会帮你做三件事:
- 获取Atom当前的状态
- 把你传入的函数(这里是
inc)应用到这个状态上,得到新值 - 把新值写回Atom,保证线程安全
结合「比较三元组」的场景,完整的修正代码大概是这样:
(defn compare-triplets [a b] (let [alice-score (atom 0) bob-score (atom 0)] ;; 遍历每一对元素进行比较 (doseq [[a-val b-val] (map vector a b)] (cond (> a-val b-val) (swap! alice-score inc) ;; 正确更新Alice的分数 (< a-val b-val) (swap! bob-score inc) ;; 正确更新Bob的分数 :else nil)) ;; 返回最终分数 [@alice-score @bob-score]))
测试一下,比如输入(compare-triplets [5 6 7] [3 6 10]),应该返回[1 1],这就符合题目要求了。
问题2:为什么调试模式下能得到正确数值?
这是个很容易踩的坑!调试时你大概率是查看了(inc @alice-score)这个表达式的返回值——这个返回值确实是inc后的正确数值,但Atom本身的状态根本没被修改。举个REPL里的例子:
user=> (def alice (atom 0)) #'user/alice user=> (inc @alice) ;; 调试时看到这个返回值是1 1 user=> @alice ;; 但Atom内部还是0 0
调试工具可能只展示了表达式的结果,没让你看到Atom的实际状态,所以你误以为数值被保留了,但实际上只是临时计算出了正确值而已。
额外提一句:更地道的Clojure写法
虽然你说知道用Atom不是最优解,但还是提一句——Clojure更推荐纯函数式的写法,用reduce来累加分数,完全不需要可变状态:
(defn compare-triplets [a b] (reduce (fn [[a-score b-score] [a-val b-val]] (cond (> a-val b-val) [(inc a-score) b-score] (< a-val b-val) [a-score (inc b-score)] :else [a-score b-score])) [0 0] (map vector a b)))
这种写法没有副作用,更符合Clojure的设计哲学,但如果你只是想搞懂Atom的问题,前面的修正方案就足够啦~
内容的提问来源于stack exchange,提问作者perkss
相关产品推荐
相关产品推荐

