我有一个seq, (def coll '([:a 20] [:b 30] [:c 50] [:d 90]))
我想迭代seq,并只修改与谓词匹配的第一个元素.
谓词 (def pred (fn [[a b]] (> b 30)))
(f pred (fn [[a b]] [a (+ b 2)]) coll) => ([:a 20] [:b 30] [:c 52] [:d 90])
f是我想要的fn,它取一个pred,一个fn应用于匹配pred的第一个elem.所有其余的元素都没有修改并在seq中返回.
做上述的惯用方法是什么?
一种可能的方法是拆分集合split-with,将函数f应用于返回的第二个集合的第一个元素split-with,然后concat再将元素组合在一起.
(defn apply-to-first [pred f coll]
(let [[h t] (split-with (complement pred) coll)]
(concat h (list (f (first t))) (rest t))))
Run Code Online (Sandbox Code Playgroud)
请注意,pred示例中的函数应该如下所示:
(def pred #(> (second %) 30))
Run Code Online (Sandbox Code Playgroud)
与大多数问题一样,有多种方法可以解决它。这只是其中之一。
如果您运行的是 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])
Run Code Online (Sandbox Code Playgroud)
该算法的关键是使用reduced(注意“d”)函数 - 它本质上是告诉reduce停止迭代并返回结果。从它的文档字符串:
-------------------------
clojure.core/reduced
([x])
Wraps x in a way such that a reduce will terminate with the value x
Run Code Online (Sandbox Code Playgroud)
代码有点简洁,但它应该可以让您了解基本的想法。
希望这可以帮助。