类似:Haskell Random from Datatype
我创建了一种数据类型,用于在Rock Paper Scissor游戏中包含不同的武器。
data Weapon = Rock | Paper | Scissor
现在我想生成一个随机武器,计算机将对用户使用该武器。 我看一下我在开头发布的类似链接,但对我来说似乎太笼统了。
我可以从任何其他类型生成随机数。我可以理解的是如何使我的数据类型成为Random类的一个实例。
答案 0 :(得分:14)
要生成随机Weapon
,无论您是否Weapon
Random
的实例,您需要的是将数字映射到Weapon
的方法。如果为类型派生Enum
,则编译器会定义往返Int
的映射。所以你可以定义
randomWeapon :: RandomGen g => g -> (Weapon, g)
randomWeapon g = case randomR (0,2) g of
(r, g') -> (toEnum r, g')
例如,。使用Enum
实例,您还可以轻松地Weapon
Random
的实例:
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
,Joachim Breitner } immediately suggested:
data Weapon
= Rock
| Paper
| Scissors
deriving (Bounded, Enum)
instance Random Weapon where
random g = case randomR (fromEnum (minBound :: Weapon), fromEnum (maxBound :: Weapon)) g of
(r, g') -> (toEnum r, g')
randomR (a,b) g = case randomR (fromEnum a, fromEnum b) g of
(r, g') -> (toEnum r, g')
答案 1 :(得分:8)
虽然Daniel Fischer的Enum
方法确实是一种很好的通用方法,但使用Int
的显式映射并不是真的。你也可以这样做
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')
使用Double
的{{1}}实例。这比派生的Random
实例效率低,但更灵活 - 例如,您可以轻松定义不等分布
Enum
其中 random g = case random g of
(r,g') | r < 1/4 = (Rock , g')
| r < 1/2 = (Paper , g')
| otherwise = (Scissors, g')
比其他两个更有可能。当然,如果不等分布在某种程度上对您的数据类型是规范的,那么您应该只做这样的事情,当然不是在这个例子中。
答案 2 :(得分:8)
{-# 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
其中Anything必须是Enum和Bounded类的实例。
答案 3 :(得分:2)
如果您有数据生成器,那么您可以使用Test.QuickCheck.Gen中的oneof
答案 4 :(得分:0)
一位同事向我建议了Alekseev解决方案的替代方案,我发现它更具可读性(我仍在学习Haskell,因此它可能比代码更能吸引读者)
{-# 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)
您可以执行以下操作:getRandomWeapon = randomRIO (minBound, maxBound) :: IO Weapon
,然后用weaponChoice <- getRandomWeapon
提取一个