如何在规范中为两个不同的路径生成相同的值?

Jer*_*eld 1 clojure clojure.spec

我想学习如何使用overridess/gen.

我有一张::parent包含::child地图的地图.父母和孩子都有共同的钥匙.该要求是,按键则有父母和孩子之间,例如相同的值{:a 1 :b 2 :child {:a 1 :b 2}.我知道这似乎是多余的,但问题领域需要它.

下面的代码生成示例,但不满足上述要求.

有没有办法在两个位置使用相同的生成值?

(ns blah
  (:require [clojure.spec.alpha :as s]
            [clojure.spec.gen.alpha :as gen])) 

(s/def ::a (s/int-in 1 5))
(s/def ::b (s/int-in 1 6))

(s/def ::child
  (s/keys :req-un [::a ::b]))

(defn- parent-gen []
  (let [a #(s/gen ::a)
        b #(s/gen ::b)]
    (s/gen ::parent-nogen
           ; overrides map follows
           {::a a ::b b
            ::child #(s/gen ::child
                            ; another overrides map
                            {::a a ::b b})))

(s/def ::parent-nogen
  (s/keys :req-un [::a ::b ::child]))

(s/def ::parent
  (s/with-gen ::parent-nogen parent-gen))

(gen/sample (s/gen ::parent))
Run Code Online (Sandbox Code Playgroud)

Tay*_*ood 5

您可以使用test.check执行此操作fmap:

(s/def ::a (s/int-in 1 5))
(s/def ::b (s/int-in 1 6))
(s/def ::child (s/keys :req-un [::a ::b]))
(s/def ::parent (s/keys :req-un [::a ::b ::child]))
(gen/sample
  (s/gen ::parent
         {::parent ;; override default gen with fmap'd version
          #(gen/fmap
            (fn [{:keys [a b child] :as p}]
              (assoc p :child (assoc child :a a :b b)))
            (s/gen ::parent))}))
=>
({:a 1, :b 2, :child {:a 1, :b 2}}
 {:a 2, :b 2, :child {:a 2, :b 2}}
 {:a 1, :b 1, :child {:a 1, :b 1}}
 {:a 3, :b 2, :child {:a 3, :b 2}}
 {:a 2, :b 4, :child {:a 2, :b 4}}
 {:a 4, :b 4, :child {:a 4, :b 4}}
 {:a 3, :b 3, :child {:a 3, :b 3}}
 {:a 4, :b 4, :child {:a 4, :b 4}}
 {:a 3, :b 4, :child {:a 3, :b 4}}
 {:a 3, :b 4, :child {:a 3, :b 4}})
Run Code Online (Sandbox Code Playgroud)

fmap接受一个函数f和一个生成器gen,并返回一个适用f于生成的每个值的新生成器gen.在这里,我们传递默认生成器::parent,以及一个获取这些父映射并将相应键复制到映射中的函数:child.

如果您希望此规范强制执行该等式(除了生成),您还需要使用谓词添加s/and::parent规范中以检查:

(s/def ::parent
  (s/and (s/keys :req-un [::a ::b ::child])
         #(= (select-keys % [:a :b])
             (select-keys (:child %) [:a :b]))))
Run Code Online (Sandbox Code Playgroud)

编辑:这是另一种做同样事情的方法,gen/let它允许更"自然" let的语法:

(gen/sample
  (gen/let [{:keys [a b] :as parent} (s/gen ::parent)
            child (s/gen ::child)]
    (assoc parent :child (assoc child :a a :b b))))
Run Code Online (Sandbox Code Playgroud)