【问题标题】:How do I get core clojure functions to work with my defrecords如何让核心 clojure 函数与我的 defrecords 一起使用
【发布时间】:2010-09-19 12:11:21
【问题描述】:

我有一个名为 bag 的 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 的 keys 函数最终在作为其参数的地图上调用 getKeys 来以正确的方式解决这个问题?

【问题讨论】:

    标签: clojure protocols frequency census


    【解决方案1】:

    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。

    【讨论】:

    • 有趣。谢谢你。这不是原版地图是有原因的。如果我对居住在行星上的人进行人口普查:我可能想要这样的东西:(获取行星地球)=> 80 亿,(获取行星火星)=> 0。除非我使用(得到行星火星0)。我不想让客户知道这一点。
    【解决方案2】:

    Rob 的回答当然是正确的;我发布这个是为了回应 OP 对此的评论——也许它可能有助于使用 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 放入常规地图中(merge-ing 反过来可以正常工作)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-28
      • 1970-01-01
      • 2012-05-07
      • 2019-08-14
      • 2019-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多