要生成随机 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')
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')
2
{-# 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
其中任何东西必须是枚举和有界类的实例 .
6
虽然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')
4 回答
要生成随机
Weapon
,无论是否使Weapon
成为Random
的实例,您需要的是将数字映射到Weapon
的方法 . 如果为类型派生Enum
,则编译器将定义往返于Int
的映射 . 所以你可以定义例如 . 使用
Enum
实例,您还可以轻松地使Weapon
成为Random
的实例:如果有可能从类型中添加或删除构造函数,则保持
randomR
与该类型同步的边界的最佳方法是派生Bounded
,如Joachim Breitner immediately suggested:现在你可以说:
其中任何东西必须是枚举和有界类的实例 .
虽然Daniel Fischer的
Enum
方法肯定是一种很好的通用方法,但实际上并没有必要使用Int
中的显式映射 . 你也可以这样做使用
Random
的Double
实例 . 这比派生的Enum
实例效率低,但更灵活 - 例如,您可以轻松定义不等分布其中
Scissors
比其他两个更可能 . 当然,如果不等分布在某种程度上对您的数据类型是规范的,那么您应该只做这样的事情,当然不是在这个例子中 .如果您有数据生成器,那么您可以使用Test.QuickCheck.Gen中的
oneof