1

如果我有一个像

(clojure.spec/def ::person (clojure.spec/keys :req [::name ::address] :opt [::age]))

当我这样做时

(clojure.spec.gen/generate (clojure.spec/gen ::person))

有没有办法告诉生成器在为其生成数据时始终考虑可选键?

我知道这可以通过自定义生成器来完成,但我想知道是否有可用的功能,或者可能是一种更简单的方法,它不涉及我定义自定义生成器。

4

2 回答 2

3

我认为对您的问题的简短回答是“否”,但您可以s/merge使用需要可选键的规范:

(s/def ::name string?)
(s/def ::age pos-int?)
(s/def ::person (s/keys :req [::name] :opt [::age]))

(gen/sample (s/gen ::person)) ;; ::age not always gen'd
(gen/sample                   ;; ::age always gen'd
  (s/gen (s/merge ::person (s/keys :req [::age]))))

您可以编写一个宏来生成一个s/keys带有生成器的规范来执行此操作。

于 2018-04-12T20:20:51.063 回答
0

我的方法是遍历该规范的表单(使用clojure.spec.alpha/form),如果使用创建规范,则将可选键合并到所需键中clojure.spec.alpha/keys,最后重新生成规范。

(defn merge-opt-keys
  "Merges optional keys into requried keys (for specs which are created using `clojure.spec.alpha/keys`) using a spec's form/description"
  [fspec]
  (let [keymap (into {} (map (fn [pair] (vec pair)) (partition 2 (rest fspec))))]
    (->> (cond-> {}
           (contains? keymap :opt)
             (assoc :req (vec (concat (keymap :req) (keymap :opt))))
           (contains? keymap :opt-un)
             (assoc :req-un (vec (concat (keymap :req-un) (keymap :opt-un)))))
         (mapcat identity)
         (cons 'clojure.spec.alpha/keys))))

(clojure.spec.alpha/def ::name string?)
(clojure.spec.alpha/def ::desc string?)
(clojure.spec.alpha/def ::book (clojure.spec.alpha/keys :req [::name] :opt [:desc]))

(clojure.spec.gen.alpha/generate (clojure.spec.alpha/gen (eval (merge-opt-keys (clojure.spec.alpha/form ::book)))))
于 2018-04-13T00:02:48.283 回答