假设我有一个基于System.Random
的生成器,我想把它变成一个FsCheck生成器:
let myGen = MyGen(System.Random())
let fsGen = gen { return myGen.Generate() }
这个简单的解决方案存在一些问题: 首先是忽略大小的概念;我认为这不是一个大问题,许多发电机忽略了它的大小。 另一个问题影响再现性,因为FsCheck发生器是引擎盖下的纯函数,随机性 仅由测试运行器中的采样机构提供。 (this answer)清楚地解释了这一点。
现在,解决方案可能是:
let fsGen =
gen {
let! seed = Gen.choose(0, System.Int32.MaxValue)
let myGen = MyGen(System.Random(seed))
return myGen.Generate() }
但是存在性能损失,因为我每次都必须创建MyGen
的新实例(初始化成本可能很高)
有更好的方法吗?
答案 0 :(得分:4)
以下工作可以吗?尽管MyGen
本质上是随机的,但你可以通过修复种子来确定它:
let deterministicGen = MyGen(Random(42))
由于Random
的性质,这不能保证具有足够随机的分布,但如果它适用于您的目的,您可以创建由{{1}生成的确定性值序列}:
MyGen
这只有100个值,但根据您的需要,您可以创建一个更大的样本集1000,或者甚至10000个值。
就像let deterministicValues = List.init 100 (fun _ -> deterministicGen.Generate())
一样,deterministicGen
是固定的:它是由deterministicValues
生成的值列表。
您可以轻松地让FsCheck随机选择此列表中的值:
MyGen
此处let fsGen = Gen.elements deterministicValues
为fsGen
,其中Gen<'a>
为'a
返回的任何内容。