Clojure:具有可更新状态的map函数

时间:2015-02-20 16:55:26

标签: clojure

实现map函数的最佳方法是什么,以及函数应用程序与序列中每个元素之间的可更新状态?为了说明这个问题,我们假设存在以下问题:

我有一个数字向量。我想要一个新的序列,其中每个元素乘以2,然后在序列中添加10到10的数量,直到并包括当前元素。例如来自:

[20 30 40 10 20 10 30]

我想生成:

[40 60 80 21 41 22 62]

不添加10的计数,可以使用高级抽象来制定解决方案:

(map #(* 2 %) [20 30 40 10 20 10 30])

有计数更新迫使我去"去基本"我想出的解决方案是:

(defn my-update-state [x state]
  (if (= x 10) (+ state 1) state)
  )

(defn my-combine-with-state [x state]
  (+ x state))

(defn map-and-update-state [vec fun state update-state combine-with-state]
  (when-not (empty? vec)
    (let [[f & other] vec
          g (fun f)
          new-state (update-state f state)]
      (cons (combine-with-state g new-state) (map-and-update-state other fun new-state update-state combine-with-state))
      )))


(map-and-update-state [20 30 40 50 10 20 10 30 ] #(* 2 %) 0 my-update-state my-combine-with-state )

我的问题:这是解决问题的适当/规范方式,还是忽略了一些重要的概念/功能。

PS:

  1. 最初的问题是走AST(抽象语法树)并生成新的AST以及更新符号表,因此在提出上述问题的解决方案时请牢记这一点。

  2. 我不担心炸毁堆栈,所以用loop + recur替换不是 我在这里担心。

  3. 使用全局Vars或Refs而不是将状态作为参数传递给明确的禁止?

4 个答案:

答案 0 :(得分:3)

您可以使用reduce累积到目前为止看到的10对数和当前结果向量。:

(defn map-update [v]
  (letfn [(update [[ntens v] i]
             (let [ntens (+ ntens (if (= 10 i) 1 0))]
               [ntens (conj v (+ ntens (* 2 i)))]))]
    (second (reduce update [0 []] v))))

答案 1 :(得分:3)

要计算10个你可以做的数量

(defn count-10[col]
  (reductions + (map #(if (= % 10) 1 0) col)))

示例:

user=> (count-10 [1 2 10 20 30 10 1])
(0 0 1 1 1 2 2)

然后是最终结果的简单地图

(map + col  col (count-10 col)))

答案 2 :(得分:0)

减少和减少是遍历保持状态的序列的好方法。如果您觉得您的代码不清楚,您可以随时使用loop / recur或lazy-seq这样的递归

(defn twice-plus-ntens
  ([coll] (twice-plus-ntens coll 0))
  ([coll ntens]
    (lazy-seq
      (when-let [s (seq coll)]
        (let [item (first s)
              new-ntens (if (= 10 item) (inc ntens) ntens)]
          (cons (+ (* 2 item) new-ntens) 
                (twice-plus-ntens (rest s) new-ntens)))))))

查看在您的repl

评估此内容的地图源代码
(source map)

我已经跳过了分块优化和多重收集支持。

你可以通过这种方式使它成为更高阶的函数

(defn map-update
  ([mf uf coll] (map-update mf uf (uf) coll))
  ([mf uf val coll]
    (lazy-seq
      (when-let [s (seq coll)]
        (let [item (first s)
              new-status (uf item val)]
          (cons (mf item new-status) 
                (map-update mf uf new-status (rest s))))))))

(defn update-f
  ([] 0)
  ([item status]
    (if (= item 10) (inc status) status)))

(defn map-f [item status]
  (+ (* 2 item) status))

(map-update map-f update-f in)

答案 3 :(得分:0)

最合适的方法是使用状态函数

(into
  []
  (map
    (let [mem (atom 0)]
      (fn [val]
        (when (== val 10) (swap! mem inc))
        (+ @mem (* val 2)))))
  [20 30 40 10 20 10 30])

另见

memoize

标准功能