首页 文章

从Haskell中的用户定义数据类型生成随机值

提问于
浏览
10

Similar: Haskell Random from Datatype

我已经创建了一种数据类型来包含Rock Paper Scissor游戏中的不同武器 .

data Weapon = Rock | Paper | Scissor

现在我想生成一个随机武器,计算机将对用户使用该武器 . 我看一下我在开头发布的类似链接,但对我来说似乎太笼统了 .

我能够从任何其他类型生成随机数 . 我可以理解的是如何使我的数据类型成为Random类的一个实例 .

4 回答

  • 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')
    
  • 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')
    

    使用 RandomDouble 实例 . 这比派生的 Enum 实例效率低,但更灵活 - 例如,您可以轻松定义不等分布

    random g = case random g of
                   (r,g') | r < 1/4    = (Rock    , g')
                          | r < 1/2    = (Paper   , g')
                          | otherwise  = (Scissors, g')
    

    其中 Scissors 比其他两个更可能 . 当然,如果不等分布在某种程度上对您的数据类型是规范的,那么您应该只做这样的事情,当然不是在这个例子中 .

  • 8

    如果您有数据生成器,那么您可以使用Test.QuickCheck.Gen中的 oneof

相关问题