Jer*_*eld 1 clojure clojure.spec
我想学习如何使用overrides与s/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)
您可以使用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)