从Haskell中的用户定义数据类型生成随机值
类似: 我已经创建了一个数据类型来包含岩石纸剪刀游戏中的不同武器从Haskell中的用户定义数据类型生成随机值,haskell,types,random,Haskell,Types,Random,类似: 我已经创建了一个数据类型来包含岩石纸剪刀游戏中的不同武器 data Weapon = Rock | Paper | Scissor 现在我想生成一个随机武器,它将被计算机用来对付用户。 我已经看了一看类似的链接,我在开始张贴,但它似乎太一般,我 我能够从任何其他类型生成随机数。我能想到的是如何使我的数据类型成为Random类的一个实例。要生成一个Random武器,无论你是否使武器成为Random的一个实例,你需要的是一种将数字映射到武器的方法。如果为类型派生Enum,则编译器将定义指向
data Weapon = Rock | Paper | Scissor
现在我想生成一个随机武器,它将被计算机用来对付用户。
我已经看了一看类似的链接,我在开始张贴,但它似乎太一般,我
我能够从任何其他类型生成随机数。我能想到的是如何使我的数据类型成为Random类的一个实例。要生成一个Random
武器
,无论你是否使武器
成为Random
的一个实例,你需要的是一种将数字映射到武器
的方法。如果为类型派生Enum
,则编译器将定义指向Int
s和来自Int
s的映射。所以你可以定义
randomWeapon :: RandomGen g => g -> (Weapon, g)
randomWeapon g = case randomR (0,2) g of
(r, g') -> (toEnum r, g')
比如说。使用Enum
实例,您还可以轻松地将武器
作为随机
的实例:
instance Random Weapon where
random g = case randomR (0,2) g of
(r, g') -> (toEnum r, g')
randomR (a,b) g = case randomR (fromEnum a, fromEnum b) g of
(r, g') -> (toEnum r, g')
如果有可能在类型中添加或删除构造函数,保持randomR
的边界与类型同步的最佳方法是派生Bounded
,如下所示:
虽然Daniel Fischer的
Enum
方法无疑是一种很好的通用方法,但实际上并不需要使用Int
s的显式映射。你也可以这样做
instance Random Weapon where
random g = case random g of
(r,g') | r < 1/3 = (Rock , g')
| r < 2/3 = (Paper , g')
| otherwise = (Scissors, g')
其中,剪刀
比其他两个更可能。当然,只有在非相等分布在某种程度上对您的数据类型是规范的情况下,才应该这样做,在本例中肯定不是这样
{-# LANGUAGE FlexibleInstances, UndecidableInstances,
ScopedTypeVariables, OverlappingInstances #-}
import System.Random
class (Bounded a, Enum a) => BoundedEnum a
instance (Bounded a, Enum a) => BoundedEnum a
instance BoundedEnum a => Random a where
random gen = randomR (minBound :: a, maxBound :: a) gen
randomR (f, t) gen =
(toEnum r :: a, nextGen)
where
(rnd, nextGen) = next gen
r = fromEnum f + (rnd `mod` length [f..t])
现在你可以说:
r <- randomIO :: Anything
r如果你有一个数据生成器,那么你可以使用Test.QuickCheck.Gen中的one
一位同事向我建议了一个替代Alekseev的解决方案,我发现它可读性更强一些(我仍在学习Haskell,所以它可能更多的是在阅读器上,而不是在代码上)
然后,您可以执行:getrandomWearm=randomRIO(minBound,maxBound)::IO-wearm
然后使用weaponChoice提取一个,使用一个附加的派生绑定实例,您可以使代码完全抽象。如果我添加一个新的武器会怎么样?有没有一种通用的方法可以知道我的数据类型可以有多少个不同的值?那么派生有界也是保持同步的最简单的方法。更新答案。定义BoundedEnum
而不是直接使用(Bounded a,Enum a)=>Random a
,原因是什么?@huon–虽然这篇文章很老,但我怀疑它是为了避免创建孤立实例(),这可能会有问题。说它希望在可能的产出中得到统一的分配。对于非均匀分布,最好实现一个单独的函数,以避免破坏typeclass约定。@4尽管如此,该typeclass约定实际上并不起作用。例如,Double
,“均匀”是在连续分布的意义上理解的,即实线上的概率密度是恒定的。这肯定是有道理的。但是,实数行不是通过浮点值进行统一采样的,因此如果您实际查看二进制不同个体双
值的分布,您会注意到0.8346253987632499
比0.000000326538256732462
发生的频率要高得多像武器之类的类型我同意。
{-# LANGUAGE FlexibleInstances, UndecidableInstances,
ScopedTypeVariables, OverlappingInstances #-}
import System.Random
class (Bounded a, Enum a) => BoundedEnum a
instance (Bounded a, Enum a) => BoundedEnum a
instance BoundedEnum a => Random a where
random gen = randomR (minBound :: a, maxBound :: a) gen
randomR (f, t) gen =
(toEnum r :: a, nextGen)
where
(rnd, nextGen) = next gen
r = fromEnum f + (rnd `mod` length [f..t])
r <- randomIO :: Anything
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
instance {-# OVERLAPPABLE #-} (Bounded a, Enum a) => Random a where
random = randomR (minBound, maxBound)
randomR (f, t) gen =
let (rndInt, nxtGen) = randomR (fromEnum f, fromEnum t) gen
in (toEnum rndInt, nxtGen)
data Weapon = Rock | Paper | Scissor deriving (Eq, Show, Ord, Bounded, Enum)