Хотя было бы полезно, если бы вы дали реализацию 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
вместо того, чтобы разрешить ограничение мономорфизма, превращающее их в единичные списки. Сформулируем свойство:
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.
person
danr
schedule
13.03.2012
next
будет производить непустой вывод. Возможно, вам будет интересно протестировать это свойство вместо или в дополнение к упомянутому вами свойству. - person John L   schedule 13.03.2012next
, а неgen
, аnext
- это свойство первого порядка, поэтому я знаю, как это проверить. - person Norman Ramsey   schedule 15.03.2012