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}
$