返回满足函数返回true的地图/列表/序列中的第一项
我正在寻找一个函数,返回一个序列中的第一个元素,将fn评估为true。 例如:
(first-map (fn [x] (= x 1)) '(3 4 1))
上面的假function应该返回1(列表中的最后一个元素)。 在Clojure中有这样的东西吗?
user=> (defn find-first [f coll] (first (filter f coll))) #'user/find-first user=> (find-first #(= % 1) [3 4 1]) 1
编辑:并发。 :)不,它不适用于整个名单。 由于filter
懒惰,只能到达第一个匹配的元素。
就你而言,这个成语是
(some #{1} [1 2 3 4])
工作原理:#{1}是一组字面值。 一个集合也是一个函数,如果arg存在于集合中则评估其arg,否则为零。 任何set元素都是一个“真值”(好吧,除了布尔值假,但这是一个罕见的集合)。 some
返回谓词的返回值,这个谓词是针对第一个集合成员评估的,结果是真实的。
我尝试了这个线程中提到的几个方法(JDK 8和Clojure 1.7),并做了一些基准testing:
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
方式可能是最有效的解决scheme,如clojure 1.7。
我认为some
是这个工作最好的工具:
(some #(if (= % 1) %) '(3 4 1))
使用drop-while
代替filter
应该解决分块序列的f
“over-application”问题:
(defn find-first [f coll] (first (drop-while (complement f) coll))) ;;=> #'user/find-first (find-first #(= % 1) [3 4 1]) ;;=> 1