の実装を提供していただけると助かりますが、次の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))
Control.Arrowsecond
から
使用します。オリジナルは次のように簡単に定義できます。gen
genWitness:
gen' :: a -> ([a] -> [a]) -> ([a] -> Bool) -> a
gen' init next stop = fst (genWitness init next stop)
遅延評価のおかげで、オーバーヘッドはあまり発生しません。物件に戻ります!QuickCheck から生成された関数を表示できるようにするには、モジュール
Test.QuickCheck.Functionを使用します。ここでは厳密に必要というわけではありませんが、良い習慣として、プロパティをモノモーフィズします: Int
s をユニット リストにするモノモーフィズム制限を許可する代わりに、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
何かがループしているようです... はい、もちろん:リストにある fromが neverの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
2 番目の関数は引数next
で、空のリストを返す場合、ループ インは空のリストを返しgen
ますnext
。
これがこの質問の答えであり、QuickCheck を使用して高階関数をテストする方法についての洞察が得られることを願っています。