任意のツリーが与えられた場合、シューベルトの番号付けを使用して、そのツリーに対してサブタイプの関係を構築できます。
constructH :: Tree a -> Tree (Type a)
whereType
は元のラベルをネストし、さらに子/親 (またはサブタイプ) チェックを実行するために必要なデータを提供します。Schubert Numbering では、2 つの Int パラメータで十分です。
data Type a where !Int -> !Int -> a -> Type a
これは二項述語につながります
subtypeOf :: Type a -> Type a -> Bool
ここで、QuickCheck を使用して、これが実際にやりたいことを実行することをテストしたいと思います。ただし、次のプロパティは機能しません。QuickCheck があきらめるだけだからです。
subtypeSanity ∷ Tree (Type ()) → Gen Prop
subtypeSanity Node { rootLabel = t, subForest = f } =
let subtypes = concatMap flatten f
in (not $ null subtypes) ==> conjoin
(forAll (elements subtypes) (\x → x `subtypeOf` t):(map subtypeSanity f))
への再帰呼び出しsubtypeSanity
、つまり に渡すリストの末尾を省略したconjoin
場合、プロパティは問題なく実行されますが、ツリーのルート ノードだけがテストされます。QuickCheck が新しいテスト ケースの生成をあきらめずに、データ構造に再帰的に降りるにはどうすればよいですか?
必要に応じて、シューベルト階層を構築するコードと のArbitrary
インスタンスTree (Type a)
を提供して、完全に実行可能な例を提供できますが、それはかなりの量のコードになります。私は、QuickCheck を「取得」していないだけでなく、ここで間違った方法で使用していると確信しています。
編集: 残念ながら、sized
関数はここで問題を解決していないようです。最終的に同じ結果になります(J. Abrahamsonの回答へのコメントを参照してください)。
編集 II: 再帰的なステップを回避し、conjoin
. ツリー内のすべてのノードのリストを作成し、それらの単一ノード プロパティ (最初から正常に機能していました) をテストします。
allNodes ∷ Tree a → [Tree a]
allNodes n@(Node { subForest = f }) = n:(concatMap allNodes f)
subtypeSanity ∷ Tree (Type ()) → Gen Prop
subtypeSanity tree = forAll (elements $ allNodes tree)
(\(Node { rootLabel = t, subForest = f }) →
let subtypes = concatMap flatten f
in (not $ null subtypes) ==> forAll (elements subtypes) (\x → x `subtypeOf` t))
木のインスタンスを微調整してArbitrary
も機能しませんでした。私がまだ使用している任意のインスタンスは次のとおりです。
instance (Arbitrary a, Eq a) ⇒ Arbitrary (Tree (Type a)) where
arbitrary = liftM (constructH) $ sized arbTree
arbTree ∷ Arbitrary a ⇒ Int → Gen (Tree a)
arbTree n = do
m ← choose (0,n)
if m == 0
then Node <$> arbitrary <*> (return [])
else do part ← randomPartition n m
Node <$> arbitrary <*> mapM arbTree part
-- this is a crude way to find a sufficiently random x1,..,xm,
-- such that x1 + .. + xm = n, for any n, m, with 0 < m.
randomPartition ∷ Int → Int → Gen [Int]
randomPartition n m' = do
let m = m' - 1
seed ← liftM ((++[n]) . sort) $ replicateM m (choose (0,n))
return $ zipWith (-) seed (0:seed)
問題は「今のところ解決した」と考えていますが、誰かが再帰的なステップやconjoin
QuickCheck をあきらめさせた理由を説明してくれたら (「0 個の」テストのみを通過した後)、感謝します。