从嵌套结构中选择与Clojure中的条件匹配的元素
我最近发现了提供数据结构导航和转换功能的库,它是用Clojure编写的 实现一些API作为学习练习似乎是个好主意。Specter实现了一个API,该API将函数和嵌套结构作为参数,并从嵌套结构返回满足以下函数的元素向量:从嵌套结构中选择与Clojure中的条件匹配的元素,clojure,specter,Clojure,Specter,我最近发现了提供数据结构导航和转换功能的库,它是用Clojure编写的 实现一些API作为学习练习似乎是个好主意。Specter实现了一个API,该API将函数和嵌套结构作为参数,并从嵌套结构返回满足以下函数的元素向量: (选择(沃克号码?[1:a{:b2}])=>[12] 下面是我使用类似API实现函数的尝试: (defn select-walker [afn ds] (vec (if (and (coll? ds) (not-empty ds)) (concat (se
(选择(沃克号码?[1:a{:b2}])
=>[12]
下面是我使用类似API实现函数的尝试:
(defn select-walker [afn ds]
(vec (if (and (coll? ds) (not-empty ds))
(concat (select-walker afn (first ds))
(select-walker afn (rest ds)))
(if (afn ds) [ds]))))
(选择沃克号码?[1:a{:b2}])
=>[12]
我已经尝试通过使用、和使用和来实现select walker
。在所有这些情况下
返回值是一个嵌套列表,而不是元素的平面向量
然而,我的实现看起来不像是惯用的Clojure,时间和空间复杂度都很低
(time (dotimes [_ 1000] (select (walker number?) (range 100))))
"Elapsed time: 19.445396 msecs"
(time (dotimes [_ 1000] (select-walker number? (range 100))))
"Elapsed time: 237.000334 msecs"
请注意,我的实现大约比Specter的实现慢12倍
关于select walker
的实现,我有三个问题
select walker
的尾部递归实现是否可能select walker
是否可以用更惯用的Clojure编写select walker
更快地执行吗(defn select-walker-rec [afn ds]
(loop [res [] ds ds]
(cond (empty? ds) res
(coll? (first ds)) (recur res
(doall (concat (first ds)
(rest ds))))
(afn (first ds)) (recur (conj res (first ds)) (rest ds))
:else (recur res (rest ds)))))
答复:
user> (select-walker-rec number? [1 :a {:b 2}])
[1 2]
user> user> (time (dotimes [_ 1000] (select-walker-rec number? (range 100))))
"Elapsed time: 19.428887 msecs"
(simple select walker为我工作约200毫秒)
第二种方法(虽然速度较慢,但更适合于更困难的任务)是使用拉链
:
(require '[clojure.zip :as z])
(defn select-walker-z [afn ds]
(loop [res [] curr (z/zipper coll? seq nil ds)]
(cond (z/end? curr) res
(z/branch? curr) (recur res (z/next curr))
(afn (z/node curr)) (recur (conj res (z/node curr))
(z/next curr))
:else (recur res (z/next curr)))))
user> (time (dotimes [_ 1000] (select-walker-z number? (range 100))))
"Elapsed time: 219.015153 msecs"
这一个真的很慢,因为拉链在更复杂的结构上工作。它的强大功能为这个简单的任务带来了不必要的开销tree-seq
:
(defn select-walker-t [afn ds]
(filter #(and (not (coll? %)) (afn %))
(tree-seq coll? seq ds)))
user> (time (dotimes [_ 1000] (select-walker-t number? (range 100))))
"Elapsed time: 1.320209 msecs"
它的速度令人难以置信,因为它产生了一系列延迟的结果。事实上,您应该了解公平测试的数据:
user> (time (dotimes [_ 1000] (doall (select-walker-t number? (range 100)))))
"Elapsed time: 53.641014 msecs"
关于这个变体,还有一点需要注意,它不是尾部递归的,因此在嵌套非常深的结构的情况下它会失败(也许我错了,但我猜它大约有几千层嵌套),但它仍然适用于大多数情况(defn select-walker-rec [afn ds]
(loop [res [] ds ds]
(cond (empty? ds) res
(coll? (first ds)) (recur res
(doall (concat (first ds)
(rest ds))))
(afn (first ds)) (recur (conj res (first ds)) (rest ds))
:else (recur res (rest ds)))))
答复:
user> (select-walker-rec number? [1 :a {:b 2}])
[1 2]
user> user> (time (dotimes [_ 1000] (select-walker-rec number? (range 100))))
"Elapsed time: 19.428887 msecs"
(simple select walker为我工作约200毫秒)
第二种方法(虽然速度较慢,但更适合于更困难的任务)是使用拉链
:
(require '[clojure.zip :as z])
(defn select-walker-z [afn ds]
(loop [res [] curr (z/zipper coll? seq nil ds)]
(cond (z/end? curr) res
(z/branch? curr) (recur res (z/next curr))
(afn (z/node curr)) (recur (conj res (z/node curr))
(z/next curr))
:else (recur res (z/next curr)))))
user> (time (dotimes [_ 1000] (select-walker-z number? (range 100))))
"Elapsed time: 219.015153 msecs"
这一个真的很慢,因为拉链在更复杂的结构上工作。它的强大功能为这个简单的任务带来了不必要的开销tree-seq
:
(defn select-walker-t [afn ds]
(filter #(and (not (coll? %)) (afn %))
(tree-seq coll? seq ds)))
user> (time (dotimes [_ 1000] (select-walker-t number? (range 100))))
"Elapsed time: 1.320209 msecs"
它的速度令人难以置信,因为它产生了一系列延迟的结果。事实上,您应该了解公平测试的数据:
user> (time (dotimes [_ 1000] (doall (select-walker-t number? (range 100)))))
"Elapsed time: 53.641014 msecs"
关于这个变体,还有一点需要注意,它不是尾部递归的,因此在嵌套非常深的结构的情况下它会失败(也许我错了,但我猜它大约有几千层嵌套),但它仍然适用于大多数情况谢谢我喜欢
select walker rec
通过嵌套的ds递归的方式。类似于深度优先遍历。谢谢。我喜欢select walker rec
通过嵌套的ds递归的方式。类似于深度优先遍历。