Haskell - 如何获得随机点(Int,Int)

use*_*786 5 random haskell

我正在尝试获取一组随机点(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 ()))
Run Code Online (Sandbox Code Playgroud)

任何帮助将非常感激.

Mtn*_*ark 7

首先,让我们稍微清理你的代码.有一个复数版本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)
Run Code Online (Sandbox Code Playgroud)

我们可以通过使用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)
Run Code Online (Sandbox Code Playgroud)

请注意,我还将传入列表的类型一般化[a].由于这些值从未使用过,所以它们不一定是Strings!

现在,它每次都给出相同的值,因为它每次都使用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
Run Code Online (Sandbox Code Playgroud)

在这里,newStdGen每次创建一个新的伪随机生成器,但它在IO.这最终传递给一个接收生成器的纯(非IO)函数rndPoints3,并返回一个随机s 的无限列表Point.在该函数中,split用于从中创建两个生成器,每个生成器用于派生随机的坐标列表.

pointsForLables现在分离出为每个标签匹配一个新的随机点的逻辑.我也改变它以返回更有可能有用的标签对和Points.

最后,example3生活在IO,并创建生成器并将其全部传递到其他纯粹的代码中.