まず、コードを少しクリーンアップしましょう。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]
。値は使用されないため、s である必要はありませんString
。
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
) 関数に渡され、ランダムなs の無限リストを返します。その関数内で、それから 2 つのジェネレーターを作成するために使用され、それぞれが座標のランダム リストを導出するために使用されます。rndPoints3
Point
split
pointsForLables
は、ラベルごとに新しいランダム ポイントを照合するロジックを分離するようになりました。また、より役立つ可能性が高いラベルとPoint
s のペアを返すように変更しました。
最後に、 にexample3
住みIO
、ジェネレーターを作成し、それ以外の場合は純粋なコードにすべて渡します。