加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

了解Scala中的随机单子

发布时间:2020-12-16 09:31:34 所属栏目:安全 来源:网络整理
导读:这是我之前 question的后续行动 Travis Brown指出,java.util.Random是副作用的,并建议一个随机的monad Rng library使代码纯功能。现在我试图用自己的方式构建一个简化的随机单子,以了解它的工作原理。 是否有意义 ?你将如何修正/改进下面的解释? 随机发
这是我之前 question的后续行动

Travis Brown指出,java.util.Random是副作用的,并建议一个随机的monad Rng library使代码纯功能。现在我试图用自己的方式构建一个简化的随机单子,以了解它的工作原理。

是否有意义 ?你将如何修正/改进下面的解释?

随机发生器

首先我们剽窃java.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) = ...

请注意,接下来将返回新的种子和值,而不仅仅是值。我们需要它将新的种子传递给另一个函数调用。

随机点

现在我们来写一个函数来生成单位平方的随机点。

假设我们有一个函数来生成范围[0,1]中的随机双精度

def randomDouble(seed: Long): (Long,Double) = ... // some bit magic

现在我们可以编写一个函数来生成一个随机点。

def randomPoint(seed: Long): (Long,(Double,Double)) = {
   val (seed1,x) = randomDouble(seed)
   val (seed2,y) = randomDouble(seed1)
   (seed2,(x,y)) 
}

到目前为止,这么好,randomDouble和randomPoint都是纯的。唯一的问题是我们组合randomDouble来构建randomPoint特设。我们没有一个通用的工具来组合产生随机值的函数。

莫纳德随机

现在我们将定义一个通用工具来组合产生随机值的函数。首先,我们推断randomDouble的类型:

type Random[A] = Long => (Long,A) // generate a random value of type A

然后在其周围构建一个包装类。

class Random[A](run: Long => (Long,A))

我们需要包装器来定义方法flatMap(在Haskell中绑定)和由for-comprehension使用的map。

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))})
}

现在我们添加一个工厂函数来创建一个简单的Random [A](这绝对是确定性的,而不是“随机”),这是一个返回函数(作为Haskell中的返回)。

def certain[A](a: A) = new Random({seed: Long => (seed,a)})

随机[A]是产生类型A的随机值的计算。平面图,地图和功能单元的方法用于构建简单的计算以构建更复杂的。例如,我们将组成两个Random [Double]来构建Random [(Double,Double)]。

单体随机点

现在,当我们有一个monad时,我们准备再次访问randomPoint和randomDouble。现在我们将它们定义为不同的函数,产生Random [Double]和Random [(Double,Double)]

def randomDouble(): Random[Double] = new Random({seed: Long => ... })
def randomPoint(): Random[(Double,Double)] =
  randomDouble().flatMap(x => randomDouble().flatMap(y => certain(x,y))

这个实现比前一个更好,因为它使用通用工具(flatMap和certain)来组合Random [Double]的两个调用并构建Random [(Double,Double)]。

现在可以重新使用此工具来构建更多生成随机值的函数。

蒙特卡罗计算Pi

现在我们可以使用map来测试一个随机点在圆圈中:

def randomCircleTest(): Random[Boolean] = 
  randomPoint().map {case (x,y) => x * x + y * y <= 1}

我们还可以根据Random [A]定义蒙特卡罗模拟,

def monteCarlo(test: Random[Boolean],trials: Int): Random[Double] = ...

最后计算PI的功能

def pi(trials: Int): Random[Double] = ....

所有这些功能都是纯粹的。副作用只有当我们最终应用pi函数才能获得pi的值时才会发生。

解决方法

你的方法很好,虽然有点复杂。我也建议你看看 Functional Programming in Scala的第6章作者:Paul Chiusano和Runar Bjarnason。 该章节称为纯功能状态,它显示了如何创建纯函数的随机生成器并定义其数据类型代数,以具有完整的功能组合支持。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读