Как создать одно и то же значение для двух разных путей в спецификации? - PullRequest
0 голосов
/ 30 сентября 2018

Я пытаюсь научиться использовать 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 Ответ

0 голосов
/ 30 сентября 2018

Вы можете сделать это с помощью 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 и возвращает новый генератор, который применяет 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]))))

Изменить: вот еще один способ сделать то же самое с 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))))
...