我在这里玩了一个巧妙的教程:
http://buildnewgames.com/introduction-to-crafty/和我想知道如何在clojurescript/clojure中实现这个特定的函数
var max_villages = 5;
for (var x = 0; x < Game.map_grid.width; x++) {
for (var y = 0; y < Game.map_grid.height; y++) {
if (Math.random() < 0.02) {
Crafty.e('Village').at(x, y);
if (Crafty('Village').length >= max_villages) {
return;
}
}
}
}
我知道我们可以有(for [])
结构,但你如何让它停止当max_villages达到5?
这里有一个方法:
(def max-villages 5)
(->> (for [x (range map-width)
y (range map-height)]
[x y])
(filter (fn [_] (< (rand) 0.02)))
(take max-villages))
然后可能添加(map make-village-at)
或类似于管道的下一阶段;如果它意味着执行副作用,则添加dorun
或doall
作为最后阶段,以迫使它们立即发生(根据返回值是否有趣选择一个或另一个)。
NB。由于序列分块可能会产生一些额外的向量和随机数,但它将小于32。
更命令式的方法,使用计数器进行比较:
(let [counter (atom 0)]
(doseq [x (range map-width)
:while (< @counter max-villages)
y (range map-height)
:while (< @counter max-villages)
:when (< (rand) 0.02)]
(swap! counter inc)
(prn [x y]))) ; call make-village-at here
当测试表达式失败时, :while
在当前嵌套层终止循环;:when
立即进入下一个迭代。doseq
也支持分块,但是:while
会阻止它执行不必要的工作。
使用递归,它将是这样的:
(letfn [(op [x y]
(if (= (rand) 0.02)
(do
(village-at x y)
(if (>= (village-length) max-villages) true))))]
(loop [x 0 y 0]
(when (and (< x width) (not (op x y)))
(if (= (inc y) height)
(recur (inc x) 0)
(recur x (inc y))))))
这是一个很棒的教程!
Michael方法的一个变体(我本想评论他的答案,但我还没有足够的堆栈能力)是使用笛卡尔积而不是嵌套的for循环:
;; some stub stuff to get the example to run
(ns example.core
(:use clojure.math.combinatorics))
(def max-villages 5)
(def map-width 10)
(def map-height 10)
(defn crafty-e [x y z] (print z))
;; the example, note I use doseq rather than map to empasize the fact that the loop
;; is being performed for its side effects not its return value.
(doseq [coord (take max-villages
(filter
(fn [_] (< (rand) 0.02))
(cartesian-product (range map-width) (range map-height))))]
(crafty-e :village :at coord))