【问题标题】:Clojure: Executing an operation n times using the output as input of next operation (a-la reduce)Clojure:使用输出作为下一个操作的输入执行操作 n 次(a-la reduce)
【发布时间】:2014-10-27 05:59:47
【问题描述】:

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

我通过对 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))

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

【问题讨论】:

  • 迭代是我一直在寻找的。最终的解决方案略有不同,因为 generate-tribe 返回一个包含游戏的地图(它还包含生成部落)。对我来说,最终的解决方案是:``` (defn generate-game [world n-tribes] (let [ game (create-game world) game (nth (iterate #(:game (generate-tribe %)) game) n -tribes :just_something)] 游戏)) ```

标签: clojure functional-programming reduce


【解决方案1】:

如果相同的函数重复了n-tribes 次,那么使用iterate 并获取n-tribes'th 元素应该可以工作:

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

正是表达了这个想法。

【讨论】:

  • 最好使用->> in let game (->> (create-game world) (...))
  • @edbond 如果我错了,请纠正我,但如果在读取时不知道要通过的函数数量,则线程宏将不起作用。在已知它是最干净的解决方案的情况下。
  • 只是一个语法糖。将编译时间扩展到相同的形式。
  • @soulcheck 在你的两个 let 块中,重复次数是一个数字参数,线程宏和 let 宏一样容易使用。您可以从一个语句串连到下一个语句,而不是重复绑定相同的值。
  • @noisesmith 啊,好吧,你的意思是在let 内的表单之间进行线程化,而不是线程化多个generate-tribe 调用。是的,这会起作用,但这只是语法偏好。我没有改变 OP 使用 let 表单的方式,因为这不是这里真正的问题。
【解决方案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)))

上班

【讨论】:

    【解决方案3】:

    问题中使用的方法没问题,但改用范围

    (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 进行更多讨论。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-13
      • 2016-03-24
      • 1970-01-01
      • 2017-05-10
      • 2013-12-08
      • 2022-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多