【问题标题】:Similar to for-loop in functional Scala类似于函数式Scala中的for循环
【发布时间】:2020-06-12 07:30:34
【问题描述】:

我想创建一个仿真模型来学习 Scala 和函数式编程 (FP)。我已经有了所有的逻辑:创建一组代理(它只是一个List[Agent],其中Agent 是一个定义单个成员的类,就像气体中的一个粒子)和一些功能(例如在空间中移动) ) 对人口“行动”。

我的问题出现了,因为 FP 中的不变性,我想多次对初始种群应用相同的函数。我想将这些函数应用于 N 轮的初始总体(在应用所有函数后定义一轮)。我不知道如何处理轮次之间的不可变值。

通常,我会做一个 for 循环,其中一个变量会改变它的值,但是当值是不可变的时你如何处理呢?

我的代码现在看起来像这样:

object Main extends App {
    val soc = Society(numAgents = 1000)        // Create a Society

    val agents = soc.initSociety()             // Init the society
    val movedAgents = soc.moveAgents(agents)   // Move the agents
}

方法被定义为返回List[Agent],因此类型始终相同。

我已经看到一些使用foldleft 的解决方案,但我需要将函数 moveAgents 应用于它返回的内容。

【问题讨论】:

    标签: scala functional-programming simulation


    【解决方案1】:

    您可以通过折叠获得moveAgents 的返回值。如果你只是想调用moveAgents方法n次,你可以这样做

    val newAgents = (1 to n).foldLeft(soc.initSociety()) { (a, i) => soc.moveAgents(a) }
    

    这相当于使用n 调用soc.moveAgents(soc.moveAgents(...(soc.initSociety())))moveAgents 执行soc.moveAgents(soc.moveAgents(...(soc.initSociety())))

    如果您有多个要应用的功能(每轮使用不同的功能),您可以执行相同的操作:

    // n/3 because there are 3 functions
    val newAgents = (1 to n/3).foldLeft(soc.initSociety()) { (a, i) => f3(f2(f1(a))) }
    

    如果你有一个List 的函数,你可以试试这个:

    val fs = List(f1, f2, f3)
    val newAgents = (1 to (n/fs.size)).foldLeft(soc.initSociety()){ (a, i) => fs.foldLeft(a){ (ag, f) => f(ag) } }
    

    【讨论】:

    • 谢谢!如果它们不止一种功能?例如,如果一轮是先申请f1(),然后f2(),然后f3()(然后返回f1)?
    • 你可以改用f3(f2(f1)))
    • @plr 您可以应用所有步骤,foldLeft 以非常简单的方式工作。你有一些初始值和一个来自一些中间状态的函数和列表的当前值,然后返回一个新状态。
    • @plr 更新了我的答案以获取列表(或其他集合)
    【解决方案2】:

    如果每轮之间的中间值有任何兴趣...

    val rounds = List.iterate(agents, n)(f _ andThen g andThen h)
    

    【讨论】:

      【解决方案3】:

      任何简单的 for 循环都可以很容易地重写为 tal 递归,而 (tail) 递归通常可以写为 foldLeft

      第一种方法,简单循环。

      def stimulate(soc: Society, n: Int): List[Agent] = {
        var agents = soc.initSociety()
        for (i <- 0 to n) {
          agents = soc.moveAgents(agents)
        }
        agents
      }
      

      第二种方法,递归。
      (让我们删除那个 var)

      def stimulate(soc: Society, n: Int): List[Agent] = {
        @annotation.tailrec
        def loop(i: Int, agents: List[Agent]): List[Agent] =
          if (i < n) loop(i + 1, agents = soc.moveAgents(agents))
          else agents
      
        loop(i = 0, agents = soc.initSociety())
      }
      

      第三种方法,折叠。
      (让我们从递归中删除样板)

      def stimulate(soc: Society, n: Int): List[Agent] =
        (0 to n).foldLeft(soc.initSociety()) { case (agents, _) =>
          soc.moveAgents(agents)
        }
      

      【讨论】:

      • 在实践中,使用foldLeft 将实现返回到while 循环,该循环累积到var,但至少它是一个库函数而不是用户代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-16
      • 1970-01-01
      相关资源
      最近更新 更多