如何为规范中的两个不同路径生成相同的值?
How to generate the same value for two different paths in spec?
我正在尝试学习如何将 overrides
与 s/gen
结合使用。
我有一张 ::parent
地图,其中包含一张 ::child
地图。 parent 和 child 都有共同的密钥。 要求是键在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))
您可以使用 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}})
fmap
接受一个函数 f
和一个生成器 gen
,以及 returns 一个新的生成器,它将 f
应用于从 [= 生成的每个值16=]。在这里,我们将 ::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]))))
编辑:这是用 gen/let
做同样事情的另一种方法,允许更像 "natural" 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))))
我正在尝试学习如何将 overrides
与 s/gen
结合使用。
我有一张 ::parent
地图,其中包含一张 ::child
地图。 parent 和 child 都有共同的密钥。 要求是键在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))
您可以使用 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}})
fmap
接受一个函数 f
和一个生成器 gen
,以及 returns 一个新的生成器,它将 f
应用于从 [= 生成的每个值16=]。在这里,我们将 ::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]))))
编辑:这是用 gen/let
做同样事情的另一种方法,允许更像 "natural" 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))))