请在 2024 年 Clojure 状态调查!中分享您的想法。

欢迎!请参阅关于页面以获取更多关于此工作方式的信息。

0 投票
test.check
我正在研读John Hughes的“QuickCheck Testing for Fun and Profit”论文中的某些示例。我希望在那些测试中生成的结构看起来像这样
 
 [:apply `reg [(gen-name) (gen/elements pids)]]

而不是现在必须这样表示

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

我想一个简单的递归函数可以用来生成这些,前提是当前生成器创建一个映射{:gen #<fn...>},我建议将其改为记录,这样我们就可以区分生成器和映射数据结构。

这似乎在Quvig QuickCheck中已经有很好的实现了

bq. 总体而言,Quviq QuickCheck允许任何包含嵌入生成器的数据结构作为相应形状的数据结构的生成器使用——这对用户来说非常方便,但在Haskell中,通常会在数据结构中嵌入生成器导致类型错误。这种技术被纳入了在爱立信编写的生成器中。– 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)))

`

记录生成可能是通常有用的,所以我添加了此功能

`
(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)))))
`

这样我就可以扩展记录,如下所示

(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

我认为第一步是将生成器改为使用记录。有兴趣为这个更改制作补丁吗?

0 投票
_评论由:pangloss_

一个非常简单的补丁,使用Generator记录而不是简单的{:gen ƒ}映射。
0 投票
_评论由:reiddraper_

我已将record-patch应用到ef132b5f85a07879f01417c9104aa6dea771fdb4中。谢谢。我还添加了一个{{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 报告)
...