繁体   English   中英

使用Haskell的QuickCheck生成特定长度的列表

[英]Generating a lists of a specific length with Haskell's QuickCheck

-- 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运行时,它会放弃,因为它不会生成足够长的列表。

如何编写生成长度超过随机整数的列表的生成器?

哈马尔的答案完全适合这个问题。 但为了回答所提出的确切问题,我忍不住调查了一下。 让我们一起使用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 smallNumbersample (listLongerThan 3)来确保它生成正确的内容。

走另一条路怎么样? 首先,我们让QuickCheck选择一个列表,然后我们约束我们允许的索引。 这有效,并且不会丢弃任何测试用例。

prop_3a (NonEmpty xs) = forAll (choose (0, length xs - 1)) $ \i ->
    element_at xs i == (xs !! i :: Int)

在这里,我使用forAll为索引使用特定的生成器,在这种情况下使用choose从指定范围中选择一个元素,并且我还使用NonEmptyList类型来确保我们不尝试索引到空列表。

这有效:

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

但是,问题在于丢弃了大量样本值。 您可以使用Positive东西来帮助确保索引有效。

如果你想变得更复杂,你可以使用更多newtype包装器来尝试生成足够长度的值(可能使用sized ,或者一起生成列表和索引:生成列表,然后根据长度生成索引名单)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM