2011-04-27 68 views
3

在另一種語言中,我有一些自己與新狀態調用來維護它,但我沒有看到如何與scala演員做到這一點。這樣的事情是我如何設想:斯卡拉保持狀態在沒有var的演員

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

回答

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

的好處是多一點的調整,它可以很容易地一概而論。

+1

像這樣的情況下,將@tailrec註釋添加到proc方法是明智的(http://stackoverflow.com/questions/3114142/what-is-the-scala-annotation-to-ensure-a-尾遞歸函數-被優化)。確保你不會炸燬你的堆棧。 – thoredge 2011-04-27 22:52:06

+0

謝謝。我確實運行了100萬次,以確保不是,但讓編譯器檢查是否正確。我添加了它,它給了我一個錯誤,說它不能優化它,這有點奇怪,因爲我會盡管有一百萬次會炸掉一個堆棧,否則。 – mentics 2011-04-28 01:19:18

+3

我在這裏錯了@tailrec註釋; proc電話不是最後一個電話;反應是。因此它不能是尾遞歸。根據這個線程http://scala-programming-language.1934581.n4.nabble.com/scala-Tail-recursion-question-td1990627.html堆棧溢出不太可能發生。 – thoredge 2011-04-28 07:50:40

0

還有兩種方法可以在不使用變量的情況下在演員中存儲狀態。如果你只需要直傳狀態,可以讓演員將消息發送到本身:

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

另外,如果您需要保存狀態在其他消息是可訪問的,您可以創建另一個演員;誰打電話給你,然後需要通知新的演員:

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

我個人認爲這樣做是愚蠢的。這是非常低效的(一個新的演員必須被創建,並且一箇舊的演員會隨時改變狀態 - 而演員並不是輕量級的!),並且它實質上使代碼複雜化了。在大多數情況下,在演員中保持隱藏的可變狀態爲private[this] var更爲實用,以便您知道只有演員本身可以更改其可變狀態。另外,你可以不用新的actor來回答,而是用調用者應該返回的狀態信息來回答,但這樣做的安全性稍差,因爲他們原則上可以修改狀態信息而不是僅僅傳遞回來。

+0

我不認爲第一個做我需要的。客戶端發送輸入,並且主角本身發送自己的狀態 - 但是他們從不被同時處理,所以它不能同時訪問狀態和輸入,這是整個點。另外,我不明白你爲什麼說它更短 - 它不會比任何東西都短。 而第二個我沒有遵循,當然是複雜的。 另一個答案中的解決方案沒有堆棧溢出問題,並且不需要做什麼。我不確定你有什麼問題。 – mentics 2011-04-28 15:46:32

+0

@taotree - 我想它可以正常工作;我誤解了一個溢出郵箱作爲遞歸閉包生成。我已經將「兩種方式」改變爲「兩種其他方式」。 – 2011-04-28 16:45:58