在Clojure中使用循环多次应用assoc

时间:2016-06-19 17:09:56

标签: clojure

Clojure中有没有使用recur的快速方法,可以连续多次将一个关联应用于一个元素?

就是这样:

(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的使用。实际代码在结构上的结构中非常深。

是否有可能再次实现这种效果,而不使用复发?

修改

这是一段使用循环和重复的代码,做我想要实现的目标。

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

"终端"在doc评论中指的是我正在研究的应该模拟终端的库,可以用于应用程序开发。

4 个答案:

答案 0 :(得分:3)

您可以使用reduce

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

答案 1 :(得分:1)

如果您需要值为<asp:ListView ID="ListView1" DataSourceID="SqlDataSource1" runat="server" />的5个实例的序列,请使用repeat

32

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

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

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

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

如果您愿意,可以将其打包成一个函数:

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

答案 2 :(得分:1)

除了使用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]) < / p>

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

在repl中:

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]),即{ {1}}:

doseq

与第一个变体完全相同。

答案 3 :(得分:0)

重新定义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]