Clojure 反记录参数筛选

Clojure 反记录参数筛选,clojure,record,Clojure,Record,假设我定义了这样一个记录: (defrecord MyRecord [x y z]) (def test (map->MyRecord {:x "1" :y "2" :z "3" :w "ikk"})) (:w test) ; Returns "ikk" 我是这样构造的: (defrecord MyRecord [x y z]) (def test (map->MyRecord {:x "1" :y "2" :z "3" :w "ikk"})) (:w test) ; Re

假设我定义了这样一个记录:

(defrecord MyRecord [x y z])
(def test (map->MyRecord {:x "1" :y "2" :z "3" :w "ikk"}))
(:w test) ; Returns "ikk"
我是这样构造的:

(defrecord MyRecord [x y z])
(def test (map->MyRecord {:x "1" :y "2" :z "3" :w "ikk"}))
(:w test) ; Returns "ikk"
我可以这样做:

(defrecord MyRecord [x y z])
(def test (map->MyRecord {:x "1" :y "2" :z "3" :w "ikk"}))
(:w test) ; Returns "ikk"
为什么保留:w?我在想,因为我已经创建了一个记录,记录了x,y和z,所以这些是唯一应该出现的键。 有没有一种不使用选择键就排除记录声明中不作为参数出现的键的好方法? 例如:

(defrecord MyRecord1 [x y z])
(defrecord MyRecord2 [x y w])
(defprotocol MyProtocol
  (do-stuff [data]))

(extend-protocol MyProtocol
  MyRecord1
  (do-stuff [data]
    (let [data (select-keys data [:x :y :z])] ; (S1)
      ...))

  MyRecord2
  (do-stuff [data]
    (let [data (select-keys data [:x :y :w])] ; (S2)
      ...)))
当使用map->和其他我不关心的数据构造记录时,我想避免在使用MyProtocol时为每个记录手动选择键S1、S2。

这是defrecord的一个功能。请参阅:deftype和defrecord部分:

defrecord提供了持久映射的完整实现,包括:

... 可扩展字段可以关联未随defrecord定义提供的键 通过反射,您可以看到x、y、z参数是对象的常规属性:

user=> (>pprint (.? (map->MyRecord {:w 4})))
(#[__extmap :: (user.MyRecord) | java.lang.Object]
 ;...
 #[x :: (user.MyRecord) | java.lang.Object]
 #[y :: (user.MyRecord) | java.lang.Object]
 #[z :: (user.MyRecord) | java.lang.Object])
附加值存储在_extmap中:

这意味着,除了注意您希望将记录作为地图处理的位置之外,您什么都没有了,因为可以随时添加新的键:

user=> (let [r (->MyRecord 1 2 3) r (assoc r :w 4)] (keys r))
(:x :y :z :w)
因此,如果您发现自己在重复类似select keys myr[:x:y:z]的代码,则将其提取为辅助对象fn

添加像您自己的c'tors这样的东西总是一个好主意,例如,如果您想用0来表示缺少的密钥,而不是零,例如,但这只会保护您自己和遵循您的示例的用户。

Clojure记录实现以及Java互操作,其行为类似于普通贴图-这意味着您可以在任何地方使用它们,使用方式与使用贴图相同。您可以将记录视为一个类型化映射—它是一个映射,但您可以使用多方法和协议轻松地进行调度

这使得开始使用普通映射表示数据非常容易,然后在需要类型时前进到记录

作为映射,记录支持附加键,但它们的处理方式不同。在您的示例中,.x test起作用,但.w test不起作用,因为只有预定义的键成为实现Java类中的字段

要避免额外的键,只需创建自己的构造函数:

(defn limiting-map->MyRecord
  [m]
  (map->MyRecord
   (select-keys m [:x :y :z])))