对于更新世界" state"的函数,我想返回发生的事件串的向量

时间:2016-05-14 10:19:33

标签: clojure

我有这个小游戏世界状态,如下所示:

(defn odds [percentage]
  (< (rand-int 100) percentage))

(defn world []
  {:entities []})

(defn make-bird []
  {:pos [(rand-int 100) (rand-int 100)]
   :age 0
   :dir (vec/dir (rand (. Math PI)))})

(defn generate-entities [entities]
  (if (odds 10)
    (conj entities (make-bird))
    entities))

(defn update-entity [entity]
  (-> entity
      (update :pos (partial vec/add (:dir entity)))
      (update :age inc)))

(defn update-entities [entities]
  (vec (map update-entity entities)))

(defn old? [{age :age}]
  (> age 10))

(defn prune-entities [entities]
  (vec (filter #(not (old? %)) entities)))

(defn update-world [world]
  (-> world
      (update :entities generate-entities)
      (update :entities update-entities)
      (update :entities prune-entities)))

所以update-world经历了三个步骤。首先,有一个1/10的机会产生一个新的鸟类实体,它随机飞行。然后它更新所有鸟类,更新它们的位置并递增它们的age。然后它修剪所有老鸟。 我使用相同的技术生成粒子系统。你可以做一些有趣的事情,比如(iterate update-world (world))来得到一个懒惰的世界状态列表,你可以用你想要的任何帧速率来消费。

然而,我现在拥有一个拥有自主实体的游戏世界,它们四处游荡并做一些事情,有点像鸟类。但我希望得到评估update-world时发生的事情的文字表示。例如,update-world理想情况下会返回新世界状态的元组和字符串向量 - ["A bird was born at [12, 8].", "A bird died of old age at [1, 2]."]

但是我真的不能再使用(iterate update-world (world))了。我真的不知道该怎么做。 这是您使用with-out-string吗?

3 个答案:

答案 0 :(得分:2)

如果你想在你的情况下只增强你的顶级函数(update-world),你可以创建一个可以在iterate中使用的包装函数。一个简单的例子:

(defn increment [n]
  (inc n))

(defn logging-increment [[_ n]]
  (let [new-n (increment n)]
    [(format "Old: %s New: %s" n new-n) new-n]))

(take 3 (iterate logging-increment [nil 0]))
;; => ([nil 0] ["Old: 0 New: 1" 1] ["Old: 1 New: 2" 2])

如果您想在多个级别收集数据时这样做并且您不想修改现有函数的签名(例如,您只想将它​​用于调试),那么使用动态范围似乎一个合理的选择。

或者,您可以考虑使用某些跟踪工具,例如clojure/tools.trace。您只需将defn更改为deftrace或使用trace-nstrace-vars即可打开和关闭功能调用记录。

答案 1 :(得分:1)

使用with-out-str

有两个潜在问题
  • 它返回一个字符串,而不是一个向量。如果您需要使用向量,则需要使用其他内容。
  • 仅返回字符串。如果您使用with-out-str来包装副作用(例如swap!),则可能没问题。

出于调试目的,我通常只使用println。如果要控制输出的位置,可以使用with-out。如果需要,您甚至可以实现一个自定义流,将输出收集到字符串向量中。您可以使用动态绑定向量获得类​​似的结果,您可以累积(通过set!)输出字符串(或将向量包装在原子中并使用swap!)。

如果累积的矢量是计算本身的一部分,并且您希望保持纯净,则可以考虑使用monad

答案 2 :(得分:1)

使用clojure.data/diff生成更改的字符串表示怎么样?你可以这样做:

(defn update-world [[world mutations]]
  (let [new-world (-> world
                      (update :entities generate-entities)
                      (update :entities update-entities)
                      (update :entities prune-entities))]
    [new-world (mutations (clojure.data/diff world new-world))]))

然后你可以做一些像(iterate update-world [(world) []])这样的事情让球滚动。