2

我想为具有特定键存在规则的映射创建一个 clojure 规范。

地图必须有一个:type并且可以有一个:default:value但不能同时有。我试过了:

(s/def ::propertyDef
  (s/keys :req [::type (s/or ::default ::value) ] :opt [::description ::required]))

但我得到了

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) 

但是or给了我一个错误,因为它的格式错误。我不得不承认对s/or.

4

1 回答 1

6

首先:您用于在所需键列表中s/or指定 a::default或 a ::values/or需要:label spec成对,而您只提供规格本身,这是错误的原因。

要解决,只需使用or

(s/def ::propertyDef (s/keys :req [::type (or ::default ::value)]
                             :opt [::description ::required]))

这允许::default::value都出现在地图中,但这几乎总是可以的。实际使用地图的代码可以简单地检查是否存在::value并使用它,如果它不存在,则使用::default(或任何你的逻辑发生)。这通常是这样完成的:

(let [myvalue (or (::value mymap) (::default mymap))] ...)

地图中可能有数千个键,这不会影响您提取所需键的能力。这就是为什么规范没有提供一种内置方法来指定不应该在映射中的键,只提供指定哪些键应该存在的方法(即,:req:req-unin s/keys)。想想大多数 http 服务器是如何工作的:你可以给它们一些无意义的头部键和值,但它们不会拒绝为请求提供服务;他们只是忽略它们并返回响应。

因此,您可能不需要强制只存在一个或另一个,但如果必须,您可以定义一个独占或函数:

(defn xor
  [p q]
  (and (or p q)
       (not (and p q))))

然后将其添加为规范的附加谓词:

(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
于 2017-03-01T12:26:15.817 回答