Clojure 将集合指定为每个值中正好有一个

Clojure 将集合指定为每个值中正好有一个,clojure,clojure.spec,Clojure,Clojure.spec,拥有这张地图: {:id 5 :fields [{:field :a :val 1} {:field :c :val 3} {:field :b :val 2} {:field :d :val 4} ...]} 以及每个字段值的单独规格,如(s/def::field-a…,(s/def::field-b…,(s/def::field-c… 我应该如何规范它,使映射中的:fields包含{:field a…},{:

拥有这张地图:

{:id 5
 :fields [{:field :a :val 1}
          {:field :c :val 3}
          {:field :b :val 2}
          {:field :d :val 4}
          ...]}
以及每个字段值的单独规格,如
(s/def::field-a…
(s/def::field-b…
(s/def::field-c…


我应该如何规范它,使映射中的
:fields
包含
{:field a…}
{:field b…}
{:field c…}
(忽略顺序)和可选的其他值中的每一个(并且正好一个)?

是的,我知道它有点大,但无论如何:

(s/def ::id int?)
(s/def ::val int?)
(s/def ::field keyword?)
(s/def ::some string?)

(s/def ::abstract-field (s/keys :req-un [::field ::val]))

(s/def ::field-a (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :a)))
(s/def ::field-b (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :b)))
(s/def ::field-c (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :c)))
(s/def ::field-d (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :d)))

(s/def ::fields (s/and
                    (s/+ (s/alt :required (s/or :a ::field-a
                                                :b ::field-b
                                                :c ::field-c
                                                :d ::field-d)
                                :rest ::abstract-field))
                    (fn [x] (= [:a :b :c :d]
                               (into [] (sort (map (comp first second) (filter #(= :required (first %)) x))))))))

(let [x1 [{:field :b :val 2}
          {:field :a :val 1}
          ;{:field :a :val 1}
          {:field :d :val 4}
          {:field :e :val 4}
          {:field :c :val 3}
          ]]
    (s/explain ::fields x1)
    (s/conform ::fields x1))

你的问题很可能是这样回答的:该死,这是一个大规格我希望有一些简洁的解决方案,但我想没有别的办法了。非常感谢你!