Хотя было бы полезно, если бы вы дали реализацию 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 .
Хотя это не является строго необходимым здесь, хорошая привычка
мономорфизировать свойство: мы используем списки Int
s вместо разрешения
ограничение мономорфизма делает их списками единиц. Давайте теперь заявим
собственность:
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.