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

zca*_*ate 3 clojure clojurescript

我在这里玩一个狡猾的教程:

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;
       }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道我们可以拥有(for [])构造但是当max_villages达到5时你会如何让它停下来?

Mic*_*zyk 5

这是一种方法:

(def max-villages 5)

(->> (for [x (range map-width)
           y (range map-height)]
       [x y])
     (filter (fn [_] (< (rand) 0.02)))
     (take max-villages))
Run Code Online (Sandbox Code Playgroud)

然后可能添加(map make-village-at)或类似管道的下一个阶段; 如果它意味着执行副作用,添加一个dorundoall作为最后阶段强制它们立即发生(根据返回值是否有趣选择一个或另一个).

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
Run Code Online (Sandbox Code Playgroud)

:while当测试表达式失败时,在当前嵌套级别终止循环; :when立即进入下一次迭代.doseq也支持分块,但:while会阻止它执行不必要的工作.

  • 使用`@ counter`的版本可以工作,但不是出于你想要的原因:`:while`只会"跳过"当前的`x`绑定并转到下一个,这意味着`counter`需要是每个`x`测试一次,即使它再也不会改变.对于小的`map-width`来说很好,但是对于大(或无限)宽度来说,这是一个严重的问题. (2认同)