在clojure中如何实现带有嵌套返回的循环?



我在这里玩了一个巧妙的教程:

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)或类似于管道的下一阶段;如果它意味着执行副作用,则添加dorundoall作为最后阶段,以迫使它们立即发生(根据返回值是否有趣选择一个或另一个)。

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

最新更新