Как заставить основные функции clojure работать с моими отчетами? - PullRequest
2 голосов
/ 19 сентября 2010

У меня есть defrecord, называемый сумкой.Он ведет себя как список предметов для подсчета.Это иногда называют частотой или переписью.Я хочу иметь возможность сделать следующее

(def b (bag/create [:k 1 :k2 3])  
(keys bag)
=> (:k :k1)

Я попробовал следующее:

(defrecord MapBag [state]                                                                                                                                         
  Bag                                                                                                                                                             
   (put-n [self item n]                                                                                                                                          
     (let [new-n (+ n (count self item))]                                                                                                                        
          (MapBag. (assoc state item new-n))))                                                                                                                      

  ;... some stuff

  java.util.Map                                                                                                                                                   
    (getKeys [self] (keys state)) ;TODO TEST                                                                                                                      

  Object                                                                                                                                                          
   (toString [self]                                                                                                                                              
     (str ("Bag: " (:state self)))))   

Когда я пытаюсь потребовать его в ответ, я получаю:

java.lang.ClassFormatError: Duplicate interface name in class file compile__stub/techne/bag/MapBag (bag.clj:12)

Что происходит?Как получить функцию ключей на моей сумке?Также я собираюсь сделать это правильным путем, предполагая, что функция ключей clojure в конечном итоге вызывает getKeys на карте, которая является ее аргументом?

Ответы [ 2 ]

4 голосов
/ 19 сентября 2010

Defrecord автоматически удостоверяется, что любая запись, которую это определяет, участвует в интерфейсе ipersistentmap. Таким образом, вы можете вызывать ключи, ничего не делая .

Таким образом, вы можете определить запись, а также создать и вызвать ключи следующим образом:

user> (defrecord rec [k1 k2])
user.rec
user> (def a-rec (rec. 1 2))
#'user/a-rec
user> (keys a-rec)
(:k1 :k2)

Ваше сообщение об ошибке указывает на то, что одно из ваших объявлений дублирует интерфейс, который defrecord предоставляет вам бесплатно. Я думаю, что на самом деле это могут быть оба.

Есть ли какая-то причина, почему вы не можете просто использовать простую карту ванили для своих целей? С clojure вы часто хотите использовать простые ванильные структуры данных, когда можете.

Редактировать: если по какой-либо причине вы не хотите, чтобы ipersistentmap был включен, посмотрите на deftype.

3 голосов
/ 19 сентября 2010

Ответ Роба, конечно, правильный;Я публикую это в ответ на комментарий ОП к нему - возможно, это может быть полезно для реализации требуемой функциональности с deftype.

. Я однажды написал реализацию «карты по умолчанию» дляClojure, который действует как обычная карта, за исключением того, что он возвращает фиксированное значение по умолчанию, когда его спрашивают о ключе, отсутствующем внутри него.Код находится в этом Gist .

Я не уверен, что он подойдет для вашего варианта использования напрямую, хотя вы можете использовать его для таких вещей, как

user> (:earth (assoc (DefaultMap. 0 {}) :earth 8000000000))
8000000000
user> (:mars (assoc (DefaultMap. 0 {}) :earth 8000000000))
0

Что еще более важно, он должен дать вам представление о том, что связано с написанием такого рода вещей с помощью deftype.

С другой стороны, он основан на clojure.core/emit-defrecord, так что вы можете посмотреть на эту часть источников Clojure.вместо этого ... Он делает много вещей, которые вам не понадобятся (потому что это функция для подготовки расширений макросов - в нем много цитат и тому подобного, которые вы должны удалить из него, чтобы использоватьнепосредственно), но это, безусловно, источник информации самого высокого качества. Здесь - прямая ссылка на эту точку в источнике для релиза Clojure 1.2.0.

Обновление:

Еще одна вещь, которую японял, может быть важно.Если вы используете специальный тип карты для реализации такого рода вещей, клиент может merge превратить его в обычную карту и потерять «дефолтную» функциональность (и даже любую другую специальную функциональность) в процессе.Пока иллюзия "сходства с картой", поддерживаемая вашим типом, является достаточно полной, чтобы ее можно было использовать в качестве обычной карты, передавать в стандартную функцию Clojure и т. Д., Я думаю, что этого не может быть.

Итак, на каком-то уровне клиент, вероятно, должен будет знать, что в этом есть какая-то «магия»;если они получат правильные ответы на запросы, такие как (:mars {...}) (без :mars в {...}), им придется помнить, чтобы не merge это в регулярную карту (merge - наоборот)будет работать нормально).

...