随着Spec的推出,我尝试为我的所有函数编写test.check生成器。这对简单的数据结构来说很好,但对于具有相互依赖的部分的数据结构往往会变得困难。换句话说,发电机内的一些国家管理部门是需要的。test.check中的循环和状态管理
Clojure循环的生成器等价物/递归或减少已经非常有帮助,因此一次迭代中生成的值可以存储在某个聚合值中,然后在随后的迭代中可以访问该聚合值。
一个简单的例子,其中,这将是必需的,是提供一种用于拆分集合的发生器写入准确X分区,零和Y的元素,并且其中所述元件然后随机分配到任何之间具有每个分区的分区。 (请注意,
test.chuck
的partition
函数不允许指定X或Y)。如果你写这个发电机通过收集循环,那么这将需要访问以前的迭代过程中填补了分区,以避免超过Y.
没有任何人有什么想法?部分的解决方案,我发现:
- test.check的
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)与状态monad结合使用。然而,在Cats 2.0中删除了State monad(因为据说它不适合Clojure),而我知道的其他支持库没有正式的Clojurescript支持。此外,在他自己的图书馆中实施国家monad时,Clojure的monad专家之一Jim Duey似乎警告说,使用State monad与test.check的收缩不兼容(参见http://www.clojure.net/2015/09/11/Extending-Generative-Testing/的底部),这会显着降低使用test.check的优点。
谢谢! (我现在想知道为什么递归的想法不是自然而然地发生在我身上......)你能否扩展你的“破坏缩小的过程”?在我的(当然还是有限的)测试中,包含许多让/绑定的生成器,我似乎对发生的收缩感到高兴。你是否知道任何更可能破坏收缩的具体情况? –
这里描述的问题:http://dev.clojure.org/jira/browse/TCHECK-112 – gfredericks
我还应该澄清,如果你有多个子句,'gen/let'只会导致一个真正的'gen/bind'或者如果身体本身就是发电机。否则它等同于'gen/fmap'。 – gfredericks