我有以下数据类型(如列表):
data List a =
Nil
| Cons a (List a)
deriving (Eq, Show)
并为Monoid
创建了一个实例:
instance Monoid (List a) where
mempty = Nil
mappend Nil ys = ys
mappend (Cons x xs) ys = Cons x (mappend xs ys)
我尝试为Arbitrary
编写一个实例来测试后者,但无法完成它:
instance Arbitrary a => Arbitrary (List a) where
arbitrary = do
请帮我完成这项功能。
答案 0 :(得分:2)
我们将其分为两个功能。首先,我们生成一个固定长度列表的函数:
vectorList :: (Arbitrary a) => Int -> Gen (List a)
vectorList n = if n <= 0
then return Nil
else Cons <$> arbitrary <*> vectorList (n - 1)
现在我们可以生成固定大小的仲裁列表,我们可以使用sized
生成列表:
instance Arbitrary a => Arbitrary (List a) where
arbitrary = sized vectorList
这确保我们启用QuickCheck来选择列表的大小。通常,QuickCheck会在测试中首先选择小尺寸参数并增加尺寸。如果您要选择随机大小,您可能会创建一个非常长的列表,其中一个小列表已足以检查您的测试用例。
请注意,vectorList
只适用于您的数据类型replicateM n arbitrary
。如果您提供fromList :: [a] -> List a
,则可以将vectorList
更改为
vectorList n = fromList (replicateM n arbitrary)
但是你可以使用
instance Arbitrary a => Arbitrary (List a) where
arbitrary = fmap fromList arbitrary
答案 1 :(得分:1)
这个怎么样?
replicateM 0 action = Nil
replicateM n action = Cons <$> action <*> replicateM (n-1) action
instance Arbitrary a => Arbitrary [a] where
arbitrary = do
length <- getSize
replicateM length arbitrary
此replicateM
版本与the standard version类似,不同之处在于它返回List
而不是[]
。
答案 2 :(得分:1)
使用oneof函数怎么样?
instance Arbitrary a => Arbitrary (List a) where
arbitrary = oneof [pure Nil, Cons <$> arbitrary <*> arbitrary]
它将生成空值Nil
或非空列表。 oneof
获取Gen a
值列表,这就是pure
和<$>
需要<*>
将值包装到Gen
所需的原因。 <{1}}和return
也可以使用。
在定义liftM2
之后快速检查一个简单的测试:
instance
顺便说一下,在大多数情况下prop_nil_mappend :: List Int -> Bool
prop_nil_mappend xs = mappend Nil xs == xs
main :: IO ()
main = quickCheck prop_nil_mappend
没有非空列表那么有用,所以你可以定义每个列表的生成频率:
Nil