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_Specter - Fatal编程技术网

从嵌套结构中选择与Clojure中的条件匹配的元素

从嵌套结构中选择与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

我最近发现了提供数据结构导航和转换功能的库,它是用Clojure编写的

实现一些API作为学习练习似乎是个好主意。Specter实现了一个API,该API将函数和嵌套结构作为参数,并从嵌套结构返回满足以下函数的元素向量:

(选择(沃克号码?[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递归的方式。类似于深度优先遍历。