5

私はシーケンスを持っています、(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 と、pred に一致する最初の elem に適用する fn を取ります。残りの要素はすべて変更されず、seq で返されます。

上記を行う慣用的な方法は何ですか?

4

4 に答える 4

5

考えられる方法の 1 つは、 でコレクションを分割し、によって返された 2 番目のコレクションの最初の要素にsplit-with関数を適用し、要素を再び一緒にすることです。fsplit-withconcat

(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))
于 2013-05-10T11:24:52.150 に答える
4

ほとんどの問題と同様に、それを解決する方法はいくつかあります。これはそのうちの 1 つにすぎません。

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

コードは少し簡潔ですが、基本的な考え方は理解できるはずです。

お役に立てれば。

于 2013-05-10T11:20:24.587 に答える
4

この関数は、「最初から」再帰的に書くのは難しくありません。これは良い学習課題であるだけでなく、最善の解決策も生み出します。可能な限り怠惰で、計算量を最小限に抑えます。これまでのところ、この質問に対する怠惰な答えは 1 つだけであり、pred更新が発生する前にすべての項目に対して 2 回呼び出します。take-whiledrop-whilesplit-with

(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)))))))
于 2013-05-11T09:44:11.787 に答える
0

簡単にするために、最初の要素を見つけ、そのインデックスを見つけ、assoc を使用してインデックスの要素を「更新」します。

(let [e (first (filter pred coll))
      ind (.indexOf coll e)] 
  (assoc (vec coll) ind ((fn [[a b]] [a (+ b 2)]) e) ))

Pred に関する Dominic のメモが適用されます。

(def pred #(> (second %) 30))
于 2013-05-10T12:34:21.943 に答える