Understanding Random Monad in Scala
Dies ist eine Fortsetzung meines vorherigenFrag
Travis Brown wies darauf hin, dassjava.util.Random
ist nebenwirkend und hat eine zufällige Monade vorgeschlagenRng
Bibliothe, um den Code rein funktional zu machen. Jetzt versuche ich, selbst eine vereinfachte Zufallsmonade zu erstellen, um zu verstehen, wie das funktioniert.
Macht das Sinn ? Wie würden Sie die unten stehende Erklärung korrigieren / verbessern?
Random GeneratorZunächst plagiieren wir eine zufallsgenerierende Funktion ausjava.util.Random
// do some bit magic to generate a new random "seed" from the given "seed"
// and return both the new "seed" and a random value based on it
def next(seed: Long, bits: Int): (Long, Int) = ...
Beachten Sie, dassnext
kehrt zurückbeid der neue Keim und der Wert und nicht nur der Wert. Wir brauchen es, um den neuen Startwert an einen anderen Funktionsaufruf zu übergeben.
Nun schreiben wir eine Funktion, um einen zufälligen Punkt in einem Einheitsquadrat zu erzeugen.
Angenommen, wir haben eine Funktion zum Erzeugen eines zufälligen Double im Bereich [0, 1]
def randomDouble(seed: Long): (Long, Double) = ... // some bit magic
etzt können wir eine Funktion schreiben, um einen zufälligen Punkt zu erzeuge
def randomPoint(seed: Long): (Long, (Double, Double)) = {
val (seed1, x) = randomDouble(seed)
val (seed2, y) = randomDouble(seed1)
(seed2, (x, y))
}
So weit, so gut und beidesrandomDouble
undrandomPoint
sind rein. Das einzige Problem ist, dass wir @ komponierrandomDouble
bauenrandomPoint
Ad ho. Wir haben keingenerisc Tool zum Erstellen von Funktionen, die zufällige Werte liefern.
Nun definieren wir eingenerisc -Tool zum Erstellen von Funktionen mit zufälligen Werten. Zunächst verallgemeinern wir den Typ vonrandomDouble
:
type Random[A] = Long => (Long, A) // generate a random value of type A
und dann bauen Sie eine Wrapper-Klasse um ihn herum.
class Random[A](run: Long => (Long, A))
Wir brauchen den Wrapper, um Methoden zu definierenflatMap
(wiebinde in Haskell) undmap
benutzt von für-verständnis.
class Random[A](run: Long => (Long, A)) {
def apply(seed: Long) = run(seed)
def flatMap[B](f: A => Random[B]): Random[B] =
new Random({seed: Long => val (seed1, a) = run(seed); f(a)(seed1)})
def map[B](f: A => B): Random[B] =
new Random({seed: Long = val (seed1, a) = run(seed); (seed1, f(a))})
}
Nun fügen wir ein @ hinFabri -Funktion zum Erstellen eines trivialenRandom[A]
(was übrigens eher absolut deterministisch als "zufällig" ist) Dies ist einRückkeh funktionieren alsRückkeh in Haskell).
def certain[A](a: A) = new Random({seed: Long => (seed, a)})
Random[A]
ist einBerechnun ergibt einen Zufallswert vom Typ A. Die MethodenflatMap
, map
und Funktionunit
dient composing einfache Berechnungen, um komplexere zu erstellen. Zum Beispiel werden wir zwei @ komponierRandom[Double]
bauenRandom[(Double, Double)]
.
Nun, wenn wir eine Monade haben, sind wir bereit zu überdenkenrandomPoint
undrandomDouble
. Jetzt definieren wir sie anders als Funktionen, die @ ergebeRandom[Double]
undRandom[(Double, Double)]
def randomDouble(): Random[Double] = new Random({seed: Long => ... })
def randomPoint(): Random[(Double, Double)] =
randomDouble().flatMap(x => randomDouble().flatMap(y => certain(x, y))
Diese Implementierung istbesse als das vorherige, da es ein @ verwendgenerisc tool flatMap
undcertain
) um zwei Aufrufe von @ zu erstellRandom[Double]
und buildRandom[(Double, Double)]
.
Now kann dieses Tool wiederverwenden, um weitere Funktionen zu erstellen, die zufällige Werte generieren.
Monte-Carlo-Berechnung von Pi Jetzt können wir @ verwendmap
um zu testen, ob sich ein zufälliger Punkt im Kreis befindet:
def randomCircleTest(): Random[Boolean] =
randomPoint().map {case (x, y) => x * x + y * y <= 1}
Wir können auch eine Monte-Carlo-Simulation in Bezug auf @ definiereRandom[A]
def monteCarlo(test: Random[Boolean], trials: Int): Random[Double] = ...
und schließlich die Funktion zur Berechnung von PI
def pi(trials: Int): Random[Double] = ....
Alle diese Funktionen sind rein. Nebenwirkungen treten erst auf, wenn wir das @ endgültig anwendepi
-Funktion, um den Wert von pi zu erhalten.