Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.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_Random - Fatal编程技术网

Haskell 生成自定义数据类型的随机列表,其中提供给数据构造函数的值以某种方式限定在一个范围内

Haskell 生成自定义数据类型的随机列表,其中提供给数据构造函数的值以某种方式限定在一个范围内,haskell,random,Haskell,Random,我已经定义了一个点数据类型,具有如下单值构造函数: data Point = Point { x :: Int, y :: Int, color :: Color } deriving (Show, Eq) data Color = None | Black | Red | Green | Blue deriving (Show, Eq, Enum, Bounded) 我发现了一个将Bounded Enum作为Random类实例的示例,并将Color作为其实例

我已经定义了一个
数据类型,具有如下单值构造函数:

data Point = Point {
  x :: Int,
  y :: Int,
  color :: Color
} deriving (Show, Eq)

data Color = None
  | Black
  | Red
  | Green
  | Blue
  deriving (Show, Eq, Enum, Bounded)
我发现了一个将
Bounded Enum
作为
Random
类实例的示例,并将
Color
作为其实例,如下所示:

instance Random Color where
  random g = case randomR (1, 4) g of
    (r, g') -> (toEnum r, g')
  randomR (a, b) g = case randomR (fromEnum a, fromEnum b) g of
    (r, g') -> (toEnum r, g')
然后我就能够找到如何指出Random类的一个实例:

instance Random Point where
  randomR (Point xl yl cl, Point xr yr cr) g =
    let (x, g1) = randomR (xl, xr) g
        (y, g2) = randomR (yl, yr) g1
        (c, g3) = randomR (cl, cr) g2
    in (Point x y c, g3)
  random g =
    let (x, g1) = random g
        (y, g2) = random g1
        (c, g3) = random g2
    in (Point x y c, g3)
所以,让我们做一些随机的点值。但是,我想做的是能够创建一个随机
值列表,其中
x
y
属性在一定范围内有界,而
颜色
属性则是一个无界随机值。我目前对代码建模的方式是否可能做到这一点,或者我是否需要重新思考如何构建
值?例如,我是否应该在
IO
monad中创建
Int
的随机列表,然后使用一个纯函数创建n个点,使用随机列表中的值构造每个
值,而不是将
作为随机类的实例

编辑,我想我已经知道了怎么做:

在不更改上述代码的情况下,我可以在
IO
monad中执行以下操作:

solved :: IO ()
solved = do
  randGen <- getStdGen

  let x = 2
  let randomPoints = take x $ randomRs (Point 0 0 None, Point 200 200 Blue) randGen

  putStrLn $ "Random points: " ++ show randomPoints
solved::IO()
解决了

randGen它之所以有效,是因为Int和Color类型的属性,而不是因为Point的属性。如果取消点的
Eq
子句,它仍然有效

您的代码总体上相当好,但是我要提到一些小的警告

的随机实例中,您正在手动链接生成器状态;这有点容易出错,一元do表示法被认为是不必要的。颜色实例可以简化

您使用的IO不是真正需要的。如果
g
RandomGen
的实例,则任何
Rand g
都是
MonadRandom
的实例

从一个程序执行到下一个程序执行,您得到的随机值是不可复制的;这是因为
getStdGen
隐式使用启动时间作为随机数生成种子。它可能会这样做,因为它是IO托管的。在许多情况下,这是一个问题,因为人们可能希望彼此独立地改变随机序列和系统参数的选择

使用一元风格,您的代码的基础可以重写,例如:

import  System.Random
import  System.Random.TF -- Threefish random number generator
import  Control.Monad.Random

data Point = Point {
  x :: Int,
  y :: Int,
  color :: Color
} deriving (Show, Eq)

data Color = None
  | Black
  | Red
  | Green
  | Blue
  deriving (Show, Eq, Enum, Bounded)

instance Random Color where
  randomR (a, b) g = let (r,g') = randomR (fromEnum a, fromEnum b) g
                     in  (toEnum r, g')
  random g = randomR (minBound::Color, maxBound::Color) g


singleRandomPoint ::  -- monadic action for just one random point
  MonadRandom mr => Int -> Int -> Color -> Int -> Int -> Color -> mr Point
singleRandomPoint xmin ymin cmin xmax ymax cmax =
  do
    -- avoid manual chaining of generator states:
    x <- getRandomR (xmin, xmax)
    y <- getRandomR (ymin, ymax)
    c <- getRandomR (cmin, cmax)
    return (Point x y c)

最后,我们可以在
stdout
上打印前几个随机点:


-- Small printing utility:
printListAsLines :: Show t => [t] -> IO()
printListAsLines xs = mapM_ (putStrLn . show) xs

solved01 :: IO ()
solved01 = do
  let
       seed            = 42  -- for random number generator setup
       -- unlimited list of random points:
       allRandomPoints = randomPoints  seed  0 0 None 200 200 Blue
       count           = 5
       someRandomPoints = take count allRandomPoints
       -- IO not used at all so far

  putStrLn $ "Random points: "
  printListAsLines someRandomPoints

main = solved01

程序执行(可通过恒定种子进行复制): 如果您希望只获取有限数量的点,并返回随机数生成器的更新状态,则必须使用而不是
repeat
,也不是
evalRand


关于一元方法的更多细节。

我会使用相同的方法,但我会编写
randomRs(点0 0最小界,点200最大界)
,这样可以清楚地看到颜色在最小和最大颜色值之间受到“约束”,因此没有对颜色施加实际约束
code
有界在这里很重要,
Int
有界并不重要——我们只需要
随机Int
就可以了,因为我们通过了界
0
200
。学究地说,我们只需要
COLORE
存在一个最小/最大值,而不是
有界的code>实例。然而,在这种假设下,不定义(或派生)有界颜色实例将是愚蠢的。您甚至可以定义
random=randomR(minBound,maxBound)
并避免复制代码。

-- Small printing utility:
printListAsLines :: Show t => [t] -> IO()
printListAsLines xs = mapM_ (putStrLn . show) xs

solved01 :: IO ()
solved01 = do
  let
       seed            = 42  -- for random number generator setup
       -- unlimited list of random points:
       allRandomPoints = randomPoints  seed  0 0 None 200 200 Blue
       count           = 5
       someRandomPoints = take count allRandomPoints
       -- IO not used at all so far

  putStrLn $ "Random points: "
  printListAsLines someRandomPoints

main = solved01

$ randomPoints
Random points: 
Point {x = 187, y = 56, color = Green}
Point {x = 131, y = 28, color = Black}
Point {x = 89, y = 135, color = Blue}
Point {x = 183, y = 190, color = Red}
Point {x = 27, y = 161, color = Green}
$