如果您提供gen 的实现会有所帮助,但我是
猜测是这样的:
gen :: a -> ([a] -> [a]) -> ([a] -> Bool) -> a
gen init next stop = loop [init]
where
loop xs | stop xs = head xs
| otherwise = loop (next xs)
您要测试的属性是 next 从未提供过
空列表。测试这个的一个障碍是你想检查一个
gen 内部的内部循环不变,因此需要从
外。让我们修改gen 以返回此信息:
genWitness :: a -> ([a] -> [a]) -> ([a] -> Bool) -> (a,[[a]])
genWitness init next stop = loop [init]
where
loop xs | stop xs = (head xs,[xs])
| otherwise = second (xs:) (loop (next xs))
我们使用second 来自
Control.Arrow。
原来的gen很容易用genWitness:定义
gen' :: a -> ([a] -> [a]) -> ([a] -> Bool) -> a
gen' init next stop = fst (genWitness init next stop)
由于惰性评估,这不会给我们带来太多开销。回到
财产!要启用从 QuickCheck 显示生成的函数,
我们使用模块
Test.QuickCheck.Function。
虽然这里不是绝对必要的,但一个好习惯是
单态属性:我们使用Ints 列表而不是允许
使它们成为单元列表的单态性限制。现在让我们声明
属性:
prop_gen :: Int -> (Fun [Int] [Int]) -> (Fun [Int] Bool) -> Bool
prop_gen init (Fun _ next) (Fun _ stop) =
let trace = snd (genWitness init next stop)
in all (not . null) trace
让我们尝试使用 QuickCheck 运行它:
ghci> quickCheck prop_gen
某些东西似乎在循环...当然是的:gen 循环如果 stop 在
来自next 的列表永远不会是True!让我们尝试查看输入跟踪的有限前缀
而是:
prop_gen_prefix :: Int -> (Fun [Int] [Int]) -> (Fun [Int] Bool) -> Int -> Bool
prop_gen_prefix init (Fun _ next) (Fun _ stop) prefix_length =
let trace = snd (genWitness init next stop)
in all (not . null) (take prefix_length trace)
我们现在很快得到一个反例:
385
{_->[]}
{_->False}
2
第二个函数是参数next,如果返回的是空列表,
那么gen 中的循环将给next 一个空列表。
我希望这能回答这个问题,并为您提供一些见解
了解如何使用 QuickCheck 测试高阶函数。