Clojure 仅更新coll中与pred匹配的第一个元素的惯用方法
我有一个序列,Clojure 仅更新coll中与pred匹配的第一个元素的惯用方法,clojure,Clojure,我有一个序列,(def coll'([:a20][:b30][:c50][:d90]) 我想遍历seq,只修改与谓词匹配的第一个元素 谓词(def pred(fn[[ab]](>b30)) (fpred(fn[[ab]][a(+b2)]coll)=>([:a20][:b30][:c52][:d90]) f是我想要的fn,它取一个pred,一个fn应用于匹配pred的第一个元素。所有其余元素均未修改并返回到seq中 做上述工作的惯用方法是什么?与大多数问题一样,有许多方法可以解决。这只是其中之一
(def coll'([:a20][:b30][:c50][:d90])
我想遍历seq,只修改与谓词匹配的第一个元素
谓词(def pred(fn[[ab]](>b30))
(fpred(fn[[ab]][a(+b2)]coll)=>([:a20][:b30][:c52][:d90])
f是我想要的fn,它取一个pred,一个fn应用于匹配pred的第一个元素。所有其余元素均未修改并返回到seq中
做上述工作的惯用方法是什么?与大多数问题一样,有许多方法可以解决。这只是其中之一 如果您正在运行Clojure 1.5,请尝试一下:
(reduce
(fn [acc [a b]]
(if (pred b)
(reduced (concat (:res acc) [[a (+ b 2)]] (rest (:coll acc))))
(assoc acc
:res (conj (:res acc) [a b])
:coll (rest (:coll acc)))))
{:coll coll :res []}
coll)
;; ([:a 20] [:b 30] [:c 52] [:d 90])
该算法的关键是使用reduced
(注意“d”)函数-它本质上告诉reduce
停止迭代并返回结果。从其文档字符串:
-------------------------
clojure.core/reduced
([x])
Wraps x in a way such that a reduce will terminate with the value x
代码有点简洁,但它应该给你基本的想法
希望这有帮助。一种可能的方法是使用
拆分来拆分集合,将函数f
应用于拆分返回的第二个集合的第一个元素,然后将这些元素再次合并
(defn apply-to-first [pred f coll]
(let [[h t] (split-with (complement pred) coll)]
(concat h (list (f (first t))) (rest t))))
请注意,示例中的pred
函数可能如下所示:
(def pred #(> (second %) 30))
为了保持简单:找到第一个元素,找到它的索引,并使用assoc“更新”索引处的元素:
(let [e (first (filter pred coll))
ind (.indexOf coll e)]
(assoc (vec coll) ind ((fn [[a b]] [a (+ b 2)]) e) ))
多米尼克关于pred的说明适用于:
(def pred #(> (second %) 30))
这个函数不难“从头开始”递归编写。这不仅是一个很好的学习练习,它还产生了最好的解决方案:它尽可能地懒惰,并且计算量绝对最小。到目前为止,这个问题只有一个答案是懒惰的,pred
在更新发生之前对所有项目调用两次:一次在take while
中,一次在drop while
中,部分与分离
(defn update-first [pred f coll]
(lazy-seq
(when-let [coll (seq coll)]
(if (pred (first coll))
(cons (f (first coll))
(rest coll))
(cons (first coll)
(update-first pred f (rest coll)))))))
好处:这个解决方案是懒惰的。