2014-08-29 5 views
2

Для получения следующих данных:Clojure: построить функцию на основе переменных динамически?

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

Got ошибка:

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. Как динамически строить такие координаты на основе функций?

Спасибо за ваши указатели и помогите!

Я также предполагаю, что использование макроса может также быть возможным?

Или я использую слишком сложный метод для достижения своей цели?

ответ

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]]} 
+0

Я думаю, что OP смешивал функции и макросы. Вам не нужно цитировать тела функций. – DaoWen

+0

Это имеет смысл, хороший момент! – YosemiteMark

+0

Ваш ответ пропускает вторую часть вопроса OP: как * динамически * создавать такую ​​функцию в зависимости от набора координат. Это тривиально сделать в Clojure, используя анонимную функцию, которая закрывается над координатами. – DaoWen

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} 
+0

Спасибо, что продемонстрировали мне существующее решение и элегантное сокращение! –

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, что при прочих равных условиях является методом выбора.

+0

Спасибо! Удивительно элегантно, используя mapv или карту и группу для достижения целей. –

Смежные вопросы