为递归记录制作FsCheck生成器

时间:2015-11-23 21:24:24

标签: f#

我有这样的递归类型:

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的值,但由于该类型是递归的,因此生成的值可以变得非常嵌套,以至于生成的值实际上永远不会停止(或者至少)非常慢。

1 个答案:

答案 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时,生成器始终返回一个叶节点 - 即:PositiveNegative都是Result值。

size大于0时,发电机会从四个发电机中选择一个:

  • 生成叶子的
  • 生成新PositiveNegative Query值的新节点。
  • 其中PositiveResult,但NegativeQuery
  • 其中PositiveQuery,但NegativeResult

但是,在每次递归中,size都会递减,因此最终会返回一个叶子节点。

此答案基于the FsCheck documentation生成递归数据类型部分。