【问题标题】:Clojure: construct function based on variables dynamcally?Clojure:动态地基于变量构造函数?
【发布时间】:2014-08-29 00:56:01
【问题描述】:

对于以下数据:

(def occurrence-data '(["John" "Artesyn" 1 31.0] ["Mike" "FlexPower" 2 31.0] ["John" "Eaton" 1 31.0]))

我想要一个函数:

(defn visit-numbers
  "Produce a map from coordinates to number of customer visits from occurrence records."
  [coordinates occurrences]
  (let [selector ??? ; a function that would be equivalent to (juxt #(nth % c1) #(nth % c2) ..), where c1, c2, ... are elements of coordinates 
        ]
    (group-by selector occurrences)
  )

例如,对于坐标 = [1 3]

应该是

(group-by (juxt #(nth % 1) #(nth % 3)) occurrence-data)

我想应该可以吧?我尝试使用一些列表表达式,但还没有弄清楚。

我的以下实验:

(def selector (list 'juxt '#(nth % 1) '#(nth % 3)))
(group-by selector occurrence-data)

出现错误:

java.lang.ClassCastException: clojure.lang.PersistentList cannot be cast to clojure.lang.IFn
           core.clj:6600 clojure.core/group-by[fn]
       protocols.clj:143 clojure.core.protocols/fn
        protocols.clj:19 clojure.core.protocols/fn[fn]
        protocols.clj:31 clojure.core.protocols/seq-reduce
        protocols.clj:48 clojure.core.protocols/fn
        protocols.clj:13 clojure.core.protocols/fn[fn]
           core.clj:6289 clojure.core/reduce
           core.clj:6602 clojure.core/group-by

我有两个问题要解决:

  1. 如何让选择器成为一个函数?
  2. 如何动态构建这种基于函数的坐标?

感谢您的指点和帮助!

我也猜想使用宏也可以做到?

还是我使用了太复杂的方法来实现我的目标?

【问题讨论】:

    标签: clojure higher-order-functions


    【解决方案1】:

    只需直接调用 juxt 即可创建您的函数,并定义选择器来保存该函数:

    (def selector (juxt #(nth % 1) #(nth % 3)))
    

    要使其动态化,请创建一个函数创建函数:

    (defn make-selector [& indexes] (apply juxt (map (fn[i] #(nth % i)) indexes)))
    

    REPL 示例:

    core> (def occurrence-data '(["John" "Artesyn" 1 31.0] ["Mike" "FlexPower" 2 31.0] ["John" "Eaton" 1 31.0]))
    #'core/occurrence-data
    core> (def selector (juxt #(nth % 1) #(nth % 3)))
    #'core/selector
    core> (group-by selector occurrence-data)
    {["Artesyn" 31.0] [["John" "Artesyn" 1 31.0]], ["FlexPower" 31.0] [["Mike" "FlexPower" 2 31.0]], ["Eaton" 31.0] [["John" "Eaton" 1 31.0]]}
    core> (group-by (make-selector 0 1 2) occurrence-data)
    {["John" "Artesyn" 1] [["John" "Artesyn" 1 31.0]], ["Mike" "FlexPower" 2] [["Mike" "FlexPower" 2 31.0]], ["John" "Eaton" 1] [["John" "Eaton" 1 31.0]]}
    

    【讨论】:

    • 我认为 OP 混淆了函数和宏。你不需要引用函数体。
    • 您的答案错过了 OP 问题的第二部分:如何根据一组坐标动态创建这样的函数。在 Clojure 中使用关闭坐标的匿名函数很容易做到这一点。
    【解决方案2】:

    这几乎是index

    (clojure.set/index occurrence-data [2 3])
    ;=>
    ;    {{3 31.0, 2 2} #{["Mike" "FlexPower" 2 31.0]},
    ;     {3 31.0, 2 1} #{["John" "Eaton" 1 31.0] ["John" "Artesyn" 1 31.0]}}
    

    您可以看到,例如,有两条记录在坐标 2 和 3 处共享相同的值,这些值是 1 和 31.0。

    如果你想去掉索引并映射到一个计数,那么

    (reduce-kv 
      (fn [a k v] (conj a {(vals k) (count v)})) 
      {} 
      (clojure.set/index occurrence-data [2 3]))
    ;=> {(31.0 1) 2, (31.0 2) 1}
    

    【讨论】:

    • 感谢您向我展示现有的解决方案和优雅的简化!
    【解决方案3】:

    定义

    (defn group-by-indices [ns coll]
      (group-by #(mapv % ns) coll))
    

    然后,例如,

    (group-by-indices [1] occurrence-data)
    ;{["Artesyn"] [["John" "Artesyn" 1 31.0]],
    ; ["FlexPower"] [["Mike" "FlexPower" 2 31.0]],
    ; ["Eaton"] [["John" "Eaton" 1 31.0]]}
    

    (group-by-indices [2 3] occurrence-data)
    ;{[1 31.0] [["John" "Artesyn" 1 31.0] ["John" "Eaton" 1 31.0]],
    ; [2 31.0] [["Mike" "FlexPower" 2 31.0]]}
    

    如果要保留选择图,请使用select-keys 而不是mapv。然后我们接近A.Webb's use of clojure.set/index,也就是在其他条件相同的情况下,选择的方法。

    【讨论】:

    • 谢谢!使用 mapv 或 map 和 group-by 来实现目标非常优雅。
    猜你喜欢
    • 2011-08-08
    • 1970-01-01
    • 2012-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-22
    • 2020-11-28
    • 1970-01-01
    相关资源
    最近更新 更多