我有这个小游戏世界状态,如下所示:
(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
吗?
答案 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-ns
或trace-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) []])
这样的事情让球滚动。