Using doseq and atoms rather than loop/recur
我一直在用 Clojure 重写 Land Of Lisp 的兽人战斗游戏。在此过程中,我使用了更实用的样式。我想出了两种方法来编写更高级别的游戏循环的一部分。一个涉及循环/递归,另一个使用doseq 和atoms。下面是两个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | (defn monster-round [player monsters] (loop [n 0 p player] (if (>= n (count monsters)) p (recur (inc n) (if (monster-dead? (nth monsters n)) p (let [r (monster-attack (nth monsters n) p)] (print (:attack r)) (:player r))))))) (defn monster-round-2 [player monsters] (let [p (atom player)] (doseq [m monsters] (if (not (monster-dead? m)) (let [r (monster-attack m @p)] (print (:attack r)) (reset! p (:player r))))) @p)) |
我更喜欢第二种方法,因为代码更简洁,更容易理解。第一种方法更好有什么理由吗?还是我错过了另一种方法来做到这一点?
这是等价的吗?如果是这样,我更喜欢它 - 它比您的解决方案更紧凑、更清晰(恕我直言!)和功能
1 2 3 4 5 6 7 8 9 | (defn monster-round [monsters player] (if-let [[monster & monsters] monsters] (recur monsters (if (monster-dead? monster) player (let [r (monster-attack monster player)] (print (:attack r)) (:player r)))) player)) |
(注意:我将参数顺序更改为
更一般地说,你不应该在你的"功能"版本中引入
但是,在写完之后,我想:"嗯。这只是对怪物的迭代。为什么我们不能使用标准形式?它不是 for 循环,因为玩家会改变。所以它必须是折叠(即减少),它带着玩家前进"。然后很容易写:
1 2 3 4 5 6 7 8 9 | (defn- fight [player monster] (if (monster-dead? monster) player (let [r (monster-attack monster player)] (print (:attack r)) (:player r)))) (defn monster-round [player monsters] (reduce fight player monsters)) |
哪个(如果它符合您的要求)是正确答案(tm)。
(也许我没有回答这个问题?我认为你错过了更好的方法,如上所述。一般来说,你应该能够围绕数据结构进行计算,这通常不需要突变;通常你可以 -并且应该 - 使用标准格式,如 map 和 reduce,因为它们有助于为其他人记录流程)。