【问题标题】:Applying assoc several times using a loop in Clojure在 Clojure 中使用循环多次应用 assoc
【发布时间】:2016-06-19 17:09:55
【问题描述】:

在 Clojure 中是否有一种快速的方法,无需使用 recur,即可将一个 assoc 连续多次应用于一个元素?

也就是说,类似于:

(defn set-thing 
  [foo i val] 
  (assoc foo i val))

(let [foo [3 4 2 5 8]]
  (last (for [i (range 0 (count test))] 
    (set-thing foo i 32)))

上面的代码,但是显然不符合我的要求。上面的函数将简单地返回一个向量,其中最后一个值为 32,其余的保持不变。我所追求的是返回一个所有值都设置为 32 的向量。

绝对有必要使用 set 函数,因为我的代码看起来与上面的代码有很大不同:从而阻止使用 map.实际代码在结构上的结构中包裹得很深。

不使用recur是否可以再次达到这个效果?

编辑:

这是一段使用循环和递归的代码,做我想做的事情。

(defn fill!
  "Fills the terminal with one specific tile."
  [{:keys [screen] :as term} tex-x tex-y color]
  (let [grid-width (screen :grid-width)
       grid-height (screen :grid-height)]
    (loop [x 0
           y 0
           term term]
      (if (= x grid-width)
        (if (= y grid-height)
          term
          (recur 0 (inc y) ((term :set-char!) term x y tex-x tex-y color)))
        (recur (inc x) y ((term :set-char!) term x y tex-x tex-y color))))))

文档注释中的“终端”是指我正在研究的一个库,它应该模拟一个终端,可以用于应用程序开发。

【问题讨论】:

  • set 函数的目的是什么?它掩盖了clojure.core/set,似乎只是assoc 的一个不太有用的版本。
  • 我拥有的代码要高级得多,其中 set 函数的名称实际上并未设置。相反,我有一个 set 函数(如果你愿意的话),在瓦片网格中设置一个瓦片(在 x、y、tex-x、tex-y 和颜色参数的帮助下),返回应用程序的当前状态。因此,我要做的是遍历整个 x 和 y 坐标网格,将所有图块设置为相同类型的图块:因此需要一个 set 方法。

标签: clojure


【解决方案1】:

您可以使用reduce

(let [foo [3 4 2 5 8]]
  (reduce (fn [acc k]
            (set acc k 32))
          foo
          (range (count foo))))

【讨论】:

    【解决方案2】:

    除了使用reduce 的建议(完全正确)之外,我还建议您为您的更新提供一个变体(设置网格值)

    假设你有这个术语的定义:

    (def term {:screen {:grid-width 5
                        :grid-height 3}
               :set-char! (fn [term & other]
                            (println :setting other)
                            term)})
    

    首先,您可以将loop/recur 替换为reduce(用于术语更新)和for 列表推导((for [x (range 2) y (range-3)] [x y]) 生成所有坐标对)的组合

    (defn fill!
      [{{:keys [grid-width grid-height]} :screen
        set-char! :set-char!}
       tex-x tex-y color]
      (reduce (fn [term [x y]] (set-char! term x y tex-x tex-y color))
              term
              (for [x (range grid-width)
                    y (range grid-height)]
                [x y])))
    

    在回复中:

    user> (fill! term 101 102 103)
    :setting (0 0 101 102 103)
    :setting (0 1 101 102 103)
    :setting (0 2 101 102 103)
    :setting (1 0 101 102 103)
    :setting (1 1 101 102 103)
    :setting (1 2 101 102 103)
    :setting (2 0 101 102 103)
    :setting (2 1 101 102 103)
    :setting (2 2 101 102 103)
    :setting (3 0 101 102 103)
    :setting (3 1 101 102 103)
    :setting (3 2 101 102 103)
    :setting (4 0 101 102 103)
    :setting (4 1 101 102 103)
    :setting (4 2 101 102 103)
    
    {:screen {:grid-width 5, :grid-height 3}, :set-char! #function[user/fn--19918]}
    

    好的。它有效,但只要你的 set-char !函数仅用于副作用(在我的情况下打印字符串,在您的情况下更新屏幕),你可以不用减少,使用专为这种情况设计的 clojure 函数(do[run|seq|all]),即doseq

    (defn fill2!
      [{{:keys [grid-width grid-height]} :screen set-char! :set-char!}
       tex-x tex-y color]
      (doseq [x (range grid-width) y (range grid-height)]
        (set-char! term x y tex-x tex-y color))
      term)
    

    这将与第一个变体完全相同。

    【讨论】:

      【解决方案3】:

      如果您需要值 32 的 5 个实例的序列,请使用 repeat

      (repeat 5 32)
      ;=> (32 32 32 32 32)
      

      如果您需要将结果作为向量,请使用vec

      (vec (repeat 5 32))
      ;=> [32 32 32 32 32]
      

      如果您需要结果与现有集合大小相同,请使用count

      (vec (repeat (count [3 4 2 5 8]) 32))
      ;=> [32 32 32 32 32]
      

      如果你愿意,你可以把它打包成一个函数:

      (defn fill [v x]
        (vec (repeat (count v) x)))
      
      (fill [3 4 2 5 8] 32)
      ;=> [32 32 32 32 32]
      

      【讨论】:

        【解决方案4】:

        重新定义 Clojure 的核心 set 可能不是一个好主意。这里似乎也没有必要,所以我只是在下面的示例中直接使用了assoc

        您可以使用constantly 来运行您的函数所需的次数。它返回一个列表,因此对vec 的额外调用只是为了获取您想要的数据类型:

        user.core=> (let [foo [3 4 2 5 8]]
                 #_=>   (vec (map (constantly 32) foo)))
        [32 32 32 32 32]
        

        这是@SerCe 的答案的一个变体,它使用了一个阅读器宏,我觉得它更简洁一些。

        user.core=> (let [foo [3 4 2 5 8]]
                 #_=>   (reduce #(assoc %1 %2 32) foo (range (count foo))))
        [32 32 32 32 32]
        

        【讨论】:

          猜你喜欢
          • 2014-09-22
          • 2022-12-18
          • 1970-01-01
          • 1970-01-01
          • 2015-08-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-06
          相关资源
          最近更新 更多