简体   繁体   English

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

[英]Clojure: Executing an operation n times using the output as input of next operation (a-la reduce)

I want to execute a given amount of time a certain operation (eg, generating a tribe). 我想执行给定时间的某个操作(例如,生成部落)。 Each time I would like to use the output of the previous iteration as input of the next one. 每次我想将上一次迭代的输出用作下一次迭代的输入时。

I am achieving it by doing a dummy reduce on collection of n elements, I then consider only the accumulator and throw away the element from the collection. 我通过对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))

I assume there is a much better way to do that. 我认为有一个更好的方法来做到这一点。 Can you help me finding it? 你能帮我找到它吗?

If it's the same function repeated n-tribes times, then using iterate and getting the n-tribes 'th element should work: 如果它是重复n-tribes时间的相同函数,那么使用迭代并获得n-tribes的元素应该工作:

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

Remember that iterate creates a lazy sequence so nothing will be calculated until you request it. 请记住, iterate创建一个惰性序列,因此在您请求之前不会计算任何东西。

Another option is applying comp to the list of functions, after all it's a simple function composition: 另一种选择是将comp应用于函数列表,毕竟它是一个简单的函数组合:

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

                                 n-tribes times

So something like: 所以类似于:

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

expresses exactly that idea. 表达了这个想法。

Not a Clojure expert, but I would expect 不是Clojure专家,但我希望如此

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

or (less terse) 或者(简洁)

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

to work 上班

The method used in the question is fine, but use range instead 问题中使用的方法很好,但是使用范围代替

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

Since range objects know how to intelligently reduce themselves, this will get turned into a loop. 由于范围对象知道如何智能地还原自身,因此这将变成一个循环。 This is the reduce method of range: 这是范围的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;
}

If your game state is going to wind up in an atom anyway then you could also consider dotimes to update it. 如果你的游戏状态无论如何都会在原子中结束,那么你也可以考虑使用dotimes来更新它。

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

Iterate is also a fine solution. 迭代也是一个很好的解决方案。 More discussion at Iteratively apply function to its result without generating a seq . 不产生seq的情况下,将函数迭代地应用于结果的更多讨论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM