如何在 QuickCheck 中生成特定的随机字符串?

How to generate specific random string in QuickCheck?

在Haskell的QuickCheck中,如何生成一个只包含'S'和'C'字符且'S'和'C'位置随机的字符串?

例如:"SCCS"、"SSSS"、"CCCC"、"CSSCCS"、""

我的用例是这样的:

我有两个函数 countCAndS :: String -> (Int, Int)countCAndS' :: String -> (Int, Int)。它们具有相同的类型签名。我想写一个 QuickCheck 属性 这样我就可以将相同的字符串传递给这两个不同的函数并检查输出是否相同。

让您的 属性 获取布尔值列表并将它们转换为 cs 和 ss。

prop_CountersEqual :: [Bool] -> Bool
prop_CountersEqual bs = countCAndS css == countCAndS' css where
    css = map (\b -> if b then 'C' else 'S') bs

如果您经常需要它,您可能需要定义一个具有合适 Arbitrary 实例的新类型。

newtype CAndS = CAndS String
instance Arbitrary CAndS where
    arbitrary = CAndS . map (\b -> if b then 'C' else 'S') <$> arbitrary

然后你可以写你的属性,例如

prop_CountersEqual' :: CAndS -> Bool
prop_CountersEqual' (CAndS css) = countCAndS css == countCAndS' css

QuickCheck 为此提供了一系列组合器,它超级简单而且非常优雅:

prop_CountersEqual = 
  forAll csString $ \s -> countCAndS s == countCAndS' s

csString :: Gen String
csString = listOf $ elements "CS"

扩展字典也很简单,如果您需要的话。

这是定义 Arbitrary 实例的另一种方法:

newtype CAndS = CAndS String
instance Arbitrary CAndS where
  arbitrary = CAndS <$> listOf (elements "CS")

除了根据参考解决方案测试函数外,您还可以测试 'C's 和 'S'es 的总和是否与字符串的长度相同:

prop_CountersAddUp :: CAndS -> Bool
prop_CountersAddUp (CAndS css) = length css == cs + ss
  where (cs, ss) = countCAndS css

如果字符串应该接受具有除 'C's 和 'S'es 之外的字符的输入(但不计算它们),您或许应该创建一个生成各种字符的生成器, 但 'C''S':

的概率更高
newtype CAndS = CAndS String
instance Arbitrary CAndS where
  arbitrary = CAndS <$> listOf (frequency [ (1, return 'C')
                                          , (1, return 'S')
                                          , (2, arbitrary) ])

或者您可以生成一个带有正确答案注释的字符串:

newtype CAndS = CAndS (String, Int, Int)
instance Arbitrary CAndS where
    arbitrary = CAndS <$> sized (\n -> do
      cs <- choose (0, n)
      let ss = n - cs
      css <- shuffle (replicate cs 'C' ++ replicate ss 'S')
      return (css, cs, ss))

所以你可以测试一下:

prop_CountersEqual :: CAndS -> Bool
prop_CountersEqual (CAndS (css, cs, ss)) = cs == cs' && ss == ss'
  where (cs', ss') = countCAndS css