mme*_*mer 2 clojure clojure.spec
我想为地图创建一个 clojure 规范,该地图具有有关特定键存在的规则。
地图必须有一个:type并且可以有一个:default或:value两个但不能同时有。我试过:
(s/def ::propertyDef
(s/keys :req [::type (s/or ::default ::value) ] :opt [::description ::required]))
Run Code Online (Sandbox Code Playgroud)
但我得到了
CompilerException java.lang.AssertionError: Assert failed:
spec/or expects k1 p1 k2 p2..., where ks are keywords
(c/and (even? (count key-pred-forms)) (every? keyword? keys)),
compiling:(C:\Users\MartinRoberts\AppData\Local\Temp\form-init4830956164341520551.clj:1:22)
Run Code Online (Sandbox Code Playgroud)
但是or给了我一个错误,因为它的格式错误。我不得不承认并没有真正理解s/or.
首先:您正在使用在所需键列表中s/or指定 a::default或 a ::value。 s/or需要:label spec成对,而您只提供规格本身,这是导致错误的原因。
要解决,只需使用or:
(s/def ::propertyDef (s/keys :req [::type (or ::default ::value)]
:opt [::description ::required]))
Run Code Online (Sandbox Code Playgroud)
这允许::default和::value出现在地图中,但这几乎总是可以的。实际使用地图的代码可以简单地检查是否存在::value并使用它,如果它不存在,则使用::default(或任何您的逻辑恰好是)。这通常是这样完成的:
(let [myvalue (or (::value mymap) (::default mymap))] ...)
Run Code Online (Sandbox Code Playgroud)
映射中可能有数千个键,这不会影响您提取所需键的能力。这就是为什么 spec 没有提供一种内置的方法来指定不应该出现在地图中的键,而只提供了指定哪些键应该存在的方法(即,:req和:req-unin s/keys)。想一想大多数 http 服务器是如何工作的:您可以为它们提供无意义的标头键和值,但它们不会拒绝为请求提供服务;他们只是忽略它们并返回响应。
因此,您可能不需要强制只存在一个或另一个,但如果必须,您可以定义一个exclusive or 函数:
(defn xor
[p q]
(and (or p q)
(not (and p q))))
Run Code Online (Sandbox Code Playgroud)
然后将其添加为规范的附加谓词:
(s/def ::propertyDef (s/and (s/keys :req [::type (or ::default ::value)]
:opt [::description ::required])
#(xor (::default %) (::value %))))
(s/valid? ::propertyDef {::type "type" ::default "default"})
=> true
(s/valid? ::propertyDef {::type "type" ::value "value"})
=> true
(s/valid? ::propertyDef {::type "type" ::default "default" ::value "value"})
=> false
Run Code Online (Sandbox Code Playgroud)