我在Clojure中重写了Land of Lisp的兽人战斗游戏。在这个过程中,我使用的是更具功能性的风格。我已经提出了两种方法来编写更高级别的游戏循环。一个涉及循环/复发,另一个使用剂量和原子。以下是两个功能:
(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))
我更喜欢第二种方法,因为代码更简洁,更容易理解。有没有理由说第一种方法更好?或者我错过了另一种方法来做到这一点?
答案 0 :(得分:8)
这相当于?如果是这样,我更喜欢它 - 它比你的解决方案(imho!)更紧凑,更清晰,更实用
(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))
(注意:我将参数顺序更改为monster-round
,以便重复看起来更好)
更一般地说,你不应该在你的“功能”版本中引入n
(如果你有一个索引,它就不是真的有用了......)。索引到序列非常非常需要。如果你曾经尝试过这么做的诱惑,我想你会写上面的例行公事......
但是,在写完之后,我想:“嗯。这只是迭代怪物。为什么我们不能使用标准形式?它不是for循环因为玩家改变。所以它必须是一个折叠(即减少),带有玩家前锋“。然后写起来很容易:
(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之类的标准形式,因为它们有助于为其他人记录过程。)