Lass uns am Anfang beginnen:
type Rand a = State StdGen a
Diese Zeile sagt Ihnen, dass Rand a
eine Art Synonym für einen Typ State
ist, dessen Zustand durch StdGen
und deren eventuellen Wert von a
Typ gegeben ist. Dies wird verwendet, um den Zustand des Zufallszahlengenerators zwischen jeder Anfrage nach einer Zufallszahl zu speichern.
Der Code für getRandom
kann in tun Notation umgewandelt werden:
getRandom :: (Random a) => Rand a
getRandom = do
r <- get -- get the current state of the generator
let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen)
put g -- store the new state of the generator
return a -- return the random number that was generated
Die runRand
Funktion nimmt eine anfängliche Samen n
und einen Wert r
von Rand a
Typ (die erinnern, ist nur ein Synonym für State StdGen a
). Er erstellt einen neuen Generator mit mkStdGen n
und führt ihn evalState r
zu. Die Funktion evalState
wertet nur den Rückgabewert eines State s a
Typs aus und ignoriert den Status.
Auch hier können wir runRandIO
in do
Notation umwandeln:
runRandIO :: Rand a -> IO a
runRandIO r = do
rnd <- randomIO -- generate a new random number using randomIO
return (runRand rnd r) -- use that number as the initial seed for runRand
Schließlich getRandoms
nimmt eine Reihe n
die Anzahl von Zufallswerten darstellt, die Sie generieren möchten. Es erstellt eine Liste [1..n]
und wendet getRandom
auf die Liste an. Beachten Sie, dass die tatsächlichen Werte in [1..n]
nicht verwendet werden (Sie können erkennen, dass die Lambda-Funktion mit \_ -> ...
beginnt). Die Liste ist nur dazu da, etwas mit der richtigen Anzahl von Elementen zu haben. Da getRandom
einen monadischen Wert zurückgibt, verwenden wir mapM
, um die Liste abzubilden, was dazu führt, dass der Status (d. H. StdGen
) korrekt durch jeden der Aufrufe an getRandom
gefädelt wird.