1

我一直在用 Clojure 重写 Land Of Lisp 的兽人战斗游戏。在此过程中,我使用了更实用的样式。我想出了两种方法来编写更高级别的游戏循环的一部分。一个涉及循环/递归,另一个使用doseq 和atoms。下面是两个函数:

(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))

我更喜欢第二种方法,因为代码更简洁,更容易理解。第一种方法更好有什么理由吗?还是我错过了另一种方法来做到这一点?

4

1 回答 1

8

这是等价的吗?如果是这样,我更喜欢它-它比您的解决方案更紧凑,更清晰(恕我直言!),并且功能强大

(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,因为它们有助于为其他人记录流程)。

于 2012-04-17T02:56:32.830 回答