如何在第一个真谓词匹配的嵌套映射上退出Clojure walk postwalk?
我使用如何在第一个真谓词匹配的嵌套映射上退出Clojure walk postwalk?,clojure,Clojure,我使用clojure.walk/postwark将谓词与嵌套集合中的每个映射进行比较,并希望在第一个true中以true退出。我该怎么做?我同意它遍历整个数据结构,如果存在true匹配,则返回true 作为一个推论问题,我想同样的问题也适用于执行map而不是postwark时 更新:这确实是一个令人厌倦/懒惰的问题;我应该提供一个代码示例。这就是说,我把它留着,以防现在有人对我这个半生不熟的问题给出答案。唯一比问别人更糟糕的是,在有人友善地开始帮忙后,把它取下来。如果没有人回答,如果他们要求一个
clojure.walk/postwark
将谓词与嵌套集合中的每个映射进行比较,并希望在第一个true
中以true退出。我该怎么做?我同意它遍历整个数据结构,如果存在true
匹配,则返回true
作为一个推论问题,我想同样的问题也适用于执行map
而不是postwark
时
更新:这确实是一个令人厌倦/懒惰的问题;我应该提供一个代码示例。这就是说,我把它留着,以防现在有人对我这个半生不熟的问题给出答案。唯一比问别人更糟糕的是,在有人友善地开始帮忙后,把它取下来。如果没有人回答,如果他们要求一个更好的问题,或者他们只是给我一些研究建议,我会非常满意。您可能对我称之为
walk seq
的这个功能感兴趣。它返回一个数据结构上的延迟深度优先序列,然后您可以对该序列进行搜索以找到第一个匹配项。我发现在这里更可取,因为它不需要回调和异常来像clojure.walk/postwark
那样提前退出
(定义如下)
“返回数据结构中所有表单的延迟深度优先序列。”
[表格]
(树序列集合?序列表格)
(defn seek)
“查找集合中与pred匹配的第一个元素,
else返回未找到。请注意,使用seek可能导致
性能差,应始终使用索引数据
结构,而不是对同一数据进行多次搜索。”
([pred coll]
(搜索pred coll nil))
([pred coll未找到]
(减少(fn[nf x](如果(pred x)(减少的x)nf))未找到coll)))
walk seq的用法
:
(defn find-deep [pred data not-found]
(->> data
(tree-seq coll? seq)
(some #(when (pred %) [%]))
((fnil first [not-found]))))
user> (find-deep #(= (:c %) 30) [{:a 10 :b [{:c 20 :d {:c 30}}]}] ::none)
;;=> {:c 30}
user> (find-deep #(= (:c %) 40) [{:a 10 :b [{:c 20 :d {:c 30}}]}] ::none)
;;=> :user/none
(行走序列{:a[{:b-1}{:b1}]:b2})
=>
({:a[{:b-1}{:b1}],:b2}
[:a[{:b-1}{:b1}]]
:a
[{:b-1}{:b 1}]
{:b-1}
[:b-1]
:b
-1
{:b1}
[:b 1]
:b
1.
[:b2]
:b
2)
将两者结合起来:
(seek(每个pred编号?pos?)(walk seq{:a[{:b-1}{:b1}]:b2})
=>
1.
如我在注释中所建议的,只要谓词为true,就可以通过postwalk抛出异常来完成。这种方法非常传统,但很简洁,让我们可以重用postwark
的逻辑来遍历数据结构:
(defn walk-some [pred data]
(try
(clojure.walk/postwalk
#(if (pred %)
(throw (ex-info "Found" {:data %}))
%)
data)
false
(catch clojure.lang.ExceptionInfo e
true)))
(walk-some #(and (number? %) (odd? %)) {:a [[9] 3]})
;; => true
(walk-some #(and (number? %) (even? %)) {:a [[9] 3]})
;; => false
很少需要为控制流使用异常,但偶尔也需要稍微偏离约定。您可能需要定义一个自定义异常类型,以提高健壮性,以防谓词可以抛出类型为ExceptionInfo
的对象,方法稍有不同,还可以使用树序列
:
(defn find-deep [pred data not-found]
(->> data
(tree-seq coll? seq)
(some #(when (pred %) [%]))
((fnil first [not-found]))))
user> (find-deep #(= (:c %) 30) [{:a 10 :b [{:c 20 :d {:c 30}}]}] ::none)
;;=> {:c 30}
user> (find-deep #(= (:c %) 40) [{:a 10 :b [{:c 20 :d {:c 30}}]}] ::none)
;;=> :user/none
如果您想要的唯一结果是true
或false
,那么听起来您根本不想要postwark
。您希望编写一个递归函数,该函数使用您的一个结构并生成一个布尔值。如果在一个分支中发现结果,则可以短路。听起来不错。。。现在我将对此进行研究。一旦谓词为true,您就可以在walk中抛出异常。如果周围的代码捕捉到该异常,则表示谓词在某个地方为true。这种方法当然是非传统的,但它可以工作。我相信coll?
(来自标准库)可以用来代替branch?
,谢谢。我已将答案更新为使用coll?
。我对一些案例有模糊的记忆,这导致我改用了seqable?
,但我现在找不到它——也许它是针对我项目中的自定义数据类型的。