【问题标题】:Scala maintaining state in actors without varScala 在没有 var 的情况下保持演员的状态
【发布时间】:2011-04-27 22:17:18
【问题描述】:

在另一种语言中,我用新状态调用了一些东西来维护它,但我不知道如何使用 scala 演员来做到这一点。我的设想是这样的:

def act(state)
  react {
    case blah => act(doSomething(state, blah))
  }
}

【问题讨论】:

    标签: scala actor


    【解决方案1】:

    嗯..有趣的事情。我稍微调整了我的问题,发现它可以编译。我意识到发生的一切就是我必须返回一个部分函数。好吧,我可以返回一个有参数的部分函数,​​所以...

    import scala.actors._
    
    object MyActor extends Actor {
      def proc(s: Int) {
        react {
          case input: String =>
            if ((s % 10000) == 0) {
              println(s + ", " + input)
            }
            proc(s + 1)
        }
      }
    
      def act = proc(0)
    }
    
    object TestActors {
      def main(args: Array[String]): Unit = {
        MyActor.start()
    
        for (s <- 1 to 10000000) {
          MyActor ! "a message"
        }
      }
    }
    

    好消息是,只要稍加调整,就可以很容易地概括它。

    【讨论】:

    • 在这种情况下,将 @tailrec 注释添加到 proc 方法 (stackoverflow.com/questions/3114142/…) 是明智的。以确保您没有炸毁您的堆栈。
    • 谢谢。我确实运行了 100 万次以确保我不是,但是让编译器检查这很好。我添加了它,它给了我一个错误,说它无法优化它,这有点奇怪,因为我本来会炸毁一百万次堆栈。
    • 我对这里的@tailrec 注释有误; proc 调用不是最后一次调用;反应是。因此它不能是尾递归的。根据这个线程scala-programming-language.1934581.n4.nabble.com/…,堆栈溢出不太可能发生。
    • @thoredge 很好的分析在这里。
    【解决方案2】:

    还有另外两种方法可以在不使用 var 的情况下将状态存储在 Actor 中。如果你只需要传递状态,你可以让actor向自己发送消息:

    object MailsSelf {
      import scala.actors._
      class Selfish extends Reactor[Any] {
        def act() { loop { react {
          case i: Int if (i>=0) =>
            println(i)
            this ! (i-1)  // Self-messaging instead of recursive call
          case _ => exit
        }}}
      }
      def main(args: Array[String]) {
        (new Selfish).start() ! 5
      }
    }
    

    或者,如果您需要保存在其他消息期间可访问的状态,您可以创建另一个 Actor;打电话给你的人都需要被告知新演员:

    object EndlessActors {
      import scala.actors._
      class Delegater(n: Int) extends ReplyReactor {
        def act() { loop { react {
          case i: Int if (i*n >= 0) =>
            println(i*n)
            val next = new Delegater(n-1)
            next.start()
            reply(Some(next))
          case _ => reply(None); exit
        }}}
      }
      def main(args: Array[String]) {
        val original = new Delegater(5)
        original.start()
        Iterator.iterate( Option(original) ){ maybe =>
          maybe.flatMap(d => {
            val result = (d !? 5)
            result match {
              case Some(d2: Delegater) => Some(d2)
              case _ => None
            }
          })
        }.takeWhile(_.isDefined).foreach(_ => {})  // Foreach forces evaluation
      }
    }
    

    就我个人而言,我认为这样做很愚蠢。这是非常低效的(必须创建一个新的actor,并在任何时候状态更改时处理一个旧的actor——而且actor 不是那么轻量级的!),并且它使代码大大复杂化。在大多数情况下,将您的可变状态安全地隐藏为演员内的private[this] var 更为实用,这样您就知道只有演员本身可以更改其可变状态。或者,您可以不使用新的参与者进行回复,而是使用调用者应该传回的状态信息进行回复,但这稍微不安全,因为原则上他们可以修改状态信息,而不仅仅是再次传回。

    【讨论】:

    • 我不认为第一个能满足我的需要。客户端发送输入,参与者自身发送状态——但它们不会同时被处理,所以它不能同时访问状态和输入,这就是重点。另外,我不明白你为什么说它更短——它看起来并不比任何东西都短。第二个我没有关注,当然很复杂。另一个答案中的解决方案没有堆栈溢出问题并且可以满足需要。我不确定你有什么问题。
    • @taotree - 我想它可以正常工作;我将一个过满的邮箱误解为递归闭包生成。我把“两种方式”改成了“另外两种方式”。
    猜你喜欢
    • 2010-11-16
    • 1970-01-01
    • 2019-04-23
    • 1970-01-01
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    相关资源
    最近更新 更多