实现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:
最初的问题是走AST(抽象语法树)并生成新的AST以及更新符号表,因此在提出上述问题的解决方案时请牢记这一点。
我不担心炸毁堆栈,所以用loop + recur替换不是 我在这里担心。
使用全局Vars或Refs而不是将状态作为参数传递给明确的禁止?
答案 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
标准功能