我在这里玩一个狡猾的教程:
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时你会如何让它停止?
答案 0 :(得分: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。由于seq分块可能会生成一些额外的向量和随机数,但它会小于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
会阻止它执行不必要的工作。
答案 1 :(得分:2)
使用递归就像是:
(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))))))
答案 2 :(得分:0)
这是一个很棒的教程!
迈克尔方法的一个变种(我刚刚回答他的答案,但我还没有足够的叠加能力)将是使用笛卡尔积而不是嵌套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))