惯用的方法只更新匹配coll中的pred的第一个elem

mur*_*a52 5 clojure

我有一个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中返回.

做上述的惯用方法是什么?

slo*_*oth 5

一种可能的方法是拆分集合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)


leo*_*ges 4

与大多数问题一样,有多种方法可以解决它。这只是其中之一。

如果您运行的是 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)

代码有点简洁,但它应该可以让您了解基本的想法。

希望这可以帮助。