Clojure:使用输出作为下一个操作的输入执行n次操作(a-la reduce)

时间:2014-09-02 21:06:32

标签: clojure functional-programming reduce

我想执行特定操作的给定时间量(例如,生成部落)。每次我想使用前一次迭代的输出作为下一次迭代的输入。

我通过对n个元素的集合进行虚拟减少来实现它,然后我只考虑累加器并从集合中丢弃元素。

(defn generate-game [world n-tribes]
  (let [ game (create-game world)
         game (reduce (fn [acc, _] (:game (generate-tribe acc))) game (repeat n-tribes :just_something))]
    game))

我认为有更好的方法可以做到这一点。你能帮我找到吗?

3 个答案:

答案 0 :(得分:4)

如果相同的功能重复n-tribes次,那么使用iterate并获取n-tribes'元素应该有效:

(defn generate-game [world n-tribes]
  (let [ game (create-game world)
         game (nth (iterate generate-tribe game) n-tribes)]
         game))

请记住,iterate会创建一个懒惰的序列,因此在您请求之前不会计算任何内容。

另一种选择是将comp应用于函数列表,毕竟它是一个简单的函数组合:

 (generate-tribe (generate-tribe .... (generate-tribe game) ....))

                                 n-tribes times

类似于:

(defn generate-game [world n-tribes]
  (let [ game (create-game world)
         game ((apply comp (repeat n-tribes generate-tribe)) game)]
         game))

完全表达了这个想法。

答案 1 :(得分:2)

不是Clojure专家,但我希望

(defn generate-game [world n-tribes]
    (nth (iterate generate-tribe (create-game world)) n-tribes))

或(更简洁)

(defn generate-game [world n-tribes]
  (let [game (create-game world)]
    (nth (iterate generate-tribe game) n-tribes)))

工作

答案 2 :(得分:1)

问题中使用的方法很好,但使用范围

(defn generate-game [world n-tribes]
  (reduce (fn [acc _] (:game (generate-tribe acc)))
          (create-game world)
          (range n-tribes)))

由于范围对象知道如何智能地减少自己,因此这将变成一个循环。这是范围的reduce方法:

public Object reduce(IFn f, Object start) {
    Object ret = f.invoke(start,n);
    for(int x = n+1;x < end;x++)
            ret = f.invoke(ret, x);
    return ret;
}

如果您的游戏状态最终会在原子中结束,那么您也可以考虑dotimes来更新它。

(defn generate-game [world n-tribes]
  (let [game (atom (create-game world))]
    (dotimes [_ n-tribes] (swap! game (comp :game generate-tribe)))
    game))

迭代也是一个很好的解决方案。在Iteratively apply function to its result without generating a seq进行了更多讨论。