返回满足谓词的map / list / sequence中的第一项

时间:2012-04-17 13:52:46

标签: clojure

我正在寻找一个函数,它返回序列中第一个元素,其中fn的计算结果为true。例如:

(first-map (fn [x] (= x 1)) '(3 4 1))

上面的假函数应该返回1(列表中的最后一个元素)。在Clojure中有这样的东西吗?

6 个答案:

答案 0 :(得分:57)

user=> (defn find-first
         [f coll]
         (first (filter f coll)))
#'user/find-first
user=> (find-first #(= % 1) [3 4 1])
1

编辑:并发。 :)不。它不会将f应用于整个列表。由于filter的懒惰,仅限于第一个匹配的元素。

答案 1 :(得分:47)

在你的情况下,成语是

(some #{1} [1 2 3 4])

工作原理:#{1}是一个集合文字。如果arg存在于集合中,则set也是评估其arg的函数,否则为nil。任何set元素都是一个“truthy”值(好吧,除了布尔值false,但这在集合中很少见)。 some返回针对结果真实的第一个集合成员计算的谓词的返回值。

答案 2 :(得分:14)

我尝试了这个线程中提到的几个方法(JDK 8和Clojure 1.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方式可能是最有效的方法,如clojure 1.7。

答案 3 :(得分:12)

我认为some是这项工作的最佳工具:

(some #(if (= % 1) %) '(3 4 1))

答案 4 :(得分:3)

使用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

答案 5 :(得分:1)

In 2016 there was a patch submitted to clojure core(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

最终补丁被拒绝了:

  

经审查,我们决定不希望包含此内容。线性搜索(尤其是嵌套线性搜索)的使用会导致性能下降-通常最好使用其他类型的数据结构,这就是过去不包含此功能的原因。 〜Alex Miller 17年5月12日下午3:34