【问题标题】:FsCheck custom generator of lists satisfying a predicateFsCheck 满足谓词的列表的自定义生成器
【发布时间】:2019-03-23 05:52:59
【问题描述】:

对于给定的谓词pred : 'a list -> bool 和生成器gen : Gen<'a>,考虑以下满足谓词的格式良好的列表生成器:

let wellFormedList pred gen =
   Gen.ofList gen
   |> Gen.filter pred 

正如FsCheck manual 中提到的,谓词应该很有可能适用于随机列表。 不幸的是,这个假设不适用于我的情况。因此,我需要为满足谓词的列表定义一个自定义生成器。

如何定义一个从空列表开始并使用新的随机元素扩展它的自定义生成器,直到列表满足谓词?

我可能需要对生成器使用计算表达式gen { },但我不知道如何。

PS:我知道,与 wellFormedList 的原始实现不同,这种自定义生成器的分布在满足谓词的所有列表中并不统一。

【问题讨论】:

  • 我们可以从您的问题中假设不满足谓词的列表只需要添加更多随机元素,直到它满足?没有必要扔掉一个不满足谓词的列表,我们应该给它附加更多的元素?
  • 是的,没错。

标签: f# generator fscheck


【解决方案1】:

好吧,如果我理解正确,我认为应该这样做:

let wellFormedList (predicate:'a list -> bool) (myGen:Gen<'a>) = 
  gen {
    let! initialList = Gen.listOf myGen
    let mutable myList = initialList
    while not <| predicate myList do
      let! newVal = myGen
      myList <- (newVal :: myList)

    return myList      
  }

或者如果你想要一个递归函数:

let wellFormedList (predicate:'a list -> bool) (myGen:Gen<'a>) = 
  gen {
    let rec addIfNecessary listSoFar =
      if predicate listSoFar 
      then gen { return listSoFar }
      else gen { 
        let! newVal = myGen
        return! addIfNecessary (newVal :: listSoFar)
      }

    let! initialList = Gen.listOf myGen
    return! addIfNecessary initialList
  }

我还没有检查给你什么样的分布。当然,如果列表永远不会收敛于通过谓词的形式,您将面临无限循环(或堆栈溢出)的风险。

【讨论】:

  • 谢谢,这就是我要找的。代码有bug:请在while循环和return语句中将initialList改为myList
  • 您能解释一下let!中感叹号的用途吗?
  • 当然!所以在计算表达式中的任何地方(这里的 gen { } 块),如果你说 let! x = a,如果 aGen&lt;int&gt;let! “展开”它,所以 x 将只是 @987654332 @。一般来说,如果有感叹号,右侧的东西必须是“包装”的东西。 (这就是为什么我在返回已经是Gen&lt;_&gt; 的东西时使用return!,但在返回不是Gen&lt;_&gt; 的东西时只是简单的return)。
  • 所以在let! initialList = Gen.listOf myGen 中,= 的右侧是Gen&lt;'a list&gt;,而initialList 只是普通的'a list。在let! newVal = myGen 中,myGenGen&lt;'a&gt;,但newVal 只是普通的'a
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-03
  • 2020-01-06
  • 2013-07-28
  • 2012-04-28
  • 2019-08-20
  • 2016-07-21
  • 1970-01-01
相关资源
最近更新 更多