我有一个叫做袋子的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)))))
当我尝试在repl中要求它时,我得到:
java.lang.ClassFormatError: Duplicate interface name in class file compile__stub/techne/bag/MapBag (bag.clj:12)
发生了什么事?如何在包上获得按键功能?我也是通过假设clojure的键函数最终在地图上调用getKeys作为其参数来正确的方式吗?
答案 0 :(得分:4)
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时,您通常希望尽可能使用普通的vanilla数据结构。
编辑:如果由于某种原因您不希望包含ipersistentmap,请查看deftype。
答案 1 :(得分:3)
deftype
实现所需的功能。
我曾经为Clojure编写了一个“默认地图”的实现,它的作用就像一个普通的地图,除了当被问及里面没有一个键时它会返回一个固定的默认值。代码位于this Gist。
我不确定它是否会直接适合您的用例,尽管您可以使用它来执行
之类的操作user> (:earth (assoc (DefaultMap. 0 {}) :earth 8000000000))
8000000000
user> (:mars (assoc (DefaultMap. 0 {}) :earth 8000000000))
0
更重要的是,它应该让您了解使用deftype
编写此类内容所涉及的内容。
然后再次,它基于clojure.core/emit-defrecord
,所以你可能会看一下Clojure的源代码......它正在做很多你不需要的事情(因为它是准备宏的功能)扩展 - 里面有很多语法引用等等你必须脱掉它才能直接使用代码),但它肯定是最高质量的信息来源。 Here是Clojure 1.2.0版本源代码中的直接链接。
更新
我意识到的另一件事可能很重要。如果您依赖特殊的类似地图类型来实现此类事物,则客户端可能会merge
将其放入常规映射中并丢失该过程中的“默认”功能(实际上还有任何其他特殊功能)。只要您的类型维护的“地图般的”错觉足以将其用作常规地图,传递给Clojure的标准函数等,我认为可能没有办法解决这个问题。
因此,在某种程度上,客户可能必须知道存在一些“魔法”;如果他们得到(:mars {...})
(:mars
中没有{...}
)等查询的正确答案,他们就必须记住不要merge
将其转换为常规地图({ {1}} - 反过来会很好的工作。)