Clojure Spec и «Не удалось удовлетворить такой-то предикат после 100 попыток ...» - PullRequest
0 голосов
/ 26 июня 2018

Допустим, у вас есть ::givee и ::giver:

(s/def ::givee keyword?)
(s/def ::giver keyword?)

, которые образуют unq/gift-pair:

(s/def :unq/gift-pair (s/keys :req-un [::givee ::giver]))

И тогда у вас есть :unq/gift-historyvector из unq/gift-pair:

(s/def :unq/gift-history (s/coll-of :unq/gift-pair :kind vector?))

Наконец, предположим, что вы хотите заменить один из :unq/gift-pair в vector:

(defn set-gift-pair-in-gift-history [g-hist g-year g-pair]
  (assoc g-hist g-year g-pair))
(s/fdef set-gift-pair-in-gift-history
        :args (s/and (s/cat :g-hist :unq/gift-history
                            :g-year int?
                            :g-pair :unq/gift-pair)
                     #(< (:g-year %) (count (:g-hist %)))
                     #(> (:g-year %) -1))
        :ret :unq/gift-history)

Все работыштраф:

(s/conform :unq/gift-history
           (set-gift-pair-in-gift-history [{:givee :me, :giver :you} {:givee :him, :giver :her}] 1 {:givee :dog, :giver :cat}))
=> [{:givee :me, :giver :you} {:givee :dog, :giver :cat}]

Пока я не попытаюсь stest/check это:

(stest/check `set-gift-pair-in-gift-history)
             clojure.lang.ExceptionInfo: Couldn't satisfy such-that predicate after 100 tries.
java.util.concurrent.ExecutionException: clojure.lang.ExceptionInfo: Couldn't satisfy such-that predicate after 100 tries. {}

Я пытался использовать s/int-in для ограничения числа векторов (думая, что это может быть проблемой) безуспех.

Есть идеи, как правильно запустить (stest/check `set-gift-pair-in-gift-history)?

Спасибо.

1 Ответ

0 голосов
/ 26 июня 2018

Проблема в том, что генераторы для вектора и индекса в коллекции независимы / не связаны.Случайные векторы и целые числа не удовлетворяют этим критериям:

#(< (:g-year %) (count (:g-hist %)))
#(> (:g-year %) -1)

Для check этой функции вы можете предоставить собственный генератор, который будет генерировать случайный вектор :unq/gift-history, и построить другой генератор для индексав зависимости от размера этого вектора:

(s/fdef set-gift-pair-in-gift-history
  :args (s/with-gen
          (s/and
            (s/cat :g-hist :unq/gift-history
                   :g-year int?
                   :g-pair :unq/gift-pair)
            #(< (:g-year %) (count (:g-hist %)))
            #(> (:g-year %) -1))
          #(gen/let [hist (s/gen :unq/gift-history)
                     year (gen/large-integer* {:min 0 :max (max 0 (dec (count hist)))})
                     pair (s/gen :unq/gift-pair)]
             [hist year pair]))
  :ret :unq/gift-history)

Используется макрос let test.check, который удобнее, чем bind / fmap, который позволяет комбинировать / компоновать генераторы, используя кодэто выглядит как обычный let.Пользовательский генератор возвращает вектор аргументов функции.

...