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

Oni*_*Oni 12 random haskell types

类似:Haskell Random来自Datatype

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

data Weapon = Rock | Paper | Scissor
Run Code Online (Sandbox Code Playgroud)

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

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

Dan*_*her 14

要生成随机数Weapon,无论您是否创建Weapon实例Random,您需要的是将数字映射到Weapons的方法.如果Enum为类型派生,Int则编译器将定义与s之间的映射.所以你可以定义

randomWeapon :: RandomGen g => g -> (Weapon, g)
randomWeapon g = case randomR (0,2) g of
                   (r, g') -> (toEnum r, g')
Run Code Online (Sandbox Code Playgroud)

例如.通过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')
Run Code Online (Sandbox Code Playgroud)

如果有可能从类型中添加或删除构造函数,那么保持randomR与类型同步的边界的最佳方法也是派生出来Bounded,正如Joachim Breitner 立即建议:

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')
Run Code Online (Sandbox Code Playgroud)

  • 使用额外的派生Bounded实例,您可以使代码完全抽象. (6认同)

lef*_*out 8

虽然Daniel Fischer的Enum方法肯定是一种很好的通用方法,但实际上并没有必要使用Ints 的显式映射.你也可以这样做

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')
Run Code Online (Sandbox Code Playgroud)

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

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

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


Ale*_*eev 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])
Run Code Online (Sandbox Code Playgroud)

现在你可以说:

r <- randomIO :: Anything
Run Code Online (Sandbox Code Playgroud)

其中任何东西必须是枚举和有界类的实例.

  • 定义`BoundedEnum`而不是直接使用`(Bounded a,Enum a)=> Random a`的原因是什么? (4认同)