-- 3 (find k"th element of a list)
element_at xs x = xs !! x
prop_3a xs x = (x < length xs && x >= 0) ==> element_at xs (x::Int) == (xs !! x::Int)
当prop_3a通过QuickCheck运行时,它会放弃,因为它不会生成足够长的列表。
如何编写一个生成长度超过随机整数的列表的生成器?
答案 0 :(得分:12)
哈马尔的答案完全适合这个问题。但为了回答所提出的确切问题,我忍不住调查了一下。我们使用forAll
。
prop_bang x = x >= 0 ==> forAll (listLongerThan x) $ \xs ->
element_at xs x == xs !! x
所以现在我们需要一个函数listLongerThan :: Int -> Gen [Int]
。它需要一个长度x,并生成一个生成器,它将生成长度大于x
的列表。
listLongerThan :: Int -> Gen [Int]
listLongerThan x = replicateM (x+1) arbitrary
这很简单:我们只是利用Gen
的Monad实例。如果你运行quickCheck prop_bang
,你会发现它开始花了很长时间,因为它开始测试荒谬的长列表。让我们限制列表的长度,使其更快一些。此外,现在listLongerThan
仅生成一个完全x+1
长的列表;让我们再混合一下,再次利用Gen的Monad实例
prop_bang =
forAll smallNumber $ \x ->
forAll (listLongerThan x) $ \xs ->
element_at xs x == xs !! x
smallNumber :: Gen Int
smallNumber = fmap ((`mod` 100) . abs) arbitrary
listLongerThan :: Int -> Gen [Int]
listLongerThan x = do
y <- fmap (+1) smallNumber -- y > 0
replicateM (x+y) arbitrary
您可以在ghci中使用sample smallNumber
或sample (listLongerThan 3)
来确保它生成正确的内容。
答案 1 :(得分:10)
走另一条路怎么样?首先,我们让QuickCheck选择一个列表,然后我们约束我们允许的索引。这有效,并且不会丢弃任何测试用例。
prop_3a (NonEmpty xs) = forAll (choose (0, length xs - 1)) $ \i ->
element_at xs i == (xs !! i :: Int)
在这里,我使用forAll
为索引使用特定的生成器,在这种情况下使用choose
从指定范围中选择一个元素,我还使用the NonEmptyList
type来确保我们不会尝试索引到一个空列表。
答案 2 :(得分:4)
这有效:
import Test.QuickCheck
element_at :: [a] -> Int -> a
element_at xs i = xs !! i
prop_3a :: [Int] -> Int -> Property
prop_3a xs i = (i >= 0) ==> (length xs > i) ==> element_at xs i == xs !! i
然而,问题在于丢弃了 lot 的样本值。您可以使用Positive
之类的东西来帮助确保索引有效。
如果您想要更复杂,可以使用更多newtype包装器来尝试生成足够长度的值(可能使用sized
,或者一起生成列表和索引:生成列表,然后生成基于列表长度的索引。)