fnがtrueと評価されるシーケンスの最初の要素を返す関数を探しています。例えば:
(first-map (fn [x] (= x 1)) '(3 4 1))
上記の偽の関数は1(リストの最後の要素)を返す必要があります。Clojureにこのようなものはありますか?
fnがtrueと評価されるシーケンスの最初の要素を返す関数を探しています。例えば:
(first-map (fn [x] (= x 1)) '(3 4 1))
上記の偽の関数は1(リストの最後の要素)を返す必要があります。Clojureにこのようなものはありますか?
user=> (defn find-first
[f coll]
(first (filter f coll)))
#'user/find-first
user=> (find-first #(= % 1) [3 4 1])
1
編集:並行性。f
:)いいえ。リスト全体には適用されません。の怠惰のため、最初に一致する要素までの要素のみfilter
。
あなたの場合、イディオムは
(some #{1} [1 2 3 4])
仕組み:#{1}はセットリテラルです。セットは、argがセットに存在する場合はそのargを評価し、存在しない場合はnilを評価する関数でもあります。すべてのセット要素は「真の」値です(ブール値のfalseを除いて、それはセットではまれです)。some
結果が真であった最初のコレクションメンバーに対して評価された述部の戻り値を返します。
このスレッドで言及されているいくつかの方法(JDK8およびClojure1.7)を試し、いくつかのベンチマークテストを行いました。
repl> (defn find-first
[f coll]
(first (filter f coll)))
#'cenx.parker.strategies.vzw.repl/find-first
repl> (time (find-first #(= % 50000000) (range)))
"Elapsed time: 5799.41122 msecs"
50000000
repl> (time (some #{50000000} (range)))
"Elapsed time: 4386.256124 msecs"
50000000
repl> (time (reduce #(when (= %2 50000000) (reduced %2)) nil (range)))
"Elapsed time: 993.267553 msecs"
50000000
結果はreduce
、clojure1.7のようにその方法が最も効率的な解決策である可能性があることを示しています。
2016年にclojureコアに提出されたパッチがあり、イディオムの効率的なショートカットが追加されました。(first (filter pred coll))
これはと呼ばれていseek
ました。
(first (filter))
実装は、と(some #(when (pred)))
代替案の両方で本質的に問題を回避しました。つまり、チャンク化されたシーケンスで効率的に機能しnil?
、false?
述語とうまく連携します。
パッチ:
(defn seek
"Returns first item from coll for which (pred item) returns true.
Returns nil if no such item is present, or the not-found value if supplied."
{:added "1.9" ; note, this was never accepted into clojure core
:static true}
([pred coll] (seek pred coll nil))
([pred coll not-found]
(reduce (fn [_ x]
(if (pred x)
(reduced x)
not-found))
not-found coll)))
例:
(seek odd? (range)) => 1
(seek pos? [-1 1]) => 1
(seek pos? [-1 -2] ::not-found) => ::not-found
(seek nil? [1 2 nil 3] ::not-found) => nil
最終的に、パッチは拒否されました。
審査の結果、これを含めたくないと判断しました。線形検索(特にネストされた線形検索)を使用すると、パフォーマンスが低下します。多くの場合、他の種類のデータ構造を使用する方が適切であるため、この機能はこれまで含まれていませんでした。〜AlexMiller2017年5月12日15:34
私some
はその仕事に最適なツールだと思います:
(some #(if (= % 1) %) '(3 4 1))
drop-while
代わりにを使用すると、チャンク化されたシーケンスfilter
の「過剰適用」に対処する必要があります。f
(defn find-first [f coll]
(first (drop-while (complement f) coll)))
;;=> #'user/find-first
(find-first #(= % 1) [3 4 1])
;;=> 1