我正在寻找一个函数,它返回 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
返回针对结果为真的第一个集合成员评估的谓词的返回值。
我尝试了这个线程中提到的几种方法(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 中最有效的解决方案。
2016 年有一个补丁提交给 clojure core(first (filter pred coll))
,它为idiom添加了一个有效的快捷方式,它被称为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
最终补丁被拒绝:
经审查,我们决定不希望将其包括在内。使用线性搜索(尤其是嵌套线性搜索)会导致性能下降——通常最好使用其他类型的数据结构,这就是为什么过去没有包含此功能的原因。~亚历克斯·米勒 12/5/17 下午 3: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