在 Quickcheck 中组合不同数据类型的生成器
Combining generator for different datatypes in Quickcheck
我想合并两个不同数据类型的自定义生成器,但它们在另一种数据类型中组合在一起。
在下面的示例中,我想使用 Legumes
和 AnimalProteins
的生成器为蛋白质创建另一个生成器。 choose
和 elements
不起作用,因为类型不同。将生成器转换为 gen Proteins
也不起作用。
data AnimalProteins = Beef | Chicken | Fish
data Legumes = WhiteBeans | RedBeans | Lentils | Chickpeas
data Proteins = AnimalProteins | Legumes deriving (Show)
rAnimalProteins :: Gen AnimalProteins
rAnimalProteins = elements [Beef , Chicken , Fish]
rLegumes :: Gen Legumes
rLegumes = elements [WhiteBeans , RedBeans , Lentils , Chickpeas]
-- This does not work !
rProteins :: Gen Proteins
rProteins = choose (rLegumes, rAnimalProteins)
解决方案可能很简单,但作为初学者我还是被困在这里。谢谢!
问题是您的 Proteins
数据类型实际上不包含任何 AnimalProteins
或 Legumes
!是的,您以相同的方式命名了 Proteins
的 constructors,但是构造函数存在于术语语言中,而不是类型语言中,因此编译器不会自动关联他们到 AnimalProteins
type.
为此,您需要明确:
data Proteins = AnimalProteins AnimalProteins | Legumes Legumes
假设您打算将 Proteins
建模为 AnimalProteins
和 Legumes
之间的选择,您可能需要这样的东西:
data AnimalProteins = Beef | Chicken | Fish deriving (Show, Eq)
data Legumes = WhiteBeans | RedBeans | Lentils | Chickpeas deriving (Show, Eq)
data Proteins = AP AnimalProteins | L Legumes deriving (Show, Eq)
您可以像这样组合生成器:
rAnimalProteins :: Gen AnimalProteins
rAnimalProteins = elements [Beef, Chicken, Fish]
rLegumes :: Gen Legumes
rLegumes = elements [WhiteBeans, RedBeans, Lentils, Chickpeas]
rProteins :: Gen Proteins
rProteins = oneof [fmap AP rAnimalProteins, fmap L rLegumes]
rAnimalProteins
和 rLegumes
是您已经定义的。您可以使用 fmap
'lift' 将它们分成两个单独的 Gen Proteins
值,因为 Gen
是 Functor
.
例如,fmap AP rAnimalProteins
将 Gen Legumes
值映射到 Gen Proteins
值,方法是映射每个生成的 Legumes
值并用 AP
数据包装它构造函数。虽然它是一个 Gen Proteins
值,但它始终会创建 AP
个值,即 AP Beef
、AP Chicken
或 AP Fish
.
同样,fmap L rLegumes
将 rLegumes
从 Gen Legumes
提升到 Gen Proteins
。它将始终生成值 L WhiteBeans
、L RedBeans
、L Lentils
或 L Chickpeas
.
之一
通过使用 oneof
,您可以将两个专用生成器组合成一个更复杂的生成器,该生成器随机选择其中一个生成器来生成值。
我想合并两个不同数据类型的自定义生成器,但它们在另一种数据类型中组合在一起。
在下面的示例中,我想使用 Legumes
和 AnimalProteins
的生成器为蛋白质创建另一个生成器。 choose
和 elements
不起作用,因为类型不同。将生成器转换为 gen Proteins
也不起作用。
data AnimalProteins = Beef | Chicken | Fish
data Legumes = WhiteBeans | RedBeans | Lentils | Chickpeas
data Proteins = AnimalProteins | Legumes deriving (Show)
rAnimalProteins :: Gen AnimalProteins
rAnimalProteins = elements [Beef , Chicken , Fish]
rLegumes :: Gen Legumes
rLegumes = elements [WhiteBeans , RedBeans , Lentils , Chickpeas]
-- This does not work !
rProteins :: Gen Proteins
rProteins = choose (rLegumes, rAnimalProteins)
解决方案可能很简单,但作为初学者我还是被困在这里。谢谢!
问题是您的 Proteins
数据类型实际上不包含任何 AnimalProteins
或 Legumes
!是的,您以相同的方式命名了 Proteins
的 constructors,但是构造函数存在于术语语言中,而不是类型语言中,因此编译器不会自动关联他们到 AnimalProteins
type.
为此,您需要明确:
data Proteins = AnimalProteins AnimalProteins | Legumes Legumes
假设您打算将 Proteins
建模为 AnimalProteins
和 Legumes
之间的选择,您可能需要这样的东西:
data AnimalProteins = Beef | Chicken | Fish deriving (Show, Eq)
data Legumes = WhiteBeans | RedBeans | Lentils | Chickpeas deriving (Show, Eq)
data Proteins = AP AnimalProteins | L Legumes deriving (Show, Eq)
您可以像这样组合生成器:
rAnimalProteins :: Gen AnimalProteins
rAnimalProteins = elements [Beef, Chicken, Fish]
rLegumes :: Gen Legumes
rLegumes = elements [WhiteBeans, RedBeans, Lentils, Chickpeas]
rProteins :: Gen Proteins
rProteins = oneof [fmap AP rAnimalProteins, fmap L rLegumes]
rAnimalProteins
和 rLegumes
是您已经定义的。您可以使用 fmap
'lift' 将它们分成两个单独的 Gen Proteins
值,因为 Gen
是 Functor
.
例如,fmap AP rAnimalProteins
将 Gen Legumes
值映射到 Gen Proteins
值,方法是映射每个生成的 Legumes
值并用 AP
数据包装它构造函数。虽然它是一个 Gen Proteins
值,但它始终会创建 AP
个值,即 AP Beef
、AP Chicken
或 AP Fish
.
同样,fmap L rLegumes
将 rLegumes
从 Gen Legumes
提升到 Gen Proteins
。它将始终生成值 L WhiteBeans
、L RedBeans
、L Lentils
或 L Chickpeas
.
通过使用 oneof
,您可以将两个专用生成器组合成一个更复杂的生成器,该生成器随机选择其中一个生成器来生成值。