我想为有序的树状结构生成任意值,其类型为
data Tree a = Leaf | Node (Tree a) a (Tree a)
在保持命令的同时将值插入此树的函数将要求该值应为Ord。但是为了为任意实例生成有序树,我需要在范围“[low,hi]”中生成值,而Ord不足以实现。因此,我还要求该值为Enum,因为Enum允许从给定边界获取值。下面的choose
也需要System.Random.Random:
import Test.QuickCheck.Arbitrary
import System.Random
generateTree :: (Arbitrary a, Ord a, Enum a, Random a) => a -> a -> Gen (Tree a)
generateTree l u = do
genLeaf <- arbitrary
if genLeaf
then return Leaf
else do
x <- choose (l, u)
left <- generateTree l (pred x)
right <- generateTree (succ x) u
return $ Node left x right
所以要将它用于arbitrary
实现,我应该在Arbitrary类中需要相同的类:
instance (Arbitrary a, Ord a, Enum a, Rand.Random a) => Arbitrary (Tree a) where
arbitrary = do
l <- arbitrary
u <- arbitrary
generateTree l u
虽然需求Ord a => Tree a
对于数据结构本身来说已经足够了,但我需要(Arbitrary a, Ord a, Enum a, Rand.Random a) => Tree a
来生成它。它似乎将实现细节泄露到声明中 - 可能我看错了这个方法,我正在学习Haskell。但仍有问题:
Arbitrary (Tree a)
声明其他实例以及一个具有不同要求集的实例,例如仅用于Arbitrary (Tree Int)
的{{1}}?< / LI>
醇>
答案 0 :(得分:4)
由于您需要任意有序树,因此无法摆脱Ord a
实例中的Arbitrary (Tree a)
约束。但是,您不需要Enum
或Random
(回想Arbitrary
正在进行随机值生成 - 所以您自然也不需要随机数)。
以下是我将如何实现Arbitrary实例。
import Test.QuickCheck.Gen
import Test.QuickCheck
arbitraryTree :: (Ord a, Arbitrary a) => Gen (Tree a)
arbitraryTree = do
x <- arbitrary
y <- suchThat arbitrary (>= x)
go x y
where
go mn mx = frequency [(1, return Leaf), (1, arbNode)] where
arbNode = do
a <- suchThat arbitrary (\x -> x >= mn && x <= mx)
l <- go mn a
r <- go a mx
return (Node l a r)
instance (Ord a, Arbitrary a) => Arbitrary (Tree a) where
arbitrary = arbitraryTree
请注意,如果在一定次数的尝试中找不到合适的值,则可以使用suchThatMaybe
并提供默认值,从而使其更可靠。
关于你的第二个问题,你确实可以拥有以下实例:
{-# LANGUAGE OverlappingInstances, FlexibleInstances #-}
instance Arbitrary (Tree a)
instance Arbitrary (Tree Int)
并且仅当Tree a
不是a
时,编译器才会选择Int
实例。但是,我建议不要这样做 - 如果您主要对测试Tree Int
或甚至一小组Tree
类型感兴趣,那么只需拥有这些类型的实例 - Tree a
没有通用实例。
顺便说一下,我上面给出的生成器不是很好 - 它只产生Leaf
一半的时间。原则上,我希望像这样的类型的生成器始终生成“中等”大小的树。实现这一点的一种简单方法是最初生成Node
的概率很高,并随着树的增长而稳定地降低概率:
arbitraryTree :: (Ord a, Arbitrary a) => Float -> Float -> Gen (Tree a)
arbitraryTree c' f = do
x <- arbitrary
y <- suchThat arbitrary (>= x)
go x y c'
where
go mn mx c = frequency [(1000-q, return Leaf), (q, arbNode)] where
q = round (1000 * c)
arbNode = do
a <- suchThat arbitrary (\x -> x >= mn && x <= mx)
l <- go mn a (c * f)
r <- go a mx (c * f)
return (Node l a r)
instance (Ord a, Arbitrary a) => Arbitrary (Tree a) where
arbitrary = arbitraryTree 0.9 0.9
您可以使用arbitraryTree
的参数来获取您喜欢的树。