2016-01-07 14 views
3

Clojure新手在这里,我正在通过优秀的“Clojure从头开始”的帖子,并尝试了最后一次练习in this postAlter vs Clojure通勤:我做错了什么?

当我将alter替换为commute时,总和不准确,但我不明白为什么

(def work (ref (apply list (range 1e5)))) 
(def sum (ref 0)) 

(defn trans-alter [work sum] 
    (dosync 
    (if-let [n (first @work)] 
    (do 
     (alter work rest) 
     (alter sum + n) 
     (count @work)) 
    0))) 

(defn trans-commute [work sum] 
    (dosync 
    (if-let [n (first @work)] 
    (do 
     (commute work rest) 
     (commute sum + n) 
     (count @work)) 
    0))) 

(我已经跳过,设置了期货,并呼吁他们等的代码)

随着trans-alter在这里我得到了4999950000的总和(这是正确的预期值),而与trans-commute我每次获得不同的值,但高于预期值(例如4999998211)。

我在这里错过了什么?提前致谢!

+0

提示:不要在交易中添加一笔金额,而是尝试将其结合到一个列表中,并查看实际得到的数字。 – Alex

+0

谢谢,我试过了,发现有重复项,同一个号码被添加了两次。 – agam

回答

4

通勤和改变本质上是做同样的事情,虽然通勤对保证正确性有点宽松。

Alter指示STM始终确保此代码一直运行,无需使用任何从它下面更改的引用。

通勤是一个指令,帮助STM决定什么时候需要中止交易,因为基础数据是从其下面更换的。

如果交易中的所有内容都是可交换的,那么即使部分数据发生更改也可以让该交易完成。在你的情况下,两个交易可能都:

  1. 抢第一个数字
  2. 移除工作
  3. 相同数量增加相同数量导致
  4. 然后使用commute指示STM,这是OK ,它应该继续前进,并承诺交易...
  5. 得到了错误的答案。

所以简而言之,你要求瓶坯的工作实际上不是一个交换操作。具体从列表中删除项目不可交换。如果你将任何通勤换成了改装,那么第4步就会将其中的一个踢出去,只允许其中一个完成。被踢出的那个会重新运行新的数据,最终会得到正确的结果。

+0

我明白了,谢谢你的好评! – agam