Scala 纯函数随机数发生器-状态单子

Scala 纯函数随机数发生器-状态单子,scala,functional-programming,monads,state-monad,Scala,Functional Programming,Monads,State Monad,《Scala中的函数编程》一书演示了纯函数随机数生成器的示例,如下所示 trait RNG { def nextInt: (Int, RNG) } object RNG { def simple(seed: Long): RNG = new RNG { def nextInt = { val seed2 = (seed*0x5DEECE66DL + 0xBL) & ((1L <

《Scala中的函数编程》一书演示了纯函数随机数生成器的示例,如下所示

trait RNG {
    def nextInt: (Int, RNG)
}

object RNG {
    def simple(seed: Long): RNG = new RNG {
        def nextInt = {
            val seed2 = (seed*0x5DEECE66DL + 0xBL) &
                        ((1L << 48) - 1)
            ((seed2 >>> 16).asInstanceOf[Int],
             simple(seed2))
        }
    }
}
我得到的部分是,它是一个纯函数,因为它返回下一个状态,并将其留在API客户端上,以便在下次需要随机数时使用它调用
nextInt
,但我不理解的是“第一个随机数将如何生成,因为我们必须至少提供一次
seed

是否应该有另一个功能来提升
种子
,以获得
RNG
?如果是这样,那么我们如何期望这个API的客户机知道它(因为在非功能性实现中,用户只需调用
nextInt
,并且状态由API维护)

有人能给出一个Scala中纯函数随机数生成器的完整示例,并将其与状态单子联系起来吗

有人能给出一个Scala中纯函数随机数生成器的完整示例,并将其与状态单子联系起来吗

这是一个纯粹的功能性RNG

val state0 = RNG.simple(1234)
val (r1, state1) = state0.nextInt
val (r2, state2) = state1.nextInt
// etc.
val (x, _) = state1.nextInt
assert (x == r2)

此外,您的示例很好地结合了Scala streams:

  def randStream(r: RNG): Stream[Int] = r.nextInt match {
    case (value, next) => value #:: randStream(next)
  }

  val rng = randStream(RNG.simple(123))
  println(rng.take(10).toList)
  println(rng.take(5).toList)

这个随机发生器是纯功能的,对于相同的输入,你总是得到相同的输出。非纯功能部分留给该API的用户(您)

要以纯功能的方式使用
RNG
,您必须始终使用相同的初始值对其进行初始化,但是您将始终获得相同的数字序列,这不是很有用

否则,您将不得不依赖于外部系统的
RNG
初始化(通常是挂钟时间),从而引入副作用(再见,纯功能)


[编辑]

受@Aivean答案的启发,我创建了自己版本的randoms
Stream

def randoms: Stream[Int] = Stream.from(0)
  .scanLeft((0, RNG.simple(System.currentTimeMillis)))((st, _) => st._2.nextInt)
  .tail
  .map(_._1)

println(randoms.take(5).toList)
println(randoms.filter(_ > 0).take(3).toList)

从基于@Aivean
Stream
的函数派生的递归函数

def rand(n: Int, r: RNG): List[Int] = { 
  if (n < 0) List() 
  else {
    val (value, next) = r.nextInt
    value :: rand(n-1, next) 
  }
}

顺便说一句,如果你真的想学习纯函数式编程,你应该试试Haskell。因为在Haskell中,如果不使用
IO
monad“戳记”函数,或者如果要更改状态,则使用
state
monad,则无法引入副作用。即使你不在日常生活中使用它,对我来说,它也是学习函数式编程的最好语言。相反,Scala过于放任,在实际项目中使用可能很好,但很难看到功能世界和命令世界之间的界限。关于完全没有副作用,Haskell背后的伟大人物之一西蒙·佩顿·琼斯(Simon Peyton Jones)很好地说:
def randoms: Stream[Int] = Stream.from(0)
  .scanLeft((0, RNG.simple(System.currentTimeMillis)))((st, _) => st._2.nextInt)
  .tail
  .map(_._1)

println(randoms.take(5).toList)
println(randoms.filter(_ > 0).take(3).toList)
def rand(n: Int, r: RNG): List[Int] = { 
  if (n < 0) List() 
  else {
    val (value, next) = r.nextInt
    value :: rand(n-1, next) 
  }
}
rand(5, RNG.simple(System.currentTimeMillis) )