QuickCheck:如何组合两台发电机?

muh*_*huk 6 haskell quickcheck frege

我有两个发电机,gen_n&gen_arr:

gen_n :: Gen Int
gen_n = suchThat arbitrary (\i -> i >= 0 && i <= 10)

gen_elem :: Gen Int
gen_elem = suchThat arbitrary (\i -> i >= 0 && i <= 100)

gen_arr :: Gen [Int]
gen_arr = listOf gen_elem 
Run Code Online (Sandbox Code Playgroud)

我如何将这两者结合成一个Gen (Int, [Int])

combine_two_gens :: Gen a -> Gen b -> Gen (a, b)
Run Code Online (Sandbox Code Playgroud)

lod*_*rik 8

(i)你可以使用普通的functorial/monadic组合来组合它们:

gen_comb :: Gen (Int, [Int])
gen_comb = (,) <$> gen_elem <*> gen_arr
Run Code Online (Sandbox Code Playgroud)

(Control.Applicative.liftA2Control.Monad.liftM2也细,当然)

(ii)不要suchThat仅仅限制范围.它可能非常低效,因为它只是在条件满足之前生成随机实例,丢弃其余的实例.相反,你可以使用elements :: [a] -> Gen a:

gen_elem' :: Gen Int
gen_elem' = elements [0..100]

gen_arr' :: Gen [Int]
gen_arr' = listOf gen_elem'

gen_comb' :: Gen (Int, [Int])
gen_comb' = (,) <$> elements [0..100] <*> listOf (elements [0..100])
Run Code Online (Sandbox Code Playgroud)

更新:正如Zeta在下面所说,在这种情况下我们可以做得更好,通过使用choose (0,100)(choose :: Random a => (a, a) -> Gen a)代替elements [0..100].请参阅此处此处获取发电机组合器的完整列表.


*Main> sample gen_arr'
[78]
[2,27]
[12,39]
[92,22,40,6,18,19,25,13,95,99]
...
*Main> sample gen_comb'
(9,[23,3])
(11,[67,38,11,79])
(5,[96,69,68,81,75,14,59,68])
...
Run Code Online (Sandbox Code Playgroud)

suchThat对比elements:

*Main> sample (suchThat arbitrary (\i -> i >= 10000 && i <= 10005))
^CInterrupted.
*Main> sample (elements [10000..10005])
10003
10002
10000
10000
...
Run Code Online (Sandbox Code Playgroud)

suchThat发电机没有输出任何东西.

  • 考虑`choose(0,100)`而不是`elements [0..100]`.如果你的范围不连续或者你的类型没有"随机"实例,那么`elements`很棒,如果你的值在一个封闭的范围内,`choose`通常会更有效. (2认同)