我有这样的递归类型:
type QueryInfo =
{ Title : string
Check : Client -> bool
Positive : Decision
Negative : Decision }
and Decision =
| Result of string
| Query of QueryInfo
我要为FsCheck创建一个生成器。我看过this,我对static member
等方法不感兴趣。我的主要问题是每个领域都有不同的类型。
FsCheck已经可以生成QueryInfo
的值,但由于该类型是递归的,因此生成的值可以变得非常嵌套,以至于生成的值实际上永远不会停止(或者至少)非常慢。
答案 0 :(得分:4)
这样的事情应该有效:
let qi =
let createQi t c p n = {
Title = t
Check = c
Positive = p
Negative = n }
let makeQiGen =
Gen.map4 createQi Arb.generate<string> Arb.generate<Client -> bool>
let resultGen = Arb.generate<string> |> Gen.map Result
let rec qi' size =
if size <= 0
then makeQiGen resultGen resultGen
else
let subQi = qi' (size - 1) |> Gen.map Query
Gen.oneof [
makeQiGen resultGen resultGen
makeQiGen subQi subQi
makeQiGen resultGen subQi
makeQiGen subQi resultGen ]
Gen.sized qi'
本质是防止无限(或非常深)的递归,这是由Gen.sized
完成的。
当size
到达0
时,生成器始终返回一个叶节点 - 即:Positive
和Negative
都是Result
值。
当size
大于0
时,发电机会从四个发电机中选择一个:
Positive
和Negative
Query
值的新节点。Positive
为Result
,但Negative
为Query
。Positive
为Query
,但Negative
为Result
。但是,在每次递归中,size
都会递减,因此最终会返回一个叶子节点。
此答案基于the FsCheck documentation的生成递归数据类型部分。