使用选择频率 Haskell QuickCheck
Using choose in frequency Haskell QuickCheck
所以我有下面的代码,我试图让它成为 Arbitrary
:
的一个实例
data MyData = I Int | B Bool
instance Arbitrary MyData where
arbitrary = do {
frequency [(1, return (I 1)),
(1, return (choose((B True), (B False))))]
}
但是我得到了(可以理解的)错误:
Couldn't match type ‘Gen MyData’ with ‘MyData’
Expected type: Gen MyData
Actual type: Gen (Gen MyData)
我怎样才能实现这个?另外,我想 return I
随机 Int
而不是 (I 1
)。但是,使用 arbitrary
函数而不是 1
会导致相同的错误。
由于您似乎希望在 I
和 B
构造函数之间均匀分布,因此更简单的解决方案是使用 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
genI
和 genB
生成器通过将原始整数和布尔值映射到各自的情况来使用 Int
和 Bool
的基础 Arbitrary
实例构造函数。
这是一组示例数据:
> 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
正如你所看到的,它也完成了选择任意整数。
OP 中的代码有几个问题。第一条错误消息是 return 类型是嵌套的。解决这个问题的一种方法是删除 do
符号。然而,这并不能解决问题。
即使你把它减少到下面这样,它也不会type-check:
instance Arbitrary MyData where
arbitrary =
frequency [(1, return (I 1)),
(1, choose(B True, B False))]
此尝试产生错误:
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))]
| ^^^^^^^^^^^^^^^^^^^^^^^
choose
方法要求输入是 Random
个实例,而 MyData
不是。
如果您真的想使用 frequency
而不是 oneof
,最简单的方法可能是首先让 oneof
起作用,因为您可以将 frequency
视为oneof
.
的概括
首先,为了使代码更加简洁,我使用了 <$>
而不是 fmap
,然后内联了两个生成器:
instance Arbitrary MyData where
arbitrary = oneof [I <$> arbitrary, B <$> arbitrary]
现在将 oneof
替换为 frequency
并将每个生成器更改为加权元组:
instance Arbitrary MyData where
arbitrary = frequency [(10, I <$> arbitrary), (1, B <$> arbitrary)]
从这个实例中抽样说明分布现在是倾斜的:
> 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)
有 10 个 I
个值,只有 1 个 B
个值。
你可以用 generic-random (since 1.5.0.0).
导出它
通过 GenericArbitraryU
推导:提供均匀分布(如来自 Mark Seemann 的回答的 oneof
):
{-# 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
通过 GenericArbitrary
推导:给出由 type-level 数字列表指定的加权分布。它们表示每个构造函数的频率(如 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
所以我有下面的代码,我试图让它成为 Arbitrary
:
data MyData = I Int | B Bool
instance Arbitrary MyData where
arbitrary = do {
frequency [(1, return (I 1)),
(1, return (choose((B True), (B False))))]
}
但是我得到了(可以理解的)错误:
Couldn't match type ‘Gen MyData’ with ‘MyData’
Expected type: Gen MyData
Actual type: Gen (Gen MyData)
我怎样才能实现这个?另外,我想 return I
随机 Int
而不是 (I 1
)。但是,使用 arbitrary
函数而不是 1
会导致相同的错误。
由于您似乎希望在 I
和 B
构造函数之间均匀分布,因此更简单的解决方案是使用 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
genI
和 genB
生成器通过将原始整数和布尔值映射到各自的情况来使用 Int
和 Bool
的基础 Arbitrary
实例构造函数。
这是一组示例数据:
> 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
正如你所看到的,它也完成了选择任意整数。
OP 中的代码有几个问题。第一条错误消息是 return 类型是嵌套的。解决这个问题的一种方法是删除 do
符号。然而,这并不能解决问题。
即使你把它减少到下面这样,它也不会type-check:
instance Arbitrary MyData where
arbitrary =
frequency [(1, return (I 1)),
(1, choose(B True, B False))]
此尝试产生错误:
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))]
| ^^^^^^^^^^^^^^^^^^^^^^^
choose
方法要求输入是 Random
个实例,而 MyData
不是。
如果您真的想使用 frequency
而不是 oneof
,最简单的方法可能是首先让 oneof
起作用,因为您可以将 frequency
视为oneof
.
首先,为了使代码更加简洁,我使用了 <$>
而不是 fmap
,然后内联了两个生成器:
instance Arbitrary MyData where
arbitrary = oneof [I <$> arbitrary, B <$> arbitrary]
现在将 oneof
替换为 frequency
并将每个生成器更改为加权元组:
instance Arbitrary MyData where
arbitrary = frequency [(10, I <$> arbitrary), (1, B <$> arbitrary)]
从这个实例中抽样说明分布现在是倾斜的:
> 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)
有 10 个 I
个值,只有 1 个 B
个值。
你可以用 generic-random (since 1.5.0.0).
导出它通过 GenericArbitraryU
推导:提供均匀分布(如来自 Mark Seemann 的回答的 oneof
):
{-# 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
通过 GenericArbitrary
推导:给出由 type-level 数字列表指定的加权分布。它们表示每个构造函数的频率(如 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