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

scala – 为什么`Random.nextInt`被认为不是’可组合的,模块化的

发布时间:2020-12-16 08:53:20 所属栏目:安全 来源:网络整理
导读:从“ Scala中的函数式编程”一书中,有一节介绍如何以函数方式编写随机数生成器. 它给出了一个关于scala.util.Random的示例: Even if we didn’t know anything about what happens inside scala.util.Random,we can assume that the object rng has some in
从“ Scala中的函数式编程”一书中,有一节介绍如何以函数方式编写随机数生成器.

它给出了一个关于scala.util.Random的示例:

Even if we didn’t know anything about what happens inside scala.util.Random,we can assume that the object rng has some internal state that gets updated after each invocation,since we’d otherwise get the same value each time we called nextInt or nextDouble. Because the state updates are performed as a side effect,these methods aren’t referentially transparent. And as we know,this implies that they aren’t as testable,composable,modular,and easily parallelized as they could be.

最后一句,它说不是:

>可测试
>可组合
>模块化
>易于并行化

在后面的内容中,它解释了可测试部分,但如何理解其他3个方面?

解决方法

1.可测试

您无法轻易隔离其状态,尤其是在测试并行运行时.这使得在并行运行测试时不可能以可预测的方式再现一系列随机数.

2.可组合

您无法构建一系列操作,这些操作首先使用已知值为随机数播种,然后获取关于此种子的序列(特别是在并行运行时).

虽然这可能与“可测试”重叠,但出于安全原因,它可能也是必要的,因此您不会意外地将状态从一个会话泄漏到另一个会话.

3.模块化

没有办法交换不同的随机化算法.所有模块都必须使用相同的算法.这与“可组合”重叠,在某种意义上,您也不能将生成器作为(可能是隐式)参数提供,而不是种子.

4.并行化

所有线程只有一种状态.在高度并发的情况下,这可能是一个严重的瓶颈.

一些示例代码

请不要在生产中使用此代码.它非常笨拙,使用scalaz或cats可以轻松地构建更好的东西.通常,随机数生成器是一个comonad,它的状态是monad.

首先,让我们通过提供不同的伪随机生成器和不同的种子来使代码模块化

// This is a commonly used pseudo-random generator
// https://en.wikipedia.org/wiki/Linear_congruential_generator
// This implementation is awful slow!
def lcg(multiplier : BigInt,increment : BigInt,dividend : BigInt)(seed : BigInt) =
  (seed * multiplier + increment) mod dividend

// Some commonly used pseudo random number generators
def glibRand = lcg(multiplier = 1103515245,increment = 12345,dividend = 2^31) _
def vb6Rand  = lcg(multiplier = 214013,increment = 2531011,dividend = 2^31) _
def javaRand = lcg(multiplier = 25214903917L,increment = 11,dividend = 2L^48) _


// This is really insecure,don't use it for anything serious!
def seedFromTime() = BigInt(System.nanoTime())

// I used a dice to determine this value,so it's random!
def randomSeed = 5

然后让我们构建一个可以组成函数的实现:

case class Random[T,U](val generator : T => T,val seed : T)(val currentValue : U){
 def apply[V](f : (T,U) => V) : Random[T,V] = {
   val nextRandom = generator(seed)
   Random(generator,nextRandom)(f(nextRandom,currentValue))
  }

  def unsafeGetValue = currentValue
}

我们使用此构建块生成随机数列表并将一些随机数相加的一些示例:

val randomNumbers  = Stream.iterate(Random(glibRand,seedFromTime())(List.empty[BigInt])){r : Random[BigInt,List[BigInt]] =>
  r.apply{(rnd : BigInt,numbers : List[BigInt]) =>
    rnd :: numbers
  }
}

randomNumbers.take(10).last.unsafeGetValue

val randomSum = Stream.iterate(Random(glibRand,seedFromTime())(BigInt(0))) { r: Random[BigInt,BigInt] =>
  r.apply(_ + _)
}

randomSum.take(100).last.unsafeGetValue

像往常一样,当我们想要用函数式语言编写某些东西时,我们将编写函数来实现不同的目标.如果我们提供了monadic实现,我们也可以将函数与副作用结合起来.

在这种情况下,并行化和可测试性会自动跟随(像往常一样).

(编辑:李大同)

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

    推荐文章
      热点阅读