首先,讓我們清理一下你的代碼。有一個複數版本的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
中創建一個生成器,該生成器可以基於計算機的自由度狀態。而不是把整個計算在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
s。在該函數中,split
用於從中創建兩個生成器,每個用於導出隨機的座標列表。
pointsForLables
現在分離出爲每個標籤匹配一個新隨機點的邏輯。我也改變它返回更可能有用的標籤對和Point
s。
最後,example3
住在IO
中,並創建生成器並將其全部傳遞到純代碼中。
[可能重複(http://stackoverflow.com/questions/2110535/sampling-sequences-of-random-numbers-in-haskell?rq=1) – Nishanth 2013-04-25 06:37:11
你可以隨心所欲的使用從快速檢查包? – 2013-04-25 08:42:56
除了每次調用函數時獲得相同點分佈的問題,使用兩次'mkStdGen 1'仍然是一個壞主意。這將導致x座標和y座標之間的相關性變得尷尬,在最壞的情況下導致沿線的點。 - 無論如何,包含大量重複代碼的兩行代碼幾乎都是壞的,有一些稱爲[DRY原則](http://en.wikipedia.org/wiki/DRY_principle)。 – leftaroundabout 2013-04-25 15:44:13