Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 返回映射/列表/序列中满足谓词的第一项_Clojure - Fatal编程技术网

Clojure 返回映射/列表/序列中满足谓词的第一项

Clojure 返回映射/列表/序列中满足谓词的第一项,clojure,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

我正在寻找一个函数,它返回序列中的第一个元素,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

编辑:一个并发代码。:)否。它不适用于整个列表。由于
过滤器的惰性,仅对第一个匹配的元素进行过滤

我认为
一些
是该工作的最佳工具:

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

在你的例子中,这个成语是

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

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

使用
drop而不是
filter
应该解决
f
对于分块序列的“过度应用”:

(defn find-first [f coll]
  (first (drop-while (complement f) coll)))
;;=> #'user/find-first

(find-first #(= % 1) [3 4 1])
;;=> 1

我尝试了本线程中提到的几种方法(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中最有效的解决方案。

(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
最终,该修补程序被拒绝:

经审查,我们决定不将其包括在内。使用线性搜索(尤其是嵌套线性搜索)会导致性能低下-通常最好使用其他类型的数据结构,这就是为什么以前没有包含此功能的原因~亚历克斯·米勒2017年5月12日下午3:34


为什么不干脆
(首先(filter#(=%1)”(3 4 1))
?@4e6因为这会将函数应用于列表中的每个元素,这在大列表中可能不受欢迎。Map是懒惰的,所以我不认为它会。不过你应该对此进行测试。谢谢@kotarak,我担心这会处理集合中的所有元素,而在大列表中可能不受欢迎。也许我需要创建一个te一个递归函数,直到找到满足条件的元素。@Matthew模分块序列。
f
可能会根据分块大小应用到更多元素。使用
过滤器或
一些
会占用更多资源。或者,
reduced
可以缩短整个计算过程,而不必花费时间更少的资源:
(reduce#(when(=250000000)(reduced%2))nil(range))
@xando您是否使用了类似的东西来证明它?
user=>(def input(range))#'user/input user=>(time(some#{50000000}input))“运行时间:3909.789411毫秒”50000000 user=>(time(reduce#(when(=250000000)(reduced%2))nil input))“运行时间:198616.007581毫秒"50000000
这些恰好是我得到的结果。@xando事实上你一开始就说
一些
使用了更多的资源,
减少的
可以简化整个计算,同时占用更少的资源。只有在使用纯
范围
或其他可以直接减少自身。此外,所有备选方案都明显短路(
范围
是一个无限序列)。这不会受到分块序列效应的影响。但在
1
nil
false
.YMMV.等等……1怎么能等于nil?:)的情况下不起作用,马修指定元素应该“求真”,因此我认为行为是需要的。请考虑<代码> f <代码> > <代码>(包含{ 0)假“false } %”< /代码>。他说“评估fn为真”。非常有趣。谢谢你测试这些,xando。如果从中获取数字会更好,但我的猜测是,通过搜索序列中的一个项目,JVM有机会优化代码。如果您可以使用整个元素进行搜索,这是很有用的,但是如果部分索引,一些
将不会返回复合元素(如hashmaps)。
(->[nil nil nil nil 42 nil nil](某些标识))
42冷溶液:)。我测试以确保懒惰<代码>(查找第一个#(do(println%)(偶数?)[1 3 2 5 7 9])
,返回2,不打印5、7或9。根据Jira通知单,该修补程序被拒绝。他们不想鼓励人们使用低效的线性搜索。我觉得有道理:)