Dev*_*aha 6 monads functional-programming scala 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 << 48) - 1)
((seed2 >>> 16).asInstanceOf[Int],
simple(seed2))
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法看起来像
val (randomNumber,nextState) = rng.nextInt
Run Code Online (Sandbox Code Playgroud)
我确实得到了它是纯函数的部分,因为它返回下一个状态并将其留在API客户端上,以便nextInt在下次需要随机数时使用它来调用但我不明白的是' 第一个随机的怎么样生成数字,因为我们必须seed至少提供一次.
如果有另一个功能提升seed得到RNG?如果是这样,那么我们如何期望这个API的客户端知道它(因为在非功能实现中用户只是调用nextInt并且状态由API维护)
有人可以给出Scala中纯函数随机数生成器的完整示例,并且可能将它与状态Monad关联起来.
此外,您的示例与 Scala 流很好地结合在一起:
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)
Run Code Online (Sandbox Code Playgroud)
该随机发生器RNG是纯功能的,对于相同的输入,您总是获得相同的输出.非纯功能部分留给该API的用户(您).
要RNG以纯函数方式使用,必须始终使用相同的初始值对其进行初始化,但随后您将始终获得相同的数字序列,这不是很有用.
否则,您将不得不依赖于RNG外部系统的初始化(通常是挂钟时间),因此引入副作用(再见纯函数).
val state0 = RNG.simple(System.currentTimeMillis)
val (rnd1, state1) = state0.nextInt
val (rnd2, state2) = state1.nextInt
val (rnd3, state3) = state2.nextInt
println(rnd1, rnd2, rnd3)
Run Code Online (Sandbox Code Playgroud)
[编辑]
受到@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)
Run Code Online (Sandbox Code Playgroud)