如何使用功能状态生成随机数?
How do I generate a random number using functional state?
我正在努力弄清楚如何将 State 的函数表示与 Scala 的 Random class 合并以生成随机整数。我正在学习 Functional Programming in Scala 一书,所以大部分代码都取自那里。
这是州 class 的样子,直接来自书中:
case class State[S, +A](run: S => (A, S))
这就是我想要做的:
object State {
type Rand[A] = State[A, Random] // the Random from scala.util.Random
def nextIntInRange(from: Int, to: Int): Rand[Int] =
??? _.nextInt(from - to) + from ??? // unsure about much of this
def get(a: Rand[A]): A = ??? // also unsure; should modify state
def getAndPreserveState(a: Rand[A]): A = ??? // ditto; should not modify state
def diceRolls(n: Int) = {
val roll = nextIntInRange(1, 6)
go(n: Int, acc: List[Int]): List[Int] = {
if (n >= 0) go(n-1, get(roll) :: acc) else acc
}
go(n, List())
}
我花了几个小时试图弄清楚如何使用 State 的构造函数,但尚未实现我想要的,因此几乎完全不了解如何实现前三种方法。
我的目标是能够将 diceRolls 与任何大小的整数和任何给定的起始种子一起使用,并生成一个永远不会改变的整数列表。换句话说,diceRolls(3)
可能是 List(3,3,2)
,如果是这样,将其重写为 diceRolls(7).take(3)
必须再次导致 List(3,3,2)
,依此类推。
我们想要生成随机数并将随机数生成器(截至目前的 RNG)(scala.util.Random
类型)作为您 State
class.[=61 中的状态=]
我们可以将类型Rand[A]
定义为:
type Rand[A] = State[Random, A]
我们希望能够得到一个范围内的随机整数。如果我们有 RNG,可以使用 :
轻松完成
def randomInRange(rng: Random, start: Int, end: Int) =
rng.nextInt(end - start + 1) + start
randomInRange(new Random(1L), 10, 20) // Int = 14
但是我们想使用之前状态的 RNG,所以我们在 run
函数中使用相同的代码定义了一个 State
:
def nextIntInRange(from: Int, to: Int): Rand[Int] =
State((r: Random) => (r.nextInt(to - from + 1) + from, r))
我们的 nextIntInRange
函数 return 是一个随机数和 RNG。让我们定义 roll
来测试它:
val roll = nextIntInRange(1, 6)
val rng = new Random(1L)
val (one, rng2) = roll.run(rng)
// (Int, scala.util.Random) = (4,scala.util.Random@5fb84db9)
val (two, rng3) = roll.run(rng2)
// (Int, scala.util.Random) = (5,scala.util.Random@5fb84db9)
到目前为止,我们可能认为一切顺利,但如果我们使用 rng
两次,我们希望收到相同的随机数:
val rng = new Random(1L)
val (one, _) = roll.run(rng) // 4
val (two, _) = roll.run(rng) // 5
我们得到了两个不同的数字,这不是我们使用 State
时想要的结果。我们希望使用相同 RNG return 的掷骰结果相同。问题是 Random
改变了它的内部状态,所以我们不能把后续的状态变化放在 State
.
在 Scala 中的函数式编程中,这个问题通过定义一个新的随机数生成器来解决,return它在 nextInt
上也有说明。
虽然使用 Random
违背了使用 State
的目的,但我们可以尝试实现其余功能作为教育练习。
让我们看看 get
和 getAndPreserveState
:
def get(a: Rand[A]): A = ??? // also unsure; should modify state
def getAndPreserveState(a: Rand[A]): A = ??? // ditto; should not modify state
如果我们查看类型签名,我们需要传递一个 Rand[A]
像我们的 roll
函数和 return 这个函数的结果。由于几个原因,这些功能很奇怪:
- 我们的
roll
函数需要一个 Random
实例来获取结果,但是我们没有 Random
. 类型的参数
- return 类型是
A
,所以如果我们有一个 Random
实例,我们可以 return 只有调用 a.run(ourRng)
后的随机数,但是我们应该如何处理我们的国家。我们希望明确地保持我们的状态。
让我们把那些试图失去我们宝贵状态的功能抛在脑后,并实现最终功能 diceRolls
,我们想在其中掷几次骰子和 return 列表随机数。因此,此函数的类型为 Rand[List[Int]]
.
我们已经有了一个函数 roll
,现在我们需要多次使用它,我们可以用 List.fill
:
List.fill(10)(roll) // List[Rand[Int]]
但是结果类型是 List[Rand[Int]]
而不是 Rand[List[Int]]
。从F[G[_]]
转换为G[F[_]]
是一个一般称为sequence
的操作,让我们直接为State
实现它:
object State {
def sequence[A, S](xs: List[State[S, A]]): State[S, List[A]] = {
def go[S, A](list: List[State[S, A]], accState: State[S, List[A]]): State[S, List[A]] =
list match {
// we have combined all States, lets reverse the accumulated list
case Nil =>
State((inputState: S) => {
val (accList, state) = accState.run(inputState)
(accList.reverse, state)
})
case stateTransf :: tail =>
go(
tail,
State((inputState: S) => {
// map2
val (accList, oldState) = accState.run(inputState)
val (a, nextState) = stateTransf.run(oldState)
(a :: accList, nextState)
})
)
}
// unit
go(xs, State((s: S) => (List.empty[A], s)))
}
}
Rand[Int]
案例的一些解释:
// use the RNG in to create the previous random numbers
val (accList, oldState) = accState.run(inputState)
// generate a new random number
val (a, nextState) = stateTransf.run(oldState)
// add the randomly generated number to the already generated random numbers
// and return the new state of the RNG
(a :: accList, nextState)
我对 State.sequence
的实现可以通过定义一个 unit
和一个 map2
函数来大大简化,就像他们在 fpinscala answers on github.[=61= 中所做的那样]
现在我们可以将 diceRolls
函数定义为:
def diceRolls(n: Int) = State.sequence(List.fill(n)(roll))
我们可以用作:
diceRolls(5).run(new Random(1L))
// (List[Int], scala.util.Random) = (List(4, 5, 2, 4, 3),scala.util.Random@59b194af)
我正在努力弄清楚如何将 State 的函数表示与 Scala 的 Random class 合并以生成随机整数。我正在学习 Functional Programming in Scala 一书,所以大部分代码都取自那里。
这是州 class 的样子,直接来自书中:
case class State[S, +A](run: S => (A, S))
这就是我想要做的:
object State {
type Rand[A] = State[A, Random] // the Random from scala.util.Random
def nextIntInRange(from: Int, to: Int): Rand[Int] =
??? _.nextInt(from - to) + from ??? // unsure about much of this
def get(a: Rand[A]): A = ??? // also unsure; should modify state
def getAndPreserveState(a: Rand[A]): A = ??? // ditto; should not modify state
def diceRolls(n: Int) = {
val roll = nextIntInRange(1, 6)
go(n: Int, acc: List[Int]): List[Int] = {
if (n >= 0) go(n-1, get(roll) :: acc) else acc
}
go(n, List())
}
我花了几个小时试图弄清楚如何使用 State 的构造函数,但尚未实现我想要的,因此几乎完全不了解如何实现前三种方法。
我的目标是能够将 diceRolls 与任何大小的整数和任何给定的起始种子一起使用,并生成一个永远不会改变的整数列表。换句话说,diceRolls(3)
可能是 List(3,3,2)
,如果是这样,将其重写为 diceRolls(7).take(3)
必须再次导致 List(3,3,2)
,依此类推。
我们想要生成随机数并将随机数生成器(截至目前的 RNG)(scala.util.Random
类型)作为您 State
class.[=61 中的状态=]
我们可以将类型Rand[A]
定义为:
type Rand[A] = State[Random, A]
我们希望能够得到一个范围内的随机整数。如果我们有 RNG,可以使用 :
轻松完成def randomInRange(rng: Random, start: Int, end: Int) =
rng.nextInt(end - start + 1) + start
randomInRange(new Random(1L), 10, 20) // Int = 14
但是我们想使用之前状态的 RNG,所以我们在 run
函数中使用相同的代码定义了一个 State
:
def nextIntInRange(from: Int, to: Int): Rand[Int] =
State((r: Random) => (r.nextInt(to - from + 1) + from, r))
我们的 nextIntInRange
函数 return 是一个随机数和 RNG。让我们定义 roll
来测试它:
val roll = nextIntInRange(1, 6)
val rng = new Random(1L)
val (one, rng2) = roll.run(rng)
// (Int, scala.util.Random) = (4,scala.util.Random@5fb84db9)
val (two, rng3) = roll.run(rng2)
// (Int, scala.util.Random) = (5,scala.util.Random@5fb84db9)
到目前为止,我们可能认为一切顺利,但如果我们使用 rng
两次,我们希望收到相同的随机数:
val rng = new Random(1L)
val (one, _) = roll.run(rng) // 4
val (two, _) = roll.run(rng) // 5
我们得到了两个不同的数字,这不是我们使用 State
时想要的结果。我们希望使用相同 RNG return 的掷骰结果相同。问题是 Random
改变了它的内部状态,所以我们不能把后续的状态变化放在 State
.
在 Scala 中的函数式编程中,这个问题通过定义一个新的随机数生成器来解决,return它在 nextInt
上也有说明。
虽然使用 Random
违背了使用 State
的目的,但我们可以尝试实现其余功能作为教育练习。
让我们看看 get
和 getAndPreserveState
:
def get(a: Rand[A]): A = ??? // also unsure; should modify state
def getAndPreserveState(a: Rand[A]): A = ??? // ditto; should not modify state
如果我们查看类型签名,我们需要传递一个 Rand[A]
像我们的 roll
函数和 return 这个函数的结果。由于几个原因,这些功能很奇怪:
- 我们的
roll
函数需要一个Random
实例来获取结果,但是我们没有Random
. 类型的参数
- return 类型是
A
,所以如果我们有一个Random
实例,我们可以 return 只有调用a.run(ourRng)
后的随机数,但是我们应该如何处理我们的国家。我们希望明确地保持我们的状态。
让我们把那些试图失去我们宝贵状态的功能抛在脑后,并实现最终功能 diceRolls
,我们想在其中掷几次骰子和 return 列表随机数。因此,此函数的类型为 Rand[List[Int]]
.
我们已经有了一个函数 roll
,现在我们需要多次使用它,我们可以用 List.fill
:
List.fill(10)(roll) // List[Rand[Int]]
但是结果类型是 List[Rand[Int]]
而不是 Rand[List[Int]]
。从F[G[_]]
转换为G[F[_]]
是一个一般称为sequence
的操作,让我们直接为State
实现它:
object State {
def sequence[A, S](xs: List[State[S, A]]): State[S, List[A]] = {
def go[S, A](list: List[State[S, A]], accState: State[S, List[A]]): State[S, List[A]] =
list match {
// we have combined all States, lets reverse the accumulated list
case Nil =>
State((inputState: S) => {
val (accList, state) = accState.run(inputState)
(accList.reverse, state)
})
case stateTransf :: tail =>
go(
tail,
State((inputState: S) => {
// map2
val (accList, oldState) = accState.run(inputState)
val (a, nextState) = stateTransf.run(oldState)
(a :: accList, nextState)
})
)
}
// unit
go(xs, State((s: S) => (List.empty[A], s)))
}
}
Rand[Int]
案例的一些解释:
// use the RNG in to create the previous random numbers
val (accList, oldState) = accState.run(inputState)
// generate a new random number
val (a, nextState) = stateTransf.run(oldState)
// add the randomly generated number to the already generated random numbers
// and return the new state of the RNG
(a :: accList, nextState)
我对 State.sequence
的实现可以通过定义一个 unit
和一个 map2
函数来大大简化,就像他们在 fpinscala answers on github.[=61= 中所做的那样]
现在我们可以将 diceRolls
函数定义为:
def diceRolls(n: Int) = State.sequence(List.fill(n)(roll))
我们可以用作:
diceRolls(5).run(new Random(1L))
// (List[Int], scala.util.Random) = (List(4, 5, 2, 4, 3),scala.util.Random@59b194af)