Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从Haskell中的用户定义数据类型生成随机值_Haskell_Types_Random - Fatal编程技术网

从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)