【问题标题】:Clojure thread-first with filter function具有过滤功能的 Clojure 线程优先
【发布时间】:2015-08-10 07:53:48
【问题描述】:

我在将一些表单串在一起以对来自 korma 函数的结果集执行一些 ETL 时遇到问题。

我从 korma sql 回来了:

({:id 1 :some_field "asd" :children [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3}] :another_field "qwe"})

我希望通过获取 :a 关键字为 1 的“孩子”来过滤此结果集。

我的尝试:

;mock of korma result
(def data '({:id 1 :some_field "asd" :children [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3}] :another_field "qwe"}))

(-> data 
    first 
    :children 
    (filter #(= (% :a) 1)))

我在这里期待的是一个 :a 设置为 1 的哈希图向量,即:

[{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4}]

但是,我收到以下错误:

IllegalArgumentException Don't know how to create ISeq from: xxx.core$eval3145$fn__3146  clojure.lang.RT.seqFrom (RT.java:505)

从我收集到的错误中,它正在尝试从一个函数创建一个序列......尽管只是无法将这些点连接起来以了解原因。

此外,如果我通过执行以下操作完全分离过滤器功能:

(let [children (-> data first :children)] 
    (filter #(= (% :a) 1) children))

它有效。我不确定为什么第一个线程没有应用过滤器函数,而是将 :children 向量作为 coll 参数传入。

非常感谢任何和所有帮助。

谢谢

【问题讨论】:

    标签: filter clojure sqlkorma


    【解决方案1】:

    你想要thread-last 宏:

    (->> data first :children (filter #(= (% :a) 1)))
    

    产量

    ({:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4})

    你原代码中的thread-first宏相当于写:

    (filter (:children (first data)) #(= (% :a) 1))
    

    这会导致错误,因为您的匿名函数不是序列。

    【讨论】:

      【解决方案2】:

      thread-first (->) 和 thread-last (->>) 宏总是有问题的,因为在选择一个而不是另一个时很容易出错(或者像你所做的那样混淆它们这里)。像这样分解步骤:

      (ns tstclj.core
        (:use cooljure.core)  ; see https://github.com/cloojure/tupelo/
        (:gen-class))
      
      (def data [ {:id 1 :some_field "asd" 
                   :children [ {:a 1 :b 2 :c 3} 
                                {:a 1 :b 3 :c 4}
                                {:a 2 :b 2 :c 3} ] 
                   :another_field "qwe"} ] )
      
      (def v1    (first data))
      (def v2    (:children v1))
      (def v3    (filter #(= (% :a) 1) v2))
      
      (spyx v1)    ; from tupelo.core/spyx
      (spyx v2)
      (spyx v3)
      

      你会得到如下结果:

      v1 => {:children [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}], :another_field "qwe", :id 1, :some_field "asd"}
      v2 => [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}]
      v3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
      

      这是您想要的。问题是您确实需要为filter 表单使用thread-last。避免此问题的最可靠方法是始终明确并使用 Clojure as-> 线程形式,或者更好的是 it-> 来自 the Tupelo library

      (def result (it-> data 
                        (first it)
                        (:children  it)
                        (filter #(= (% :a) 1) it)))
      

      通过使用线程优先,你不小心写了这样的等价物:

      (def result (it-> data 
                        (first it)
                        (:children  it)
                        (filter it #(= (% :a) 1))))
      

      并且该错误反映了函数#(= (% :a) 1) 无法转换为seq 的事实。有时,使用let 表单并为中间结果命名是值得的:

      (let [result-map        (first data)
            children-vec      (:children  result-map)
            a1-maps           (filter #(= (% :a) 1) children-vec) ]
        (spyx a1-maps))
      ;;-> a1-maps => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
      

      我们还可以查看前面两个解决方案中的任何一个,并注意到每个阶段的输出都用作管道中下一个函数的 last 参数。因此,我们也可以使用 thread-last 来解决它:

      (def result3  (->>  data
                          first
                          :children
                          (filter #(= (% :a) 1))))
      (spyx result3)
      ;;-> result3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
      

      除非您的处理链非常简单,否则我发现使用it-> 形式来明确说明管道的每个阶段应如何使用中间值总是更清晰。

      【讨论】:

        【解决方案3】:

        我不确定为什么第一个线程没有应用过滤器函数,将 :children 向量作为 coll 参数传入。

        这正是thread-first 宏的作用。

        来自clojuredocs.org

        通过表单将 expr 线程化。插入 x 作为 第一种形式中的第二项,如果不是 已经列出来了。

        因此,在您的情况下,filter 的应用程序最终是:

        (filter [...] #(= (% :a) 1))

        如果您必须使用thread-first(而不是thread-last),那么您可以通过部分应用filter 及其谓词来解决此问题:

        (->
          data
          first
          :children
          ((partial filter #(= (:a %) 1)))
          vec)
        
        ; [{:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}]
        

        【讨论】:

          猜你喜欢
          • 2019-06-09
          • 1970-01-01
          • 2017-01-13
          • 2011-06-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多