代码之家  ›  专栏  ›  技术社区  ›  Jozef

以函数方式重新编写scala中的集合填充模拟

  •  1
  • Jozef  · 技术社区  · 6 年前

    以一个非常简单的模拟为例,我们在模拟一个人在一年中的生存或死亡-根据生成的随机数,我们决定此人是否生存一年:

    case class PersonEntry(pid: Long, year: Short, age: Short, status: Byte) {
    
      // return an entry where person survives and gets 1 year older
      def Mature(): PersonEntry = {
        PersonEntry(pid, (year + 1).toShort, (age + 1).toShort, status)
      }
    
      // return an entry where person dies (status is 0)
      def Kill(): PersonEntry = {
        PersonEntry(pid, (year + 1).toShort, (age + 1).toShort, 0)
      }
    
      // based on a random number decide if return Matured or Killed
      def Simulate(p: Double = 0.5): PersonEntry = {
        val rnd = scala.util.Random
        if (rnd.nextDouble < p) this.Mature() else this.Kill() 
      }
    
    }
    

    基于此,我们可以为同一个人获得一个新的条目,模拟1年的生存概率为95%:

    val per1 = PersonEntry(1, 2018, 20, 1)
    val per2 = per1.Simulate(0.95)
    

    我们接下来要做的是创建一个任意年份的模拟。到目前为止,我所创造的是一个非常直截了当的方法:

    import scala.collection.mutable.ListBuffer
    
    case class PersonSimulation(entries: ListBuffer[PersonEntry]) {
     def Simulate(n: Int = 1, p: Double = 0.5): Unit = {
       for (i <- List.range(1, n)) {
         this.entries += this.entries.last.Simulate(p)
       }
     }
    }
    

    现在我们可以做到:

    val per = PersonEntry(1, 2018, 20, 1)
    val sim = PersonSimulation(ListBuffer(per))
    sim.Simulate(100, 0.95)
    
    // look at the result
    println(sim)
    

    这个 PersonSimulation.Simulate 方法执行该作业,但根本不起作用,因为它向 entries ListBuffer .

    我们如何有效地重写 PersonSimulation 以一种实用的方式?

    2 回复  |  直到 6 年前
        1
  •  2
  •   Travis Brown    6 年前

    签名与 Simulate 但是你可以用 iterate 你会找到的方法 List ,请 Stream 等:

    val entry = PersonEntry(1, 2018, 20, 1)
    val entries = List.iterate(entry, 100)(_.Simulate(0.95))
    

    上面写着“从 entry ,呼叫 .Simulate(0.95) 打开它,然后打电话 模拟(0.95) 在您得到的结果上,然后在该结果上,连续100次将结果收集到一个列表中”,例如:

    scala> entries.foreach(println)
    PersonEntry(1,2018,20,1)
    PersonEntry(1,2019,21,1)
    PersonEntry(1,2020,22,1)
    PersonEntry(1,2021,23,1)
    PersonEntry(1,2022,24,1)
    PersonEntry(1,2023,25,1)
    PersonEntry(1,2024,26,1)
    PersonEntry(1,2025,27,1)
    PersonEntry(1,2026,28,1)
    PersonEntry(1,2027,29,1)
    PersonEntry(1,2028,30,1)
    PersonEntry(1,2029,31,1)
    PersonEntry(1,2030,32,1)
    PersonEntry(1,2031,33,0)
    PersonEntry(1,2032,34,0)
    ...
    

    河流 您甚至不必预先设置迭代次数:

    val entries = Stream.iterate(entry)(_.Simulate(0.95))
    

    现在,您的模拟中有无限的年份流,您可以用其中的一部分来查看:

    scala> entries.take(10).foreach(println)
    PersonEntry(1,2018,20,1)
    PersonEntry(1,2019,21,1)
    PersonEntry(1,2020,22,1)
    PersonEntry(1,2021,23,1)
    PersonEntry(1,2022,24,1)
    PersonEntry(1,2023,25,1)
    PersonEntry(1,2024,26,0)
    PersonEntry(1,2025,27,0)
    PersonEntry(1,2026,28,0)
    PersonEntry(1,2027,29,0)
    

    请注意,这些解决方案都不是 纯粹地 功能性,因为您依赖于随机数生成器,并且每次运行程序时都会得到不同的结果,但从某种意义上讲,它的功能是避免使用可变集合来收集结果。

        2
  •  1
  •   Ivan Aristov    6 年前

    如果您想使用功能性的方式-使用不可变的数据结构。 对case类使用方法copy。 您可以创建另一个case类,但可以创建隐式方法。

    我们在这里:

    import scala.util.Random
    
    case class PersonEntry(pid: Long, year: Short, age: Short, status: Byte) {
    
      // return an entry where person survives and gets 1 year older
      def Mature: PersonEntry = {
        copy(year = (year + 1).toShort, age = (age + 1).toShort)
      }
    
      // return an entry where person dies (status is 0)
      def Kill: PersonEntry = {
        copy(year = (year + 1).toShort, age = (age + 1).toShort, status = 0)
      }
    
      // based on a random number decide if return Matured or Killed
      def Simulate(p: Double = 0.5): PersonEntry = {
        val rnd = Random.nextDouble
        if (rnd < p) Mature else Kill
      }
    
    }
    
    implicit class PersonListExt(l: List[PersonEntry]) {
      def simulate(n: Int = 1, p: Double = 0.5): Map[Int, List[PersonEntry]] = {
        (1 to n).map(_ -> l.map(_.Simulate(p))).toMap
      }
    }
    
    val simulateMap: Map[Int, List[PersonEntry]]  = (1 to 10).map(i =>
      PersonEntry(i, 2018.toShort, Random.nextInt(50).toShort, 1)
    ).toList.simulate()