2024 Clojure状态调研! 分享您的想法。

欢迎!请参阅关于页面获取更多关于如何使用本站的信息。

0
test.check
我在研究John Hughes的"QuickCheck 测试:乐趣与盈利"论文中的示例。我希望在测试中生成的结构看起来像这样
 
 [:apply `reg [(gen-name) (gen/elements pids)]]

而不是现在必须表达的方式

 (gen/tuple (gen/return :apply) (gen/return `reg) (gen/tuple (gen-name) (gen/elements pids)))

我认为可以使用一个简单的递归函数来生成这些,条件是当前生成器创建一个包含函数的map {:gen #<fn...>},我建议将其更改为record,以便我们可以区分生成器和map数据结构。

这在Quvig QuickCheck中已经很好地实现了

bq. 在一般的情况下,Quviq QuickCheck 允许任何包含嵌入生成器的数据结构作为该形状的数据结构的生成器使用——这对用户来说非常方便,但在Haskell中,将生成器嵌入到数据结构中通常会导致类型错误。这种技术被用于Ericsson编写的所有生成器中。 -- Hughes

9 答案

0

评论者:pangloss

在今天玩 REPL 时,我想出了以下代码,可以将任意字面量转换为生成器

`
(defprotocol LiteralGenerator
(-literal->generator [literal]))

(defn literal->generator [literal]
(cond

(satisfies? LiteralGenerator literal) (-literal->generator literal)
(vector? literal) (apply gen/tuple (mapv literal->generator literal))
(and (map? literal) (= [:gen] (keys literal)) (fn? (:gen literal))) literal
(map? literal) (gen/fmap (partial into {}) (literal->generator (mapv vec literal)))
(set? literal) (gen/fmap set (literal->generator (vec literal)))
(list? literal) (gen/fmap (partial apply list) (literal->generator (vec literal)))
:else (gen/return literal)))

`

从record生成可能是一般有用的,所以我将其添加作为特性

`
(defn record->generator
([record]
; 有更好的方法来完成这个吗?
(let [ctor (eval (read-string (str "map->" (last (clojure.string/split (str (class record)) #"\.")))))]

 (record->generator ctor record)))

([ctor record]
(gen/fmap ctor (literal->generator (into {} record)))))
`

这让我可以扩展record,就像这样

(defrecord Foo [a b] LiteralGenerator (-literal->generator [this] (record->generator map->AbcDef this)))

我还没有考虑把这段代码归类到test.check的可能性。在进一步研究之前,我想知道大家对目前所看到的意见。

0

评论者:reiddraper

因此,我对这个问题有一些矛盾的感觉。我同意这样很方便,但我又担心这种宽松的做法可能会让更多的错误发生,并且不会强迫用户区分值和生成器。例如,如果你这样写:{{[int int]}},你是指〔gen/int gen/int〕,还是你真的想写〔(gen/return int) (gen/return int)〕?如果任何期望生成器的函数也可以接受值,我们就不能再给函数添加错误检查来确保你传入了正确的生成器。同样,我也看到了使用数据结构字面量的好处。如果我们要写一个函数,必须用这个语法创建生成器,类似这样:{{(gen/literal [:age gen/int])}}。这样,你可以在gen/literal的作用域内选择这个语法。

0

评论者:pangloss

我同意这是值得关注的问题,而且由于你无法保证看到生成器的输出,所以我认为最好在使用gen/literal时明确指出。这仍然是一个简洁的解决方案。幸运的是,这正是我所写的!我们只需将literal->generator重命名为{{literal}},并安装到clojure.test.check.generators命名空间中。

0

评论者:reiddraper

我认为首先要做的是将生成器改为使用Record。你对进行这个修改的patches有兴趣吗?

0
_评论者:pangloss_

一个非常简单的patches,使用生成器record而不是简单的{:gen ƒ) map。
0
_评论者:reiddraper_

我已经应用ef132b5f85a07879f01417c9104aa6dea771fdb4的record-patch。谢谢。我还添加了一个{{generator?}}辅助函数。
0
by

评论者:ppotter

我发现了一些似乎相关的内容:ztellman/collection-check 定义了一个(链接:https://github.com/ztellman/collection-check/blob/07ee38e780d54088751dd4834ef9a30866ac5e2d/src/collection_check.clj#L16-L22 文本:tuple*)fn,它与 gen/tuple 类似,但自动将字面量用 gen/return 包装。

值得注意的是,尽管它不能解决通用情况,但这将解决问题描述中的示例。

0
by

评论者:gfredericks

现在有一个针对此目的的库:https://github.com/holguinj/jen

0
by
参考:https://clojure.atlassian.net/browse/TCHECK-19(报告人:pangloss)
...