以下clojure规范::my
允许使用密钥:width或key: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
代码看起来并不简洁。首先,将键定义为可选,然后根据需要定义。有没有人对此有更可读的解决方案?
答案 0 :(得分:7)
clojure.spec旨在鼓励能够增长的规格。因此它的s/keys
不支持禁止密钥。它甚至匹配具有既不在:req
也不在opt
的密钥的地图。
但是有一种方法可以说地图必须至少拥有:width
或 :height
,即不是XOR,只是OR。< / p>
(s/def ::my (s/keys :req-un [(or ::width ::height)]))
答案 1 :(得分:4)
此功能内置于规范中 - 您可以在req-un中指定和/或模式:
(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
答案 2 :(得分:0)
只是想在原始问题中对规范进行一些小修改,如果任何键所持有的值都是假的,即逻辑将失败,即//String line = inputStream.next();
或false
。
nil
当然,(spec/valid? ::my {:width nil})
=> false
给定问题中的值的约束不会发生这种情况。但也许后人中的某些人允许他们的值为nilable或boolean,在这种情况下,这个答案会变得很方便。
如果我们将规范定义为:
int?
我们得到
的结果(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)))