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 Generator

Zunä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.

Random Point

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.

MonadZufälli

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)].

Monadic Random Point

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.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage