【发布时间】:2019-09-06 16:38:37
【问题描述】:
我试图了解如何利用 scala 中的 monad 来解决简单的问题,以此来增加我的熟悉度。一个简单的问题是使用功能随机数生成器估计 PI。我将下面的代码包含在一个简单的基于流的方法中。
我正在寻求帮助,将其转化为单子方法。例如,是否有一种惯用的方式将此代码转换为以堆栈安全的方式使用状态(和其他 monad)?
trait RNG {
def nextInt: (Int, RNG)
def nextDouble: (Double, RNG)
}
case class Point(x: Double, y: Double) {
val isInCircle = (x * x + y * y) < 1.0
}
object RNG {
def nonNegativeInt(rng: RNG): (Int, RNG) = {
val (ni, rng2) = rng.nextInt
if (ni > 0) (ni, rng2)
else if (ni == Int.MinValue) (0, rng2)
else (ni + Int.MaxValue, rng2)
}
def double(rng: RNG): (Double, RNG) = {
val (ni, rng2) = nonNegativeInt(rng)
(ni.toDouble / Int.MaxValue, rng2)
}
case class Simple(seed: Long) extends RNG {
def nextInt: (Int, RNG) = {
val newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL
val nextRNG = Simple(newSeed)
val n = (newSeed >>> 16).toInt
(n, nextRNG)
}
def nextDouble: (Double, RNG) = {
val (n, nextRNG) = nextInt
double(nextRNG)
}
}
}
object PI {
import RNG._
def doubleStream(rng: Simple):Stream[Double] = rng.nextDouble match {
case (d:Double, next:Simple) => d #:: doubleStream(next)
}
def estimate(rng: Simple, iter: Int): Double = {
val doubles = doubleStream(rng).take(iter)
val inside = (doubles zip doubles.drop(3))
.map { case (a, b) => Point(a, b) }
.filter(p => p.isInCircle)
.size * 1.0
(inside / iter) * 4.0
}
}
// > PI.estimate(RNG.Simple(10), 100000)
// res1: Double = 3.14944
我怀疑我正在从 cat 中的 Applicative monad 中寻找类似 @987654322@ 的东西,但我不确定如何排列类型或如何以不会累积中间结果的方式进行排列在记忆中。或者,有没有办法通过 for 理解来迭代地构建 Points?
【问题讨论】:
标签: scala monads scala-cats