Как создать одно и то же значение для двух разных путей в спецификации?
Я пытаюсь научиться пользоваться 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))
1 ответ
Вы можете сделать это с помощью test.check's 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
и возвращает новый генератор, который применяется f
к каждому значению, сгенерированному из gen
, Здесь мы передаем это генератор по умолчанию для ::parent
и функция, которая берет эти родительские карты и копирует соответствующие ключи в :child
карта.
Если вы хотите, чтобы эта спецификация обеспечивала это равенство (помимо просто генерации), вам нужно добавить s/and
к ::parent
spec с предикатом, чтобы проверить, что:
(s/def ::parent
(s/and (s/keys :req-un [::a ::b ::child])
#(= (select-keys % [:a :b])
(select-keys (:child %) [:a :b]))))
Изменить: вот еще один способ сделать то же самое с 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))))