我正在尝试获取一组随机点(x,y),用于将图形节点绘制到屏幕上。我需要为传入的每个节点名称生成一个随机生成的点。
我在SO页面上发现了这个代码,并稍微修改它以适合我,但它并没有真正做我需要的。
我需要一个随机列表(尽可能随机)(Int,Int)。
无论如何,这是我到目前为止所拥有的,当然,它每次都给出相同的值,所以它不是特别随机:)
rndPoints :: [String] -> [Point]
rndPoints [] = []
rndPoints xs = zip x y where
size = length xs
x = take size (tail (map fst $ scanl (\(r, gen) _ -> randomR (25::Int,1000::Int) gen) (random (mkStdGen 1)) $ repeat ()))
y = take size (tail (map fst $ scanl (\(r, gen) _ -> randomR (25::Int,775::Int) gen) (random (mkStdGen 1)) $ repeat ()))
非常感谢任何帮助。
答案 0 :(得分:6)
首先,让我们稍微清理你的代码。 randomR
的复数版本提供了无限的随机值列表:randomRs
。这简化了一些事情:
rndPoints1 :: [String] -> [Point]
rndPoints1 [] = []
rndPoints1 xs = zip x y
where
size = length xs
x = take size $ randomRs (25, 1000) (mkStdGen 1)
y = take size $ randomRs (25, 775) (mkStdGen 1)
我们可以通过使用zip
的属性来进一步简化它,它会在较短的列表用尽后停止:
rndPoints2 :: [a] -> [Point]
rndPoints2 xs = map snd $ zip xs $ zip x y
where
x = randomRs (25, 1000) (mkStdGen 1)
y = randomRs (25, 775) (mkStdGen 1)
注意我还将传入列表的类型概括为[a]
。由于这些值从未使用过,因此它们不必是String
s!
现在,它每次都给出相同的值,因为它每次都使用mkStdGen
从同一种子(1
)创建一个伪随机生成器。如果您希望每次都不同,那么您需要在IO
中创建一个可以基于计算机的radom状态的生成器。而不是将整个计算放在IO
中,传递StdGen
更简洁:
rndPoints3 :: StdGen -> [Point]
rndPoints3 sg = zip x y
where
(sg1, sg2) = split sg
x = randomRs (25, 1000) sg1
y = randomRs (25, 775) sg2
pointsForLabels :: [a] -> StdGen -> [(a, Point)]
pointsForLabels xs sg = zip xs $ rndPoints3 sg
example3 :: [a] -> IO [(a, Point)]
example3 xs = newStdGen >>= return . pointsForLabels xs
此处,newStdGen
每次都会创建一个新的伪随机生成器,但它位于IO
中。这最终传递给一个纯(非IO
)函数rndPoints3
,该函数接收生成器,并返回随机Point
的无限列表。在该函数中,split
用于从中创建两个生成器,每个生成器用于派生随机的坐标列表。
pointsForLables
现在分离出为每个标签匹配新随机点的逻辑。我还更改了它以返回更有可能有用的标签对和Point
s。
最后,example3
位于IO
,并创建生成器并将其全部传递到纯文本代码中。
答案 1 :(得分:0)
我最终使用MonadRandom。我认为代码更清晰,更容易理解。您可以调整以下代码来解决原始问题。
import Control.Applicative
import Control.Monad.Random
type Point = (Float, Float)
type Poly = [Point]
randomScalar :: (RandomGen g) => Rand g Float
randomScalar = getRandomR (-500, 500)
randomPoint :: (RandomGen g) => Rand g Point
randomPoint = (,) <$> randomScalar <*> randomScalar
randomPoly :: (RandomGen g) => Int -> Rand g Poly
randomPoly n = sequence (replicate n randomPoint)