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

Joe*_*Dyk 14 haskell 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)
Run Code Online (Sandbox Code Playgroud)

当prop_3a通过QuickCheck运行时,它会放弃,因为它不会生成足够长的列表.

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

Dan*_*ton 12

哈马尔的答案完全适合这个问题.但为了回答所提出的确切问题,我忍不住调查了一下.我们来使用吧forAll.

prop_bang x = x >= 0 ==> forAll (listLongerThan x) $ \xs ->
  element_at xs x == xs !! x
Run Code Online (Sandbox Code Playgroud)

所以现在我们需要一个函数listLongerThan :: Int -> Gen [Int].它需要一个长度x,并产生一个生成器,它将生成长度大于的列表x.

listLongerThan :: Int -> Gen [Int]
listLongerThan x = replicateM (x+1) arbitrary
Run Code Online (Sandbox Code Playgroud)

这很简单:我们只是利用Monad实例Gen.如果你跑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
Run Code Online (Sandbox Code Playgroud)

你可以使用sample smallNumbersample (listLongerThan 3)在ghci中确保它生成正确的东西.

  • 而不是replicateM,我宁愿使用`vector`函数 (5认同)

ham*_*mar 10

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

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

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

  • 您还可以使用hoogle(http://www.haskell.org/hoogle)观察QuickCheck提供的"NonEmpty". (3认同)