使用频率选择 Haskell QuickCheck

Max*_*era 3 haskell quickcheck

所以我有下面的代码,我试图将其作为一个实例Arbitrary

\n
data MyData = I Int | B Bool\n\ninstance Arbitrary MyData where\n  arbitrary = do {\n     frequency [(1, return (I 1)),\n                (1, return (choose((B True), (B False))))]\n  }\n
Run Code Online (Sandbox Code Playgroud)\n

然而,我得到了(可以理解的)错误:

\n
Couldn't match type \xe2\x80\x98Gen MyData\xe2\x80\x99 with \xe2\x80\x98MyData\xe2\x80\x99\n      Expected type: Gen MyData\n        Actual type: Gen (Gen MyData)\n
Run Code Online (Sandbox Code Playgroud)\n

我怎样才能实现这一目标?另外,I 1我想返回I一个随机的而不是 () Int。然而,使用该arbitrary函数而不是1会导致相同的错误。

\n

Mar*_*ann 6

由于您似乎希望在IB构造函数之间均匀分布,因此更简单的解决方案是使用oneof而不是frequency

data MyData = I Int | B Bool deriving (Eq, Show)

instance Arbitrary MyData where
  arbitrary = oneof [genI, genB]
    where genI = fmap I arbitrary 
          genB = fmap B arbitrary
Run Code Online (Sandbox Code Playgroud)

genI生成器通过将原始整数和布尔值映射到相应的情况构造函数来使用和genB的底层Arbitrary实例。IntBool

这是一组示例数据:

> sample (arbitrary :: Gen MyData)
B False
B False
I 2
B False
I 1
I 7
B False
B False
B True
I 7
B False
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它还可以选择任意整数。


OP中的代码有几个问题。第一个错误消息是返回类型是嵌套的。解决这个问题的一种方法是删除符号do。然而,这并不能解决问题。

即使您将其简化为以下内容,它也不会进行类型检查:

instance Arbitrary MyData where
  arbitrary =
     frequency [(1, return (I 1)),
                (1, choose(B True, B False))]
Run Code Online (Sandbox Code Playgroud)

此尝试会产生错误:

Q72160684.hs:10:21: error:
    * No instance for (random-1.1:System.Random.Random MyData)
        arising from a use of `choose'
    * In the expression: choose (B True, B False)
      In the expression: (1, choose (B True, B False))
      In the first argument of `frequency', namely
        `[(1, return (I 1)), (1, choose (B True, B False))]'
   |
10 |                 (1, choose(B True, B False))]
   |                     ^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

choose方法要求输入是Random实例,但事实MyData并非如此。


如果您确实想使用frequency而不是oneof,最简单的方法可能是首先开始oneof工作,因为您可以将frequency其视为oneof.

首先,为了使代码更简洁,我使用了<$>代替fmap,然后内联了两个生成器:

instance Arbitrary MyData where
  arbitrary = oneof [I <$> arbitrary, B <$> arbitrary]
Run Code Online (Sandbox Code Playgroud)

现在替换oneoffrequency并将每个生成器更改为加权元组:

instance Arbitrary MyData where
  arbitrary = frequency [(10, I <$> arbitrary), (1, B <$> arbitrary)]
Run Code Online (Sandbox Code Playgroud)

从此实例中进行采样表明分布现在是倾斜的:

> sample (arbitrary :: Gen MyData)
I 0
I (-2)
I (-4)
I (-1)
I 0
I 8
B True
I 1
I 3
I (-3)
I (-16)
Run Code Online (Sandbox Code Playgroud)

有 10 个I值,只有 1 个B值。


Ice*_*ack 5

您可以使用generic-random派生它(1.5.0.0开始)。

推导方式GenericArbitraryU: 给出均匀分布(如oneofMark Seemann 的答案):

{-# Language DataKinds     #-}
{-# Language DeriveGeneric #-}
{-# Language DerivingVia   #-}

import Test.QuickCheck
import GHC.Generics

import Generic.Random.DerivingVia

-- ghci> :set -XTypeApplications
-- ghci> sample @MyData arbitrary
-- I 0
-- I 1
-- B True
-- I 6
-- I (-5)
-- I (-7)
-- B True
-- I (-10)
-- B True
-- B True
-- I (-9)
data MyData = I Int | B Bool
  deriving
  stock (Show, Generic)

  deriving Arbitrary
  via GenericArbitraryU MyData
Run Code Online (Sandbox Code Playgroud)

派生方式GenericArbitrary:给出由类型级数字列表指定的加权分布。它们表示每个构造函数的频率(如frequency):

-- ghci> sample @MyData arbitrary
-- I 0
-- I (-2)
-- I 4
-- I 5
-- I 2
-- I 0
-- B False
-- I (-9)
-- I (-10)
-- I (-3)
-- I (-8)
data MyData = I Int | B Bool
  deriving
  stock (Show, Generic)

  deriving Arbitrary
  via GenericArbitrary '[10, 1] MyData
Run Code Online (Sandbox Code Playgroud)