Clojure:动态构造基于变量的函数?

时间:2014-08-29 00:56:02

标签: clojure higher-order-functions

以下数据:

(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)
  )

例如,对于coordinates = [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. 如何动态构建这样的基于函数的坐标?
  3. 感谢您的指示和帮助!

    我也猜测使用宏也可能会这样做吗?

    或者我使用过于复杂的方法来实现我的目标?

3 个答案:

答案 0 :(得分:4)

只需直接调用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]]}

答案 1 :(得分: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}

答案 2 :(得分:1)

定义

(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,这是其他条件相同的选择方法。