clojure 规范:包含 :with 或 :height (XOR) 的地图
clojure spec: map containing either a :with or a :height (XOR)
以下 clojure 规范 ::my
允许映射具有键 :width 或键 :height,但它不允许同时具有它们:
(s/def ::width int?)
(s/def ::height int?)
(defn one-of-both? [a b]
(or (and a (not b))
(and b (not a))))
(s/def ::my (s/and (s/keys :opt-un [::width ::height])
#(one-of-both? (% :width) (% :height))))
即使它完成了工作:
(s/valid? ::my {})
false
(s/valid? ::my {:width 5})
true
(s/valid? ::my {:height 2})
true
(s/valid? ::my {:width 5 :height 2})
false
代码对我来说没有那么简洁。首先,键被定义为可选的,然后是必需的。有人对此有更易读的解决方案吗?
clojure.spec 旨在鼓励能够成长的规格。因此它的 s/keys
不支持禁止键。它甚至匹配具有不在 :req
或 opt
中的键的映射。
但是有一种说法是地图必须至少有:width
或:height
,即不是 XOR,只是 OR.
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
此功能内置于规范中 - 您可以在 req-un 中指定 and/or 模式:
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
:user/my
user=> (s/valid? ::my {})
false
user=> (s/valid? ::my {:width 5})
true
user=> (s/valid? ::my {:height 2})
true
user=> (s/valid? ::my {:width 5 :height 2})
true
只是想对原始问题中的规范进行小的修改,如果任何键持有的值是假的,即 false
或 nil
。
(spec/valid? ::my {:width nil})
=> false
当然,如果 int?
对问题中的值施加给定约束,则不会发生这种情况。但也许后代有人允许他们的价值观是 nilable 或布尔值,在这种情况下这个答案变得很方便。
如果我们改为将规范定义为:
(defn xor? [coll a-key b-key]
(let [a (contains? coll a-key)
b (contains? coll b-key)]
(or (and a (not b))
(and b (not a)))))
(spec/def ::my (spec/and (spec/keys :opt-un [::width ::height])
#(xor? % :width :height)))
我们得到的结果是
(spec/valid? ::my {:width nil})
=> true
(spec/valid? ::my {:width nil :height 5})
=> false
以下 clojure 规范 ::my
允许映射具有键 :width 或键 :height,但它不允许同时具有它们:
(s/def ::width int?)
(s/def ::height int?)
(defn one-of-both? [a b]
(or (and a (not b))
(and b (not a))))
(s/def ::my (s/and (s/keys :opt-un [::width ::height])
#(one-of-both? (% :width) (% :height))))
即使它完成了工作:
(s/valid? ::my {})
false
(s/valid? ::my {:width 5})
true
(s/valid? ::my {:height 2})
true
(s/valid? ::my {:width 5 :height 2})
false
代码对我来说没有那么简洁。首先,键被定义为可选的,然后是必需的。有人对此有更易读的解决方案吗?
clojure.spec 旨在鼓励能够成长的规格。因此它的 s/keys
不支持禁止键。它甚至匹配具有不在 :req
或 opt
中的键的映射。
但是有一种说法是地图必须至少有:width
或:height
,即不是 XOR,只是 OR.
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
此功能内置于规范中 - 您可以在 req-un 中指定 and/or 模式:
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
:user/my
user=> (s/valid? ::my {})
false
user=> (s/valid? ::my {:width 5})
true
user=> (s/valid? ::my {:height 2})
true
user=> (s/valid? ::my {:width 5 :height 2})
true
只是想对原始问题中的规范进行小的修改,如果任何键持有的值是假的,即 false
或 nil
。
(spec/valid? ::my {:width nil})
=> false
当然,如果 int?
对问题中的值施加给定约束,则不会发生这种情况。但也许后代有人允许他们的价值观是 nilable 或布尔值,在这种情况下这个答案变得很方便。
如果我们改为将规范定义为:
(defn xor? [coll a-key b-key]
(let [a (contains? coll a-key)
b (contains? coll b-key)]
(or (and a (not b))
(and b (not a)))))
(spec/def ::my (spec/and (spec/keys :opt-un [::width ::height])
#(xor? % :width :height)))
我们得到的结果是
(spec/valid? ::my {:width nil})
=> true
(spec/valid? ::my {:width nil :height 5})
=> false