随着Spec的引入,我尝试为我的所有功能编写test.check生成器。对于简单的数据结构来说,这是很好的,但是对于具有相互依赖的部分的数据结构来说,这会变得很困难。换句话说,需要在生成器中进行一些状态管理。
它已经极大地帮助了生成器--相当于Clojure的循环/重复或约简,这样在一个迭代中产生的值就可以存储在某个聚合的值中,然后在后续的迭代中访问。
需要这样做的一个简单示例是编写一个生成器,用于将集合分割成精确的X分区,每个分区在零元素和Y元素之间,然后将元素随机分配给任何分区。(请注意,
test.chuck的partition函数不允许指定X或Y)。 如果您通过循环遍历集合来编写这个生成器,那么这将需要访问以前迭代期间填充的分区,以避免超过Y。
有人有什么想法吗?我发现了部分解决办法:
let和bind允许您生成一个值,然后在稍后重用该值,但它们不允许迭代。tuple和bind函数组合遍历以前生成的值的集合,但是这些迭代不能访问以前迭代期间生成的值。
(defn bind-each [k coll] (apply tcg/tuple (map (fn [x] (tcg/bind (tcg/return x) k)) coll))reset!原子/易失性元素,以避免它们的内容在生成器的下一个调用中被重用。bind和return功能类似于monad,这意味着使用monad库(如Cats和State )。然而,在Cats 2.0中删除了State monad (因为据称它不适合Clojure),而我所知道的其他支持库没有正式的Clojurescript支持。此外,当在自己的库中实现一个State时,Jim -Clojure的monad专家之一--似乎警告说,State monad的使用与test.check的收缩(参见http://www.clojure.net/2015/09/11/Extending-Generative-Testing/底部)不兼容,这大大降低了使用test.check的优点。发布于 2016-10-31 13:45:24
您可以通过将gen/let (或等效的gen/bind)与显式递归相结合来完成所描述的迭代:
(defn make-foo-generator
[state]
(if (good-enough? state)
(gen/return state)
(gen/let [state' (gen-next-step state)]
(make-foo-generator state'))))无论如何,如果可能的话,尝试避免这种模式是值得的,因为每次使用let/bind都会破坏收缩过程。有时,可以使用gen/fmap重新组织生成器。例如,要将集合划分为X子集序列(我意识到这并不完全是您的示例,但我认为可以对其进行调整以适应),您可以这样做:
(defn partition
[coll subset-count]
(gen/let [idxs (gen/vector (gen/choose 0 (dec subset-count))
(count coll))]
(->> (map vector coll idxs)
(group-by second)
(sort-by key)
(map (fn [[_ pairs]] (map first pairs))))))https://stackoverflow.com/questions/40337996
复制相似问题