defrecord 参数过滤
defrecord argument filtrering
假设我定义了这样一条记录:
(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
的记录,所以这些是唯一应该存在的 "keys"。
- 有没有一种好方法可以在不使用
select-keys
的情况下排除 record
声明中未作为参数出现的键?
例如:
(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)
...)))
我想避免在使用 MyProtocol
时为每条记录手动执行 select-keys
(S1, S2) 当使用 map->
构建记录时使用附加数据(我不知道不关心)。
这是defrecord
的一个特点。请参阅:http://clojure.org/datatypes 部分 deftype and defrecord
:
defrecord provides a complete implementation of a persistent map, including:
- ...
- extensible fields (you can assoc keys not supplied with the defrecord definition)
通过反射你可以看到,你的 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=> (.-__extmap (map->MyRecord {:w 4}))
{:w 4}
这意味着,除了注意要将记录作为 Map 处理的地方外,您别无选择,因为可以随时添加新键:
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
用于丢失的密钥而不是 nil
,例如),但这只会保护您免受自己的伤害以及以您为榜样的用户。
Clojure 记录为 Java 互操作实现了 IPersistentMap (as well as java.util.Map),并且其行为类似于法线贴图 - 这意味着您可以在任何地方以与使用贴图相同的方式使用它们。您可以将记录视为类型映射 - 它是一个映射,但您可以使用多种方法和协议轻松地进行调度。
这使得开始使用普通地图表示数据变得非常容易,然后在需要类型时前进到记录。
作为映射,记录支持附加键,但它们的处理方式不同。对于您的示例,(.x test)
有效,但 (.w test)
无效,因为只有预定义键成为实现中的字段 Java class.
为了避免额外的键,只需创建自己的构造函数:
(defn limiting-map->MyRecord
[m]
(map->MyRecord
(select-keys m [:x :y :z])))
假设我定义了这样一条记录:
(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
的记录,所以这些是唯一应该存在的 "keys"。 - 有没有一种好方法可以在不使用
select-keys
的情况下排除record
声明中未作为参数出现的键?
例如:
(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)
...)))
我想避免在使用 MyProtocol
时为每条记录手动执行 select-keys
(S1, S2) 当使用 map->
构建记录时使用附加数据(我不知道不关心)。
这是defrecord
的一个特点。请参阅:http://clojure.org/datatypes 部分 deftype and defrecord
:
defrecord provides a complete implementation of a persistent map, including:
- ...
- extensible fields (you can assoc keys not supplied with the defrecord definition)
通过反射你可以看到,你的 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=> (.-__extmap (map->MyRecord {:w 4}))
{:w 4}
这意味着,除了注意要将记录作为 Map 处理的地方外,您别无选择,因为可以随时添加新键:
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
用于丢失的密钥而不是 nil
,例如),但这只会保护您免受自己的伤害以及以您为榜样的用户。
Clojure 记录为 Java 互操作实现了 IPersistentMap (as well as java.util.Map),并且其行为类似于法线贴图 - 这意味着您可以在任何地方以与使用贴图相同的方式使用它们。您可以将记录视为类型映射 - 它是一个映射,但您可以使用多种方法和协议轻松地进行调度。
这使得开始使用普通地图表示数据变得非常容易,然后在需要类型时前进到记录。
作为映射,记录支持附加键,但它们的处理方式不同。对于您的示例,(.x test)
有效,但 (.w test)
无效,因为只有预定义键成为实现中的字段 Java class.
为了避免额外的键,只需创建自己的构造函数:
(defn limiting-map->MyRecord
[m]
(map->MyRecord
(select-keys m [:x :y :z])))