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

scala – 如何使用功能状态生成随机数?

发布时间:2020-12-16 21:31:24 所属栏目:安全 来源:网络整理
导读:我正在努力找出如何将状态的功能表示与 Scala的Random类合并以生成随机整数.我正在Scala的功能编程书中学习,所以大部分的代码都是从那里获取的. 这是国家班级的直接看法: case class State[S,+A](run: S = (A,S)) 这就是我要做的: object State { type Ran
我正在努力找出如何将状态的功能表示与 Scala的Random类合并以生成随机整数.我正在Scala的功能编程书中学习,所以大部分的代码都是从那里获取的.

这是国家班级的直接看法:

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

我花了几个小时试图找出如何与国家的建设者合作,还没有达到我想要的目的,因此对如何实施前三种方法几乎完全缺乏理解.

我的目标是能够使用任何大小的整数和任何给定的起始种子的diceRolls,并生成一个永远不变的整数列表.换句话说,diceRolls(3)可能是List(3,3,2),如果是,则将其重写为diceRolls(7).take(3)必须在List(3,2)中再次导出,因此上.

解决方法

我们要生成随机数,并将随机数生成器(现在的RNG)(类型为scala.util.Random)保留为State类中的状态.

我们可以将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,所以我们在运行函数中定义一个具有相同代码的状态:

def nextIntInRange(from: Int,to: Int): Rand[Int] = 
  State((r: Random) => (r.nextInt(to - from + 1) + from,r))

我们的nextIntInRange函数返回一个随机数和RNG.让我们定义滚动来测试它:

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

我们有两个不同的数字,这不是我们想要的,当我们使用州.我们希望使用相同的RNG滚动返回相同的结果.问题是随机会突变其内部状态,所以我们不能将后续状态更改置于状态.

在Scala的功能编程中,这个问题是通过定义一个新的随机数生成器来解决的,它也在nextInt上返回它的状态.

虽然随机使用国家的目的,我们可以尝试执行其余的功能作为教育活动.

让我们来看看get和getAndPreserveState:

def get(a: Rand[A]): A = ??? // also unsure; should modify state

def getAndPreserveState(a: Rand[A]): A = ??? // ditto; should not modify state

如果我们看类型签名,我们需要像我们的roll函数一样传递一个Rand [A],并返回这个函数的结果.由于以下几个原因,这些功能是奇怪的:

>我们的滚动函数需要一个随机实例来获得结果,但是我们没有类型为Random的参数.
>返回类型是A,所以如果我们有一个Random实例,我们可以在调用a.run(ourRng)后返回随机数,但是我们应该怎么处理我们的状态.我们要明确地保持我们的状态.

让我们离开这些功能,试图摆脱我们宝贵的状态,并实现最终的功能diceRolls,我们想在其中滚动一个骰子几次,并返回一个随机数列表.因此,此函数的类型将为Rand [List [Int]].

我们已经有了一个功能卷,现在我们需要多次使用它,我们可以用List.fill:

List.fill(10)(roll) // List[Rand[Int]]

然而,所得到的类型是List [Rand [Int]],而不是Rand [List [Int]].从F [G [_]]转换为G [F [_]]是一个称为序列的操作,可以直接实现状态:

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

}

兰德[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)

通过定义一个单元和一个map2函数就可以清理State.sequence的实现,就像他们在fpinscala answers on github中所做的那样.

现在我们可以将我们的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)

(编辑:李大同)

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

    推荐文章
      热点阅读