Clojure 规范和 "Couldn't satisfy such-that predicate after 100 tries..."
Clojure Spec and "Couldn't satisfy such-that predicate after 100 tries..."
假设您有一个 ::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-history
,它是 unq/gift-pair
的 vector
:
(s/def :unq/gift-history (s/coll-of :unq/gift-pair :kind vector?))
最后,假设您要替换 vector
中的 :unq/gift-pair
之一:
(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)
正确的任何想法?
谢谢。
问题是向量的生成器和集合的索引是 independent/unrelated。随机向量和整数不满足这些条件:
#(< (: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)
这是使用 test.check 的 let
宏,它比 bind
/fmap
更方便,允许您使用代码 combine/compose 生成器看起来像一个普通的 let
。自定义生成器 returns 函数的参数向量。
假设您有一个 ::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-history
,它是 unq/gift-pair
的 vector
:
(s/def :unq/gift-history (s/coll-of :unq/gift-pair :kind vector?))
最后,假设您要替换 vector
中的 :unq/gift-pair
之一:
(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)
正确的任何想法?
谢谢。
问题是向量的生成器和集合的索引是 independent/unrelated。随机向量和整数不满足这些条件:
#(< (: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)
这是使用 test.check 的 let
宏,它比 bind
/fmap
更方便,允许您使用代码 combine/compose 生成器看起来像一个普通的 let
。自定义生成器 returns 函数的参数向量。