我正在寻找一个函数,它返回序列中第一个元素,其中fn的计算结果为true。例如:
(first-map (fn [x] (= x 1)) '(3 4 1))
上面的假函数应该返回1(列表中的最后一个元素)。在Clojure中有这样的东西吗?
答案 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