æåã®èšäºã§æžããããã«ããã»ã©åã§ã¯ãªããC ++ããScalaã«åãæ¿ããŸããã ããã«äŒŽããç§ã¯ã¢ãã«ãæŒãã俳åªã®ã¢ãã«ãç 究ãå§ããŸããã ç§ã«ãšã£ãŠæãé®®æãªå°è±¡ã¯ããã®ã©ã€ãã©ãªãæäŸããæéç¶æ ãã·ã³ïŒFSMïŒã®å®è£ ãšãã¹ãã®å®¹æãã§ããã Akkaã«ã¯ä»ã«ããã°ããã䟿å©ãªãã®ãããããããã®ã§ããªããããèµ·ãã£ãã®ãã¯ããããŸããã ããããä»ã§ã¯ãæåã®Scalaãããžã§ã¯ãã§ãéœåã®è¯ãããšã«è£ä»ããããããããæ©äŒã«ã¹ããŒããã·ã³ã䜿çšããŠããŸãïŒå¿ããé¡ã£ãŠããŸãïŒã ããã§ãç§ã¯Akka.FSMã«ã€ããŠã®ç¥èã ãã§ãªããç§ãèç©ããããã€ãã®ããªãã¯ãšå人çãªãã¹ããã©ã¯ãã£ã¹ãã³ãã¥ããã£ãšå ±æããæºåãã§ããŠãããšå€æããŸããã ç§ã¯ããã§åæ§ã®ãããã¯ãèŠã€ããŸããã§ããïŒãããŠãäžè¬çã«ScalaãšAkkaã«ã€ããŠã®èšäºã§ã¯ãããã¯ã©ããããããããŸããããŸããã§ããïŒã ãããŠããããéå±ããªãããã«-ç§ã¯äžç·ã«æ¬ç©ã®é»åç«ã®è¡åãå®è£ ããããšãææ¡ããŸãã ç§ã®èšäºã«è§Šçºãããå€ç¬ãªããã³ãã£ãã¯ãªéãã宿é¡ãšããŠæ¬æ ŒçãªãããŸãã£ã¡ãã«æäŸããæ©èœãæŽç·Žããããšä¿¡ãããã äž»ãªããšã¯ãã³ã¡ã³ãã§ã³ãã¥ããã£ãšçµæãå ±æããåŸããã®ãããªéã¯å¿ããªããšããããšã§ãã çæ³çã«ã¯ãå ±æã¢ã¯ã»ã¹ãåããgithubã§ãããžã§ã¯ããäœæãã誰ãããã©ã³ã¹ãã¥ãŒãããºã ã®ã¢ã€ãã¢ã®éçºã«èªåã®å人çãªè²¢ç®ãããããããšãã§ããããã«ããŸãã ãããŠä»-åè«ãšç©ºæ³ã®æ¹åã§ãç§ãã¡ã¯è¢ããŸãããŸãã æåããå§ããŸãã7Dãšãã¬ãŒã³ã¹ã®å¹æãé«ããããã«ãç§ã¯ããªããšãã¹ãŠã®ã¹ããããèžã¿ãŸãã TDDæ·»ä»ïŒèªèšŒãããŠããªãããã³ãããããã°ãããã¯ç¢ºãã«åè«ã§ã¯ãããŸããã
ãã®èšäºã®æ å ±ã¯ããã§ã«Scalaã«å°ãªããšãå°ãæ £ããŠãããå°ãªããšã俳åªã®ã¢ãã«ã«ã€ããŠè¡šé¢çãªç解ãæã£ãŠãã人ã察象ãšããŠããŸãã ç¥ãåãã«ãªãããããã©ãããå§ããã°ããã®ãåãããªã人ã®ããã«ãããŒãã¹ãšããŠãå°ããªéå§æ瀺ãæžããŠãããããã¿ãã¬ããªãããã«ã¹ãã€ã©ãŒã®äžã«é ããŸããã å¿ èŠãªãã¹ãŠã®ã©ã€ãã©ãªã䜿çšããŠãããŸãæéããããã«ã¯ãªãŒã³ãªScalaãããžã§ã¯ããäœæããæ¹æ³ã«ã€ããŠèª¬æããŸãã
ãã®ãããæ¢ã«ç解ããŠããããã«ãæåã«akka-actorãakka-testkitããã³scalatestã©ã€ãã©ãªã®ææ°ããŒãžã§ã³ã䜿çšããã¯ãªãŒã³ãªãããžã§ã¯ããå¿ èŠã§ãïŒãã®èšäºã®å·çæç¹ã§ã¯akka 2.3.4ããã³scalatest 2.1.6ã§ãã
''ããŒã...ããããããã¯ã©ã®ãããªãŽãã§ããïŒ ''ããŸãã¯å¯Ÿè±¡å€ã®äººã®ããã«
èŠåïŒ1ïŒçŽ æã§ScalaããŸã£ããæãããéµç©ŽãããScalaãèŠãèŠããªãã£ãå Žåããã®èšäºã§åŸè¿°ãããã¹ãŠã®ç¹å®ã®éšåãç解ããããšã¯ã§ããªãã§ãããã ããããæãé åºãªäººïŒç§ã¯ãããèªåã§æ¿èªããŸãïŒã®ããã«ããã¡ãã·ã§ããã«ã§å
æ²¢ã®ããTypesafe Activatorãã³ã䜿çšããŠãScalaã§äžå¿
èŠãªå°é£ãªãæ°ãããããžã§ã¯ããäœæããæ¹æ³ã説æããŸãã
èŠåïŒ2ïŒæ¬¡ã®ã³ãã³ãã©ã€ã³ã¢ã¯ã·ã§ã³ã¯ãOS Linuxããã³Mac OS Xã«æå¹ã§ããWindowsã«å¿ èŠãªã¢ã¯ã·ã§ã³ã¯ã説æãããŠãããã®ãšäŒŒãŠããŸãããç°ãªããŸãïŒå°ãªããšãProjectsãã£ã¬ã¯ããªåã®åã«ãã«ãããªãããšãããã¯ã¹ã©ãã·ã¥ãããã©ã«ãããšããèªããã£ã¬ã¯ããªããŸãã¯ããã£ã¬ã¯ããªããšããèšèã®ä»£ããã«ãWindowsçšã«èšèšãããç¹å¥ãªactivate.batãã¡ã€ã«ã®ã¢ãŒã«ã€ãå ã®ååšïŒã
è¡ããŸãããã ç§ãå人çã«æ°ãããããžã§ã¯ããäœæããæãç°¡åãªæ¹æ³ã¯ãåè¿°ã®ã¿ã€ãã»ãŒãã¢ã¯ãã£ããŒã¿ãŒãå ¬åŒãŠã§ããµã€ãããããŠã³ããŒãããããšã§ãã å·çæç¹ã§ãµã€ãã§å®£èšãããŠããã©ã€ãã©ãªã®ããŒãžã§ã³ã¯ãActivator 1.2.10ãAkka 2.3.4ãScala 2.11.1ã§ãã ãã¹ãŠãZIPã¢ãŒã«ã€ããšããŠããŠã³ããŒããããŸãã ããŠã³ããŒãäžã¯ããªãŒãã³ã230âã«äºç±ããå¿ èŠããããŸãã ãããŸã§ã®éãããªããªãŒãã³ãå¿ èŠãªã®ã§ããïŒ o_0â-352MBã®ã¢ãŒã«ã€ããæ¢ã«ããŠã³ããŒããããŠããŸãã ãã®ãã¹ãŠããã£ã¹ã¯ã®ã©ããã«å±éããŸãã ã/ Projectsãã£ã¬ã¯ããªã§ãã¹ãŠã®æäœãè¡ããŸãã ã ããïŒ
ã¢ãŒã«ã€ããé梱ãããããã³ã«ãªã€ã«ãå¡ãããšãå¿ããªãã§ãã ããã ãã¹ãŠãç§ã¯çŽæããŸãããããŠããã¹ãŠã¯éåžžã«æ·±å»ã«ãªããŸãã ãããžã§ã¯ããäœæããã«ã¯ãã°ã©ãã£ã«ã«ã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããæ¹æ³ãšã³ãã³ãã©ã€ã³ã䜿çšããæ¹æ³ã®2ã€ããããŸãã åŽåè ãžã§ãã€ãšããŠããã¡ãããé»åã®çµè·¯ãéžæããŸãïŒããã«ãã¿ãŒããã«ã¯ãã§ã«éããŠããŸã-äœããã®UIããããããã¿ãŒããã«ãéããªãã§ãã ããïŒã
ãã®ç°¡åãªè¡ã䜿çšããŠãã¢ã¯ãã£ããŒã¿ãŒã«ã hello-akkaãšãããã³ãã¬ãŒããããçŸåšã®ãã©ã«ããŒã«ïŒ æ°ãã ïŒ koteãããžã§ã¯ããäœæããããæ瀺ããŸãïŒèŠããŠããããã«ãã/ Projectsã«æ®ããŸãïŒã ãã®ãã³ãã¬ãŒãã«ã¯ãå¿ èŠãªã©ã€ãã©ãªçšã«æ§æãããbuild.sbtãã¡ã€ã«ãæ¢ã«å«ãŸããŠããŸãã ããŒã¯ãµã€ãã®å¯èœæ§ã¯ããã€ãã®ããã«ãããç°¡åã§é åçãªã®ã§ã誰ããã³ãã³ãã©ã€ã³ã§æåããªãå Žåã¯ãã
ïŒãŸãã¯æ¢ã«ã¢ã¯ãã£ããŒã¿ãŒã³ã³ãœãŒã«ã«ããå Žåã¯åã«ui ïŒãå ¥åããŠãéããã©ãŠã¶ãŒã§ãã¹ãŠãè¡ãããšãã§ããŸãã ããã¯ãã¹ãŠãšãŠããããã§ããå°ãªããšã楜ãã¿ã®ããã ãã«èŠãŠãã ãã-ããªããããã奜ãã«ãªãããšãçŽæããŸãã ãããžã§ã¯ããäœæããããããã®ãã£ã¬ã¯ããªã«ç§»åããŸãã
ããã«ãåãžã§ãã€ã¯ãedãviãvimãemacsãSublimeãTextMateãAtomãä»ã®äœãããŸãã¯æ¬æ ŒçãªIDEã䜿çšããŠèªåã®åŒ·ãã決å®ããŸãã å人çã«ã¯ãScalaã«åãæ¿ãããšãã«IntelliJ IDEAã䜿ãå§ããã®ã§ãããã«ãã®ç°å¢ã®ãããžã§ã¯ããã¡ã€ã«ãçæããŸãã åäœãããã«ã¯ã
è¡
ãproject / plugins.sbtãã¡ã€ã«ã«è¿œå ããå¿ èŠããããŸãã
ãã®åŸãã¢ã¯ãã£ããŒã¿ãèµ·åãããšã圌ã¯å¿ èŠãªãã¹ãŠãã³ãã³ãã§å®è¡ããŸãã
ããã§ãIDEAã§ãããžã§ã¯ããéãããšãã§ããŸãã
IDEããã©ãŒã¹ã®ããŒã¯ãµã€ãïŒãŸãã¯ãã®éïŒã§ããããžã§ãã€ã䟡å€ããªããšæããªãããããããªãã®å®å šãªæš©å©ã§ãã ãã®å Žåãã¢ã¯ãã£ããŒã¿ãŒã®ã³ãã³ãã©ã€ã³ã«çãŸããä»»æã®äŸ¿å©ãªæ¹æ³ã§ãã¡ã€ã«ãç·šéã§ããŸãã ãããŠãç«ã®å°æ¥ã®éåœã決å®ããã®ã¯ã2ã€ã®ã¢ã¯ãã£ããŒã¿ãŒããŒã ã ãã§ãã
ãã®èšäºã®äžéšãšããŠæ¬çªç°å¢ã§koteãèµ·åããŸããããTamagotchiã®æçµããŒãžã§ã³ã®æœåšçãªéçºè ã¯runã³ãã³ãã䜿çšããŠãããè¡ãããšãã§ããŸãã
ãåç¥ã®ããã«ããã¹ãŠã®ç«ã¯ãã¡ããšããç©è¶³ããªãã§ãã ãããã£ãŠãç§ãã¡ã¯å°æ¥ã®ãããã®ããã«æž æœã§æŽé ããã家ãæºåããããšããå§ããŸãã ã€ãŸããhello-akkaãã³ãã¬ãŒãã®äžéšãšããŠæ°ããäœæããããããžã§ã¯ãã«ä»å±ãããã¹ãŠã®äœåãªãã¡ã€ã«ãåé€ããŸãã å人çã«ã¯ãsrc / main / javaãsrc / test / javaãã£ã¬ã¯ããªãšãã¹ãŠã®ã³ã³ãã³ããããã³äžèŠãªãã¹ãŠã®.scalaãã¡ã€ã«ãèæ ®ããå¿ èŠããããŸãïŒsrc / main / scala / HelloAkkaScala.scalaãšsrc / test / scala /HelloAkkaSpec.scalaã ããŠãããã§å ãžé²ãæºåãã§ããŸããã
èŠåïŒ2ïŒæ¬¡ã®ã³ãã³ãã©ã€ã³ã¢ã¯ã·ã§ã³ã¯ãOS Linuxããã³Mac OS Xã«æå¹ã§ããWindowsã«å¿ èŠãªã¢ã¯ã·ã§ã³ã¯ã説æãããŠãããã®ãšäŒŒãŠããŸãããç°ãªããŸãïŒå°ãªããšãProjectsãã£ã¬ã¯ããªåã®åã«ãã«ãããªãããšãããã¯ã¹ã©ãã·ã¥ãããã©ã«ãããšããèªããã£ã¬ã¯ããªããŸãã¯ããã£ã¬ã¯ããªããšããèšèã®ä»£ããã«ãWindowsçšã«èšèšãããç¹å¥ãªactivate.batãã¡ã€ã«ã®ã¢ãŒã«ã€ãå ã®ååšïŒã
ãããžã§ã¯ããäœæãã
è¡ããŸãããã ç§ãå人çã«æ°ãããããžã§ã¯ããäœæããæãç°¡åãªæ¹æ³ã¯ãåè¿°ã®ã¿ã€ãã»ãŒãã¢ã¯ãã£ããŒã¿ãŒãå ¬åŒãŠã§ããµã€ãããããŠã³ããŒãããããšã§ãã å·çæç¹ã§ãµã€ãã§å®£èšãããŠããã©ã€ãã©ãªã®ããŒãžã§ã³ã¯ãActivator 1.2.10ãAkka 2.3.4ãScala 2.11.1ã§ãã ãã¹ãŠãZIPã¢ãŒã«ã€ããšããŠããŠã³ããŒããããŸãã ããŠã³ããŒãäžã¯ããªãŒãã³ã230âã«äºç±ããå¿ èŠããããŸãã ãããŸã§ã®éãããªããªãŒãã³ãå¿ èŠãªã®ã§ããïŒ o_0â-352MBã®ã¢ãŒã«ã€ããæ¢ã«ããŠã³ããŒããããŠããŸãã ãã®ãã¹ãŠããã£ã¹ã¯ã®ã©ããã«å±éããŸãã ã/ Projectsãã£ã¬ã¯ããªã§ãã¹ãŠã®æäœãè¡ããŸãã ã ããïŒ
$ mkdir ~/Projects $ cd ~/Projects $ unzip ~/Downloads/typesafe-activator-1.2.10.zip
ã¢ãŒã«ã€ããé梱ãããããã³ã«ãªã€ã«ãå¡ãããšãå¿ããªãã§ãã ããã ãã¹ãŠãç§ã¯çŽæããŸãããããŠããã¹ãŠã¯éåžžã«æ·±å»ã«ãªããŸãã ãããžã§ã¯ããäœæããã«ã¯ãã°ã©ãã£ã«ã«ã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããæ¹æ³ãšã³ãã³ãã©ã€ã³ã䜿çšããæ¹æ³ã®2ã€ããããŸãã åŽåè ãžã§ãã€ãšããŠããã¡ãããé»åã®çµè·¯ãéžæããŸãïŒããã«ãã¿ãŒããã«ã¯ãã§ã«éããŠããŸã-äœããã®UIããããããã¿ãŒããã«ãéããªãã§ãã ããïŒã
$ activator-1.2.10/activator new kote hello-akka
ãã®ç°¡åãªè¡ã䜿çšããŠãã¢ã¯ãã£ããŒã¿ãŒã«ã hello-akkaãšãããã³ãã¬ãŒããããçŸåšã®ãã©ã«ããŒã«ïŒ æ°ãã ïŒ koteãããžã§ã¯ããäœæããããæ瀺ããŸãïŒèŠããŠããããã«ãã/ Projectsã«æ®ããŸãïŒã ãã®ãã³ãã¬ãŒãã«ã¯ãå¿ èŠãªã©ã€ãã©ãªçšã«æ§æãããbuild.sbtãã¡ã€ã«ãæ¢ã«å«ãŸããŠããŸãã ããŒã¯ãµã€ãã®å¯èœæ§ã¯ããã€ãã®ããã«ãããç°¡åã§é åçãªã®ã§ã誰ããã³ãã³ãã©ã€ã³ã§æåããªãå Žåã¯ãã
./activator ui
ïŒãŸãã¯æ¢ã«ã¢ã¯ãã£ããŒã¿ãŒã³ã³ãœãŒã«ã«ããå Žåã¯åã«ui ïŒãå ¥åããŠãéããã©ãŠã¶ãŒã§ãã¹ãŠãè¡ãããšãã§ããŸãã ããã¯ãã¹ãŠãšãŠããããã§ããå°ãªããšã楜ãã¿ã®ããã ãã«èŠãŠãã ãã-ããªããããã奜ãã«ãªãããšãçŽæããŸãã ãããžã§ã¯ããäœæããããããã®ãã£ã¬ã¯ããªã«ç§»åããŸãã
$ cd kote
IDEãŸãã¯éIDE
ããã«ãåãžã§ãã€ã¯ãedãviãvimãemacsãSublimeãTextMateãAtomãä»ã®äœãããŸãã¯æ¬æ ŒçãªIDEã䜿çšããŠèªåã®åŒ·ãã決å®ããŸãã å人çã«ã¯ãScalaã«åãæ¿ãããšãã«IntelliJ IDEAã䜿ãå§ããã®ã§ãããã«ãã®ç°å¢ã®ãããžã§ã¯ããã¡ã€ã«ãçæããŸãã åäœãããã«ã¯ã
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.2")
è¡
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.2")
ãproject / plugins.sbtãã¡ã€ã«ã«è¿œå ããå¿ èŠããããŸãã
$ echo "addSbtPlugin(\"com.github.mpeltonen\" % \"sbt-idea\" % \"1.5.2\")" > project/plugins.sbt
ãã®åŸãã¢ã¯ãã£ããŒã¿ãèµ·åãããšã圌ã¯å¿ èŠãªãã¹ãŠãã³ãã³ãã§å®è¡ããŸãã
$ $ ./activator > gen-idea sbt-classifiers
ããã§ãIDEAã§ãããžã§ã¯ããéãããšãã§ããŸãã
ãããšãIDEã§ã¯ãããŸãããïŒ
IDEããã©ãŒã¹ã®ããŒã¯ãµã€ãïŒãŸãã¯ãã®éïŒã§ããããžã§ãã€ã䟡å€ããªããšæããªãããããããªãã®å®å šãªæš©å©ã§ãã ãã®å Žåãã¢ã¯ãã£ããŒã¿ãŒã®ã³ãã³ãã©ã€ã³ã«çãŸããä»»æã®äŸ¿å©ãªæ¹æ³ã§ãã¡ã€ã«ãç·šéã§ããŸãã ãããŠãç«ã®å°æ¥ã®éåœã決å®ããã®ã¯ã2ã€ã®ã¢ã¯ãã£ããŒã¿ãŒããŒã ã ãã§ãã
- ã³ã³ãã€ã« -ãããžã§ã¯ãã®ã³ã³ãã€ã« ã
- test-ãã¹ãŠã®ãã¹ããå®è¡ããŸãã å¿ èŠã«å¿ããŠã³ã³ãã€ã«ãåŒã³åºããŸãã®ã§ãç§ã¯åãã€ããã®ã§ããã®ã³ãã³ãã ãã§å¯Ÿå¿ã§ããŸãã
ãã®èšäºã®äžéšãšããŠæ¬çªç°å¢ã§koteãèµ·åããŸããããTamagotchiã®æçµããŒãžã§ã³ã®æœåšçãªéçºè ã¯runã³ãã³ãã䜿çšããŠãããè¡ãããšãã§ããŸãã
ããŠã®ããã«å Žæãæé€ããŸã
ãåç¥ã®ããã«ããã¹ãŠã®ç«ã¯ãã¡ããšããç©è¶³ããªãã§ãã ãããã£ãŠãç§ãã¡ã¯å°æ¥ã®ãããã®ããã«æž æœã§æŽé ããã家ãæºåããããšããå§ããŸãã ã€ãŸããhello-akkaãã³ãã¬ãŒãã®äžéšãšããŠæ°ããäœæããããããžã§ã¯ãã«ä»å±ãããã¹ãŠã®äœåãªãã¡ã€ã«ãåé€ããŸãã å人çã«ã¯ãsrc / main / javaãsrc / test / javaãã£ã¬ã¯ããªãšãã¹ãŠã®ã³ã³ãã³ããããã³äžèŠãªãã¹ãŠã®.scalaãã¡ã€ã«ãèæ ®ããå¿ èŠããããŸãïŒsrc / main / scala / HelloAkkaScala.scalaãšsrc / test / scala /HelloAkkaSpec.scalaã ããŠãããã§å ãžé²ãæºåãã§ããŸããã
æåã®ã¹ããã
æåã¯ãã¹ãããããŸããã ãããŠããã¹ãã¯ã³ã³ãã€ã«ãããŸããã§ããã ãåç¥ã®ããã«ããã®å£°æãTDDã®åºæ¬çãªä»®å®ã§ãããçŸåšç§ãã³ãããããŠããŸãã ãããã£ãŠã説æã¯ãã·ã³èªäœã§ã¯ãªããAkka TestKitã©ã€ãã©ãªãæäŸãããã¹ãæ©èœãå®èšŒããããã®æåã®ãã¹ãã®äœæããå§ããŸãã ç§ã䜿çšããã¢ã¯ãã£ããŒã¿ãŒã«å ããŠããã§ã«ãã¹ããã¬ãŒã ã¯ãŒã¯ã§ããscalatestããããŸãã 圌ã¯ç§ã«ãšãŠã䌌åã£ãŠãããç§ãã¡ã®ãããžã§ã¯ãã§ããã䜿ããªãçç±ã¯ãªããšæããŸãã äžè¬ã«ãAkka TestKitã¯ãã¬ãŒã ã¯ãŒã¯ã«äŸåããªããããspec2ãªã©ã§äœ¿çšã§ããŸãã ãã¹ãããã±ãŒãžã®ååãæ°ã«ããªãããã«ããã¡ã€ã«ãçŽæ¥src / test / scala / KoteSpec.scalaã«é 眮ããŸã
import akka.actor.ActorSystem import akka.testkit.{ImplicitSender, TestFSMRef, TestKit} import org.scalatest.{BeforeAndAfterAll, FreeSpecLike, Matchers} class KoteSpec(_system: ActorSystem) extends TestKit(_system) with ImplicitSender with Matchers with FreeSpecLike with BeforeAndAfterAll { def this() = this(ActorSystem("KoteSpec")) import kote.Kote._ override def afterAll(): Unit = { system.shutdown() system.awaitTermination(10.seconds) } "A Kote actor" - { // All future tests go here } }
ããã«ãã³ã¡ã³ãã®ããäžã«ãããã¹ãŠã®ãã¹ãããã®ã¯ã©ã¹ã®æ¬äœã«è¿œå ããããšãæ³å®ããŠããŸãã ããšãã°ãFlatSpecLikeã§ã¯ãªããFreeSpecLikeã䜿çšããŸããããã¯ãããŸããŸãªç¶æ ãšãªãŒãããã³ã®é·ç§»ã«å¯Ÿããå€ãã®ãã¹ããæ確ã«æ§æããæ¹ãå人çã«ã¯ããã«äŸ¿å©ã ããã§ãã æåã®ãã¹ãã®äœæãéå§ããæºåãã§ããã®ã§ãç«ã¯äœãããããããããš-ç¡ç ãšããäºå®ããå§ããããšãææ¡ããŸãã ãããã£ãŠãTDDã®ååãèæ ®ããŠãæ°ããçãŸããç«ãæåããå¯ãŠããããšã確èªãããã¹ããäœæããŸãã
"should sleep at birth" in { val kote = TestFSMRef(new Kote) kote.stateName should be(State.Sleeping) kote.stateData should be(Data.Empty) }
é çªã«ç解ããŠã¿ãŸãããã TestFSMRefã¯ãFSMã¯ã©ã¹ã䜿çšããŠå®è£ ãããã¹ããŒããã·ã³ã®ãã¹ããç°¡çŽ åããããã«Akka TestKitãã¬ãŒã ã¯ãŒã¯ãæäŸããã¯ã©ã¹ã§ãã ããæ£ç¢ºã«èšããšãTestFSMRefã¯ãapplyã¡ãœãããåŒã³åºãã³ã³ãããªã³ãªããžã§ã¯ããæã€ã¯ã©ã¹ã§ãã ãããŠãã®ã¡ââãœããã¯ãæãäžè¬çãªActorRefã®åå«ã§ããTestFSMRefã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãè¿ããŸããã€ãŸããåçŽãªã¢ã¯ã¿ãŒãšããŠã¡ãã»ãŒãžããã·ã³ã«éä¿¡ã§ããŸãã ãã ããTestFSMRefã®æ©èœã¯ãåçŽãªActorRefãšæ¯èŒããŠå€å°æ¡åŒµãããŠããããããã®æ¡åŒµæ©èœã¯ãã¹ãå°çšã«èšèšãããŠããŸãã ãããã®æ¡åŒµæ©èœã®1ã€ã¯ããã¹ãããåç«ã®çŸåšã®ç¶æ ãžã®ã¢ã¯ã»ã¹ãæäŸããstateNameãšstateDataã®2ã€ã®é¢æ°ã§ãã ãªã2ã€ã®æ©èœã1ã€ã®ç¶æ ãããã®ã§ããïŒ å®éãéåžžã®ç解ã§ã¯ãç¶æ ã¯ãªãŒãããã³ã®å éšãã©ã¡ãŒã¿ãŒã®çŸåšã®å€ã®ã»ããã§ãã 2ã€ã®å€æ°ã¯ã©ãããæ¥ãã®ã§ããïŒ äºå®ã¯ããªãŒãããã³ã®çŸåšã®ç¶æ ã説æããããã«ãAkka.FSMïŒErlangã®ãªãŒãããã³ã®èšèšã®ååã«åºã¥ãïŒã¯ãç¶æ ã®ãååããšããã«é¢é£ä»ãããããããŒã¿ãã®æŠå¿µãåããŠãããšããããšã§ãã ããã«ãAkkaã¯ãªãŒãããã³ã¯ã©ã¹ã§å¯å€ããããã£ïŒvarïŒ ã®äœ¿çšãé¿ããããšããå§ãããŸããããã«ãããããã°ã©ã ã³ãŒãå ã®ãªãŒãããã³ã®ç¶æ ã¯ãäºåå®çŸ©ãããå°æ°ã®ããç¥ãããå Žæã§ã®ã¿å€æŽã§ããæçœã§æé»çãªå€æŽãé¿ããããšãã§ãããšããå©ç¹ããããŸãã ããã«ãå°æ¥ã®ã¯ã©ã¹å ãããããã®2ã€ã®å€æ°ã«çŽæ¥ã¢ã¯ã»ã¹ããããšã¯ã§ããŸããããããã¯ãFSMåºæ¬ã¯ã©ã¹ã§privateãšããŠå®£èšãããŠããŸãã ãã ããTestFSMRefã¯ãã¹ãã®ããã«ããããžã®ã¢ã¯ã»ã¹ãæäŸããŸãã ãããŠããã·ã³èªäœã®ã¯ã©ã¹ãããããã«å°éããæ¹æ³ã¯ããã«æããã«ãªããŸãã
ããã§ãç§ãã¡ã®ç¡ç ç¶æ ãç§ã¯ç¡ç ãšåŒã³ãŸããã ãããŠããããè£å©ãªããžã§ã¯ãStateã«æŒã蟌ã¿ãŸããããã¯ãã³ãŒããæ確ã«ããæ··ä¹±ãé¿ããããã«ãããããã¹ããŒãã®ååããã¹ãŠä¿åããŸãã ããŒã¿ã«é¢ããŠã¯ããã®æ®µéã§ã¯ãŸã äœã«ãªããããããŸããã ãã ãããã·ã³ãããŒã¿ãšããŠããã£ãŒããããå¿ èŠããããŸããããããªããšæ©èœããŸããã ãããã£ãŠãEmptyãšããååã§å€æ°ã«ååãä»ããããšã«ããŸãããããã¯ç§ã®å人çãªéžæã§ãããäœã匷å¶ãããã®ã§ã¯ãããŸããã å¥ã®æ¹æ³ã§åŒã³åºãããšãã§ããŸãïŒNothingãUndefinedã ç§ã«é¢ããŠã¯ãEmptyã¯çããååãªæ å ±ãæäŸããŸãã ãŸããããŒã¿ãšåŒã°ããç¹å¥ã«å²ãåœãŠããããªããžã§ã¯ãã«ããŒã¿ãä¿åããããšã«ãæ £ããŠããŸãã ããŸããŸãªçš®é¡ã®ããŒã¿ã®ãæŠéãã¢ãµã«ãã©ã€ãã«ã«ã¯ãå·åãããå°ãªãããŸãã¯ãã以äžã®ååãããå Žåããããããåžžã«å°çšã®å Žæã«ä¿ç®¡ããŸããã«ãã¬ããå¥ã ã«ãå¥ã ã«é£ã¶ã
ããŠãã³ã³ãã€ã«ããŠããŸããïŒ ãã¹ãã§åç §ããåãšå€æ°ããªããããã³ã³ãã€ã«ã倱æããããšã¯æããã§ãã ããã¯ãTDDãµã€ã¯ã«ã®æ¬¡ã®æ®µéã«é²ãæºåãã§ããŠããããšãæå³ããŸãã
ãªãŒãããã³ã®ã¯ã©ã¹ã宣èšããã«ã¯ãç¶æ ã®ååãšãã®ããŒã¿ãèšè¿°ãããã¹ãŠã®ã¯ã©ã¹ãšãªããžã§ã¯ããç¶æ¿ããã2ã€ã®åºæ¬ã¿ã€ããå¿ èŠã§ãã ç°å¢ãæ£ããããªãããã«ãåç«ã®ç掻ã«å¿ èŠãªãã¹ãŠã®å®çŸ©ãä¿åããã³ã³ãããªã³ãªããžã§ã¯ããäœæããŸãã ããã¯Scalaã®äžçã§ã¯äžè¬çãªåºæºã§ããã誰ãç§ãã¡ã責ããããšã¯ãããŸããã ããã±ãŒãžã®ååã䜿çšãããã¹ãã§ãããããããªãå Žåã¯ããããžã§ã¯ãèªäœã«ã€ããŠãäœæããŸãã 圌ãkoteãšåŒã³ãŸãããã ãããŠããããã®å®è£ ãã¡ã€ã«ãããããsrc / main / scala / kote / Kote.scalaã«é 眮ããŸãã ããã§ã¯ãå§ããŸãããïŒ
package kote import akka.actor.FSM import scala.concurrent.duration._ /** Kote companion object */ object Kote { sealed trait State sealed trait Data }
ãããã®å®çŸ©ã¯ãåç«ã¯ã©ã¹ã宣èšããã®ã«ååã§ãïŒ
/** Kote Tamakotchi mimimi njawka! */ class Kote extends FSM[Kote.State, Kote.Data] { import Kote._ }
ã¯ã©ã¹å ã«ãããã«ã¢ã¯ã»ã¹ããããããããã«ãè£å©ãªããžã§ã¯ãã§ããã«å®£èšããããã¹ãŠã®ãã®ã®ã€ã³ããŒããè¿œå ããŸããã æåã®ãç ããç¶æ ã®ååãšããŒã¿ã®å€ã®ã¿ã宣èšã§ããŸãã
/** Kote companion object */ object Kote { sealed trait State sealed trait Data object State { case object Sleeping extends State } object Data { case object Empty extends Data } }
ãã¹ããã³ã³ãã€ã«ããåã«ãæåŸã®ã¹ããããæ®ã£ãŠããŸããã ã¯ã©ã¹èªäœãããšåãããã«ç°¡åãã€åçŽã«Koteãªããžã§ã¯ãã®å éšãåç §ããïŒãããŠä»åŸåç §ãããïŒãããKoteSpecã¯ã©ã¹ã®æ¬äœã«ã€ã³ããŒããè¿œå ãããšäŸ¿å©ã§ãã 代æ¿ã³ã³ã¹ãã©ã¯ã¿ã宣èšããçŽåŸã«ã§ããŸãïŒ
... def this() = this(ActorSystem("KoteSpec")) import Kote._ ...
ããŠãKoteSpec.scalaãã¡ã€ã«ã®importã»ã¯ã·ã§ã³ã«import kote.Koteãè¿œå ããããšãå¿ããªãã§ãã ããã ããã§ãããžã§ã¯ããæ£åžžã«ã³ã³ãã€ã«ããããã¹ããå®è¡ã§ããŸãã ãªã«ïŒ èµ€ NullPointerExceptionïŒ ãããŠãããªãã¯èããŸãã-æ°ããåç«ãäœãã®ã¯ãšãŠãç°¡åã§ããïŒ èªç¶ã¯äœçŸäžå¹Žãã®é²åãå°ç¡ãã«ããŠããŸããïŒ ãŸãããããã¯ã¯ãããŸããã ããããåé¡ã¯ãåºççŽåŸã«äœããã¹ãããåç©ã«äŒããªãã£ãããšã§ãã ãšãŠãç°¡åã§ãïŒ
class Kote extends FSM[Kote.State, Kote.Data] { import Kote._ startWith(State.Sleeping, Data.Empty) }
ãã¹ããéå§ããŸã-åºæ¥äžããïŒ ç§ã®å€§å¥œããªç·ïŒ åç«ã¯çãè¿ã£ãããã«èŠããŸããããã©ããããããéå±ãªãã®ã§ããããèªäœã¯æãã«ç ããŸã-ããããã¹ãŠã§ãã æ²ããã§ãã 圌ãèµ·ãããŸãããã
ãç ããç§ã®åã³ïŒãããŸãã¯åæç¶æ ã§ã®è¡åã®å®çŸæ¹æ³
ãããã©ã®ããã«è¡ãã®ã§ããããïŒ ãã¹ãã®å®è¡äžã«ã¢ãã¿ãŒã®é床ãèœãšããªãã§ãã ããïŒ å»ºèšçã«èããŠã¿ãŸããããåç«ã俳åªã§ããå Žåã圌ãšéä¿¡ããå¯äžã®æ¹æ³ã¯ã¡ãã»ãŒãžãéä¿¡ããããšã§ãã ãã®ãããªéèŠãªã³ãå®åãããªãã¯åœŒã«ç§æžãéãã ãã§ããã®ã§ã圌ã¯éä¿¡ãæŽçããŸãã 圌ãç®ãèŠãŸãããã«ã圌ã¯ã©ããªã¡ãã»ãŒãžãéãã¹ãã§ããïŒ ç§ãã¡ã¯åœŒãç°¡åã«æžãããšãã§ããŸããïŒkoteïŒ ããããã¹ãïŒ èµ·ããïŒã ããããç§ã¯å人çã«è¡ã®äžã§ã¡ãã»ãŒãžãéä¿¡ããã®ã¯æªããããŒã ãšèããŠããŸãããªããªããããªãã¯ãã€ã§ãããæåã§ééããç¯ãããšãã§ããã³ã³ãã€ã©ã¯ããã«æ°ä»ããããããã°ããã®ãéåžžã«é£ããããã§ãã ãããŠãããªãã空æ³ããå Žåãç§ãã¡ã®æ°çå ã®ã³ãã¯ãŸã 人éã®èšèªãç解ããŠããªãã¯ãã§ãã ããŒã ã®ç¹å¥ãªç«ã®èšèªãéçºããããšãææ¡ããŸãã圌ã¯ãããèªçããåŠã³å§ããŠããããã§ãã ãŸããæ¬èœçã«ããŸãã¯äœãã ãããŠãç§ãã¡ã¯åœŒã®æ¬èœã®çºå±ã«è²¢ç®ããŸãã 圌ãèšç·Žããæåã®ããŒã ã¯WakeUpãšåŒã°ããŸãã ãããŠãè£å©ãªããžã§ã¯ãã®Commandsãµããªããžã§ã¯ãã«é 眮ããŸãã
object Kote { ... object Commands { case object WakeUp } }
ããã§ã¯ãã¹ããå§ããŸãããïŒ
"should wake up on command" in { val kote = TestFSMRef(new Kote) kote ! Commands.WakeUp kote.stateName should be (State.Awake) }
ãã¡ããããã¹ãã¯ã³ã³ãã€ã«ãããŸããã æ¡ä»¶ã®ååã宣èšããã®ãå¿ããŸããïŒ
case object Awake extends State
ãã¹ãã¯ã³ã³ãã€ã«ãããŸããããã©ãããããã¯ç§ãã¡ã®ããã«éåœã¥ãããããã®ã§ãå¥ã®äŸå€ïŒ NoSuchElementExceptionïŒkey not foundïŒSleepingã§ã¯ã©ãã·ã¥ããŸãã ããããã¹ãŠã®ébarãªæç« ã¯ã©ãããæå³ã§ããïŒ éåå®éšã®è¥ãæ人ã«ã圌ã¯ç ãã¹ãã ãšèšããŸããããããŠåœŒã¯æ¬åœã«çŽ çŽã«ç ããŸããã圌ã¯ãŸã äœãç ã ã ã©ã®ããã«ãããããããç¥ããŸããã ããã«ããã®äžç¢ºå®æ§ã®ç¶æ ã§åœŒã«ã¡ãã»ãŒãžãéãããšããŠããŸãã ããããç«ã®æåãªæ·åè ãäžæ¯è ã®ããã«ãªãã貧ããåç©ã絶æçãªç¡ç¥ã«ä¿ã¡ããã®è¡åãç°¡åã«èª¬æããŸãããïŒ
when(State.Sleeping, Data.Empty) { FSM.NullFunction }
ã¯ããã«ãæªããªãã whenã¯ã2çµã®è§ãã£ããæã€æãäžè¬çãªscalaé¢æ°ã§ãã ã€ãŸããïŒïŒïŒïŒã®å Žåã§ãã ãŸããè¡åãèšè¿°ãããç¶æ ã®ååã瀺ãã次ã«ïŒãã®å Žåscalaããããã瀺ãããšãã§ããªãããã2çªç®ã®æ¬åŒ§ã¯è¡šç€ºãããŸããïŒãåç©ã®è¡åãç¹åŸŽä»ããéšåé¢æ°ãã®ç¶æ ã ãã¡ã³ããŒãªæ¯ãèãã§ãã ãããŠãè¡åã¯ããŸããŸãªå€éšåºæ¿ã«å¯Ÿããåå¿ã§ãã åã«-çä¿¡ã¡ãã»ãŒãžã§ã éåžžã®åå¿ã«ã¯3ã€ã®ã¿ã€ãããããŸã-ãã·ã³ãçŸåšã®ç¶æ ã®ãŸãŸïŒæ»åšïŒãæ°ããç¶æ ã«ç§»è¡ïŒgotoïŒããŸãã¯äœæ¥ãåæ¢ïŒåæ¢ïŒããŸãã 4çªç®ã®ãªãã·ã§ã³-ãç°åžžãªãåå¿-ã¯ããã·ã³ãåé¡ã«å¯ŸåŠã§ãããäŸå€ãã¹ããŒããå Žåã§ãïŒãããŠãéåžžã®ã¢ã¯ã¿ãŒã®å Žåã®ããã«ãã¹ãŒããŒãã€ã¶ãŒãçŸåšã®ç£ç£ã®æŠç¥ã«åŸã£ãŠãããã©ããããã決å®ããŸãïŒã äŸå€ã®ãããã¯ã«ã€ããŠã¯åŸã»ã©è§ŠããŸãã
FSM.NullFunctionã¯ããã®ç¶æ ã®ç«ã¯ãŸã£ããäœããããäœã«ãåå¿ããããã¹ãŠã®çä¿¡ã¡ãã»ãŒãžãè³ã«éãããšãäŒãããAkkaã©ã€ãã©ãªã«ãã£ãŠæçšã«æäŸãããé¢æ°ã§ãã {case _ =>}ãšæžãããšãã§ããŸãããããã¯ãŸã£ããåãã§ã¯ãããŸãããããã«ã€ããŠã¯åŸã§èª¬æããŸãã NullFunctionãå°æ¥ã®ç¶æ ãèšè¿°ããããã®ãã®ã£ã°ããšããŠäœ¿çšãããšäŸ¿å©ã§ãã詳现ã¯ãã®æ®µéã§ã¯éèŠã§ã¯ãããŸããããããããžã®ç§»è¡ããã¹ãããå¿ èŠããããŸãã
ããŠã§ã€ã¯ã¢ãããã¬ã€ãžãŒããŒã¹ãïŒãããŸãã¯æ°ããç¶æ ãžã®é·ç§»ã«ãã£ãŠã€ãã³ãã«å¿çããæ¹æ³
ããã§ã¯ãä»ãããã¹ããå®è¡ããŸããã-ãããŠä»ã転åã®çç±ã¯å®å šã«ç°ãªã£ãŠããŸãïŒç¡ç ã¯ç®èŠããšçãããããŸããã§ããã ãã¡ãããçµå±ã®ãšãããç§ãã¡ã®ç«ã¯ç ãããšãåŠã³ãŸããããç§ãã¡ã¯ãŸã 圌ã«ãŠã§ã€ã¯ã¢ããããŒã ãžã®å¯Ÿå¿æ¹æ³ãæããŠããŸããã å°ãããæ··ããŠã¿ãŸãããïŒ
when(State.Sleeping) { case Event(Commands.WakeUp, Data.Empty) => goto(State.Awake) }
åè¿°ããããã«ãç¶æ ãšããŒã¿ã®ååãæã€å€æ°ã«çŽæ¥ã¢ã¯ã»ã¹ããããšã¯ã§ããŸããã ã¡ãã»ãŒãžããã·ã³ã«å°çãããšãã«ã®ã¿ããããã«ã¢ã¯ã»ã¹ã§ããŸãã FSMã¯ãã®ã¡ãã»ãŒãžãã±ãŒã¹ã¯ã©ã¹ã€ãã³ãã«ã©ããããããã«çŸåšã®ã¹ããŒã¿ã¹ããŒã¿ãè¿œå ããŸãã ããã§ããã¿ãŒã³ãããã³ã°ãé©çšããå¿ èŠãªãã®ããã¹ãŠãå°çãã€ãã³ãããåé¢ã§ããŸãã ãã®å ŽåãSleepingãšããååã®ç¶æ ã§WakeUpã³ãã³ããåãåããããŒã¿ãData.Emptyã§ããããšã確èªããŸãã ãããŠãç§ãã¡ã¯ãã®ããã°ã¬ããå šäœã«åå¿ããŠãæ°ããç¶æ ãã€ãŸãç®èŠããžãšç§»è¡ããŸãã åäœãèšè¿°ãããã®ã¢ãããŒãã«ãããç¶æ åãšçŸåšã®ããŒã¿ãçµã¿åãããããã®ããŸããŸãªãªãã·ã§ã³ãåŠçã§ããŸãã ã€ãŸããåãç¶æ ã§èŠã€ããããã«ãçŸåšã®ããŒã¿ã«å¿ããŠåãã¡ãã»ãŒãžã«ç°ãªãåå¿ãããããšãã§ããŸãã
ããã§ãç¶æ éã®é·ç§»é¢æ°ã§ããgotoãšstayã®æ©èœã«æ³šç®ããããšæããŸãã åç¬ã§ã¯ããããã¯å¯äœçšã®ãªããçŽç²ãªãé¢æ°ã§ãã ããã¯ã圌ãã®åŒã³åºãã®äºå®ãçŸåšã®ç¶æ ã®å€åã«ã€ãªãããªãããšãæå³ããŸãã ãããã¯ãå¿ èŠãªç¶æ å€ïŒgotoã®å Žåã¯ãŠãŒã¶ãŒãæå®ããstayã®å Žåã¯currentã§æå®ãããïŒã®ã¿ãè¿ããFSMãç解ã§ããåã«å€æãããŸãã å€æŽãçºçããã«ã¯ãåäœé¢æ°ããå€æŽãè¿ãå¿ èŠããããŸãã
ç§ãã¡ã¯ãããç解ããŸããã ããã§ãã¹ããå®è¡ããŸã-ãã ããåã³å€±æããŸãã次ã®ç¶æ ã®Awakeã¯ååšããŸããã 次ã®ç¶æ ã宣èšãããŠããªãå Žåãé·ç§»ãçºçããããã·ã³ãåãç¶æ ã®ãŸãŸã§ããå Žåã次ã®ç¶æ ã宣èšãããå Žåã«äœãèµ·ããããæå³çã«ç€ºããããšæããŸããã éå§ç¶æ ã§çºçããäŸå€ãã¹ããŒãããŸããã å€ãã®å Žåãéçºã®äžç°ãšããŠãç§ã¯ãããå¿ããŠã移è¡ãçºçããããã¹ããã¯ã©ãã·ã¥ããçç±ãç解ããããã«æéããããŸããã ãã°å ã®éèªæãªãã¹ãã®ã次ã®ç¶æ ã®ç®èŠãã¯ååšããŸããããšããã¡ãã»ãŒãžã¯ããšãããæ°ä»ãã®ãç°¡åã§ã¯ãããŸããã ããããæéãçµã€ã«ã€ããŠããã®æ©èœã«æ £ãå§ããŸãã
ãããã£ãŠã次ã®ç¶æ ãšããŠnullé¢æ°ã宣èšãããšããã¹ããç·è²ã«å€ãããŸãã
when(State.Awake)(FSM.NullFunction)
ãç«ããªã§ãŠïŒãããŸãã¯æºãããªããŸãŸã€ãã³ãã«å¯Ÿå¿ããæ¹æ³
ããŠãä»ãããªãã¯åœŒãç®èŠãããšããäºå®ãå©çšããŠãåç«ãstrokeã§ãããšãã§ããŸãã ç§ã¯ãããšã©ãã«è¿œå ããããšãé¡ã£ãŠããŸã-ããªãã¯ãŸã ãããç解ããŸãããïŒ
ããŒã ïŒ
case object Stroke
ãã¹ãïŒ
"should purr on stroke" in { val kote = TestFSMRef(new Kote) kote ! Commands.WakeUp kote ! Commands.Stroke expectMsg("purrr") kote.stateName should be (State.Awake) }
ã³ãïŒ
when(State.Awake) { case Event(Commands.Stroke, Data.Empty) => sender() ! "purrr" stay() }
åãããšãããç°¡æœã«æžãããšãã§ããŸãïŒ
when(State.Awake) { case Event(Commands.Stroke, Data.Empty) => stay() replying "purrr" }
ãç«ãäºåºŠèµ·ãããªãã§ãã ããïŒãããŸãã¯ç¹°ãè¿ããã«ç¹°ãè¿ããã«ãã¹ãããæ¹æ³
ãããïŒ ããŠããã¹ãã§ç«ãpetã§ãããã«ãç§ãã¡ã¯æåã«åœŒãèµ·ãããŠããã圌ãpetã§ãŸããïŒ åªãããã€ãŸãããã¹ããããç¶æ ã«ãŸã 10-15ã®äžéã®ãã®ãããå ŽåïŒãããŠ100-150ã®å ŽåïŒãæ£ãããã®ã«å ¥ãããã«ãåäžã®ãšã©ãŒãç¯ããã«ãã¹ãŠãæ£ããå®è¡ããå¿ èŠããããŸããïŒ ãŸã ééãã§ãããç§ãã¡ãèããŠããå Žæã«ããªãå Žåã¯ã©ããªããŸããïŒ ãŸãã¯ãæéã®çµéãšãšãã«äžéç¶æ éã®é·ç§»ã«äœãå€åããããŸãããïŒ ãã®å ŽåãTestFSMRefã䜿çšãããšããã¹ãŠã®äžéæé ãå®è¡ããããšãªããsetStateé¢æ°ã䜿çšããŠå¿ èŠãªç¶æ ãšããŒã¿ãä¿èšŒã§ããŸãã ããã§ã¯ããã¹ããå€æŽããŸãããã
"should purr on stroke" in { val kote = TestFSMRef(new Kote) kote.setState(State.Awake, Data.Empty) kote ! Commands.Stroke expectMsg("purrr") kote.stateName should be (State.Awake) }
ããŠãããã€ãã®ç°ãªãåºæ¿ç©ã«å¯ŸããŠåãç¶æ ããã¹ãããããã«ãç§ã¯å人çã«éè€ã³ãŒããåãé€ããã®æ¹æ³ãçºæããŸããïŒ
class TestedKote { val kote = TestFSMRef(new Kote) }
ãããŠä»ãç§ã¯ãã¹ãŠã®ãã¹ããå®å šã«çœ®ãæããããšãã§ããŸãïŒ
"should sleep at birth" in new TestedKote { kote.stateName should be (State.Sleeping) kote.stateData should be (Data.Empty) } "should wake up on command" in new TestedKote { kote ! Commands.WakeUp kote.stateName should be (State.Awake) } "should purr on stroke" in new TestedKote { kote.setState(State.Awake, Data.Empty) kote ! Commands.Stroke expectMsg("purrr") kote.stateName should be (State.Awake) }
åãééå§ç¶æ ãæ°åãã¹ãããããšã«é¢ããŠãç§ã¯æ¬¡ã®ç°¡åãªããªãã¯ãæãã€ããŸããïŒ
"while in Awake state" - { trait AwakeKoteState extends TestedKote { kote.setState(State.Awake, Data.Empty) } "should purr on stroke" in new AwakeKoteState { kote ! Commands.Stroke expectMsg("purrr") kote.stateName should be(State.Awake) } }
ã芧ã®ãšããããã¹ãŠã®ãèŠéããã¹ãçšã«ãèŠéç¶æ ã«ããããšãããµãã¿ã€ãã«ã®ãã¬ãŒã ãäœæãããã®äžã«ç¹æ§AwakeKoteStateãé 眮ããŸãïŒãã€ã³ãã§ã¯ãªãã¯ã©ã¹åããããšãã§ããŸãïŒã ãã®ç¶æ ã®ãã¹ãŠã®ãã¹ãã¯ããã®å©ããåããŠå®£èšããŸãã
ããã£ãšåœãå¹ã蟌ãããã€ãŸãæå³ã®ããããŒã¿ãå·ã«è¿œå ããæ¹æ³
ç§ãã¡ã®ç«ãäœãæ¬ ããŠããã®ãèããŠãã ãããŸããç§ã¯-ãã®ç©ºè ¹æãç§ã¯ç«ã飌ã£ãŠããŸãããç§ã¯ç§ã話ããŠããããšãç¥ã£ãŠããŸãïŒåœŒãã¯åžžã«é£ã¹ããã§ãïŒåœŒããç ããªããªããã¡ããã§ããããŠã倢ã®äžã§ãããªãã¯ã圌ããæããã°ã©ããšçŽ æŽãããå¥åº·çãªããŠã«ãèŠãã®ãèŠãŸãïŒããããç«ãã©ãã«é£¢ããããã®ã§ããããïŒåœŒã®çããŠãã芪relativeã®æ£ç¢ºãªäœçœ®ã¯ããããŸããããç§ãã¡ã®ãã·ã³ã§ã¯ãå·ã®ååã«å ããŠããããã®ç®çã®ããã«ãçŸåšç©ºã®ããŒã¿ããããŸããç§ã¯å°ãèããããšãææ¡ããŸããåºçæãæ®éã®ç«ã¯ããã«ãã£ã±ããæ¢ããŸããã ããã圌ã¯ãã§ã«å°ãç©ºè ¹ã«çãŸããŠããŸãããããŠãããªãã圌ãé€ããªãã°ã圌ã¯æºè ¹ã«ãªããŸããããªãã¡ãç©ºè ¹ã«ãªããŸãã圌ãç ããèµ°ããããã«ã¯é£ã¹ããããã°ãåžžã«ç©ºè ¹æ/æºè ¹æããããŸããã€ãŸããèªåã§æ³åã§ããã©ã®ç¶æ ã§ããããŒã¿ã空ã«ããããšã¯ã§ããŸãããããã¯ã€ãŸãä»ã®ããŒã¿ãçºè¡šããããããæšãŠãŠå¿ãããšãã§ãã圌ãã®æéã¯éããé²åã¯ãã®ããã«æ±ºå®ããŸããããããŠç§ãã¡ã¯åœŒãã®ããã«æ²ããããšã¯ãããŸããããããã£ãŠã飢ã¬ãã«ãå€æ°hungerïŒIntã§è¡šããã¬ãã«100ã¯åç«ã®é£¢diesæ»ãæå³ããã¬ãã«0以äžã¯éé£ãæå³ããŸãïŒããã¯å®¶æã飢excessiveã®éå°ãªã¬ãã«ãšåŒãã§ãããã®ã§ãïŒããããŠã圌ã¯ãäŸãã°60ã®ã¬ãã«ã§çãŸããŸã-ã€ãŸãããã§ã«å°ãç©ºè ¹ã§ããããŸã èããããŸããã±ãŒã¹ã¯ã©ã¹VitalSignsã«æ°ããå€æ°ãæŒã蟌ã¿ãã±ãŒã¹ãªããžã§ã¯ãEmptyãåé€ããŸãã Dataãªããžã§ã¯ãã«ããŒã¿ã®èª¬æãä¿åãç¶ããŸããã¬ãã«0以äž-éé£ããïŒç§ãã¡ã®å®¶æã飢levelã®æ¬ åŠã®é床ã®ã¬ãã«ãåŒã³åºãããã«ïŒããããŠã圌ã¯ãäŸãã°60ã®ã¬ãã«ã§çãŸããŸã-ã€ãŸãããã§ã«å°ãç©ºè ¹ã§ããããŸã èããããŸããã±ãŒã¹ã¯ã©ã¹VitalSignsã«æ°ããå€æ°ãæŒã蟌ã¿ãã±ãŒã¹ãªããžã§ã¯ãEmptyãåé€ããŸãã Dataãªããžã§ã¯ãã«ããŒã¿ã®èª¬æãä¿åãç¶ããŸããã¬ãã«0以äž-éé£ããïŒç§ãã¡ã®å®¶æã飢levelã®æ¬ åŠã®é床ã®ã¬ãã«ãåŒã³åºãããã«ïŒããããŠã圌ã¯ãäŸãã°60ã®ã¬ãã«ã§çãŸããŸã-ã€ãŸãããã§ã«å°ãç©ºè ¹ã§ããããŸã èããããŸããã±ãŒã¹ã¯ã©ã¹VitalSignsã«æ°ããå€æ°ãæŒã蟌ã¿ãã±ãŒã¹ãªããžã§ã¯ãEmptyãåé€ããŸãã Dataãªããžã§ã¯ãã«ããŒã¿ã®èª¬æãä¿åãç¶ããŸããã ããïŒ
... object Data { case class VitalSigns(hunger: Int) extends Data } ...
åœç¶ããããžã§ã¯ãå šäœã§ãData.EmptyãData.VitalSignsã«å€æŽããå¿ èŠããããŸããstartWithè¡ããéå§ïŒ
startWith(State.Sleeping, Data.VitalSigns(hunger = 60))
å®éãæ¢ã«èª¬æããç¶æ ã§ã®åç«ã®æ¢åã®åäœã§ã¯ãç§ãã¡ïŒãã¡ãã圌ïŒã¯ãã®éèŠãªã€ã³ãžã±ãŒã¿ãŒãæ°ã«ããªãã®ã§ãããã§Data.EmptyãVitalSignsã§ã¯ãªãã¢ã³ããŒã¹ã³ã¢ã§å®å šã«çœ®ãæããããšãã§ããŸãã
when(State.Sleeping) { case Event(Commands.WakeUp, _) => goto(State.Awake) } when(State.Awake) { case Event(Commands.Stroke, _) => stay() replying "purrr" }
ä»ãç§ãã¡ã®åç«ã¯ããã«é²åããŠããããã®è¡åãè€éã«ããååã«ããããããŠããå Žåã«ã®ã¿whenã§ããšãã«é³Žãé¿ããŸãã
when(State.Awake) { case Event(Commands.Stroke, Data.VitalSigns(hunger)) if hunger < 30 => stay() replying "purrr" case Event(Commands.Stroke, Data.VitalSigns(hunger)) => stay() replying "miaw!!11" }
ãããŠãã¹ãïŒ
"while in Awake state" - { trait AwakeKoteState extends TestedKote { def initialHunger: Int kote.setState(State.Awake, Data.VitalSigns(initialHunger)) } trait FullUp { def initialHunger: Int = 15 } trait Hungry { def initialHunger: Int = 75 } "should purr on stroke if not hungry" in new AwakeKoteState with FullUp { kote ! Commands.Stroke expectMsg("purrr") kote.stateName should be(State.Awake) } "should miaw on stroke if hungry" in new AwakeKoteState with Hungry { kote ! Commands.Stroke expectMsg("miaw!!11") kote.stateName should be(State.Awake) } }
ãåç©ã¯é£¢ããŠããïŒãããŸãã¯ã€ãã³ãã®èšç»æ¹æ³
åç«ã¯æéã®çµéãšãšãã«ç©ºè ¹ã®ã¬ãã«ããç²åŸãããå¿ èŠããããŸãïŒäœã§ããïŒæ®é · ããã人çã§ãïŒ
ã¡ãã»ãŒãžïŒ
case class GrowHungry(by: Int)
ã³ãïŒ
class Kote extends FSM[Kote.State, Kote.Data] { import Kote._ import context.dispatcher startWith(State.Sleeping, Data.VitalSigns(hunger = 60)) val hungerControl = context.system.scheduler.schedule(5.minutes, 5.minutes, self, Commands.GrowHungry(3)) override def postStop(): Unit = { hungerControl.cancel() } ...
«» , «» ( +3 5 ) , . hungerControl Cancellable, postStop, , dispatcher , , , , . : implicit ExecutionContext, import context.dispatcher.
« !», ,
èšäºãåŒããããªãããã«ãç§ã¯ããã«ç©ºè ¹ããã®ç«ã®æ»ïŒç©ºè ¹> = 100ïŒãšãç¹ã«ç©ºè ¹ç¶æ ïŒç©ºè ¹> 85ïŒãžã®ç§»è¡ãå®æããŸããã¢ãã«ã¯åœŒå¥³ã®ãã©ã³ããŒããã¹ãããã¡ãã»ãŒãžã¯æééãã«å°çãããšä¿¡ããç«ãããã«ã©ã®ããã«åå¿ããããæžããŸããç«ãç ã£ãŠãããç®ãèŠãŸããŠãããé£ã¹ç©ãèŠæ±ããŠãããé£ã¹ãŠãããããŠã¹ã§éãã§ãããªã©ããã¹ãŠã®æ¡ä»¶ã§ãèªç¶ãªèèªççŒããçºçããããšã«æ³šæããŠãã ããããã®å Žåã®å¯ŸåŠæ¹æ³ãã¹ãŠã®å¯èœãªç¶æ ã«å¯ŸããŠåãåäœã説æããŸããïŒãã¹ããšäžç·ã«ïŒãããŠãããæç¹ã§ãã¹ããæžãã®ãå¿ããŠããã®ãããªããŒã«ãèŠã€ããç«ã1ã€ã®ç¶æ ã§ããªãŒãºããŠæ°žé ã®æºè ¹ã楜ããã ãïŒã¯ãã圌ãšäžç·ã«å°çã«è¡ãã圌ã«æ¥œããã§ããããæ°ã«ããŸããããããçµå±ã®ãšããããã®ãã²ééã¯ãå€ãç¿æ £ã«åŸã£ãŠãå®æçã«é£äºãããèèªã¯çããŸãã-ãããŠçµå±ã圌ã¯äœå ã®é£ç©ã®éå°æåã§æ»ã¬ã§ãããïŒãã®å ŽåãFSMã¯ãåç«ãå¿ããæããããwhenUnhandledé¢æ°ã䜿çšããããšãææ¡ããŸãããã®é¢æ°ã¯ãçŸåšã®ç¶æ ã®åäœã®ãªãã·ã§ã³ã®ããããšãäžèŽããªãã¡ãã»ãŒãžã®ä»»æã®ç¶æ ã§æ©èœããŸããèŠããŠãããŠãç§ã¯ãããæžããFSM.NullFunctionã¯{_ =>}ãšã¯ç°ãªããŸããïŒä»ãããªãã¯æ£ç¢ºã«äœãæšæž¬ããŠãããšæããŸããæåã®ã±ãŒã¹ã§ã¢ã¯ã¿ãŒã«å±ããã¡ãã»ãŒãžãåŠçãããããããã¹ãŠãwhenUnhandledé¢æ°ã«è©²åœããå Žåã2çªç®ã®ã±ãŒã¹ã§ã¯ç¶æ³ã¯éã§ãããã¹ãŠã®ã¡ãã»ãŒãžã¯ããã€ãã¢ãŒé¢æ°ã«ãã£ãŠåã«ãåžåããããwhenUnhandledã«å°éããŸããã
whenUnhandled { case Event(Commands.GrowHungry(by), Data.VitalSigns(hunger)) => val newHunger = hunger + by if (newHunger < 85) stay() using Data.VitalSigns(newHunger) else if (newHunger < 100) goto(State.VeryHungry) using Data.VitalSigns(newHunger) else throw new RuntimeException("They killed the kitty! Bastards!") }
ããã§ã¯ãå¥ã®é¢æ°ã®åºçŸãèŠãããšãã§ããŸã-䜿çšããŠããŸããã³ã³ããã¹ãããã移è¡äžã«ç¹å®ã®ããŒã¿ãç¶æ ã«ãã€ã³ãã§ããstayãšgotoã®äž¡æ¹ã§äœ¿çšã§ããããšã¯æããã§ããã€ãŸããusingã䜿çšãããšãçŸåšã®ç¶æ ã®ãŸãŸã§æ°ããããŒã¿ã䜿çšããããæ°ããããŒã¿ã䜿çšããŠæ°ããç¶æ ã«ç§»åãããã§ããŸããã³ãŒãã®ä»¥åã®ãã¹ãŠã®ããŒãžã§ã³ã®ããã«usingãæå®ãããŠããªãå ŽåãããŒã¿ã¯å€æŽãããåããŸãŸã§ãã
ãçåœãäžããç çåŠãããŸãã¯äŸå€çãªç¶æ³ããã¹ãããæ¹æ³
æéãšã¹ããŒã¹ãç¯çŽããããã«ããã¹ãŠã®ãã¹ãã«ã€ããŠã¯èª¬æããŸããããããã¯ä»¥åã®ãã®ãšå€§å·®ãããŸãããèå³æ·±ãããšã«ãã¹ããŒãããäŸå€ããã¹ãããæ¹æ³ã«èšåããå¿ èŠããããšæããŸããå®éãFSMã®å Žåãããã¯éåžžã®ã¢ã¯ã¿ãŒã«é©çšå¯èœãªæ¹æ³ãšéãã¯ãããŸããã
"should die of hunger" in new AwakeKoteState with Hungry { intercept[RuntimeException] { kote.receive(Commands.GrowHungry(1000)) // headshot } }
ã¡ãã»ãŒãžãéä¿¡ãã代ããã«ïŒãã¹ãã§ã¯ãªãããã®å ŽåãŠãŒã¶ãŒã¬ãŒãã£ã¢ã³ã§ããã¹ãŒããŒãã€ã¶ãŒã¢ã¯ã¿ãŒã«åä¿¡ãé ä¿¡ãããã¹ãã¯åã«ãã¹ããŸããïŒãã¢ã¯ã¿ãŒã®receiveã¡ãœããåŒã³åºããçŽæ¥äœ¿çšããŸããããã¯ãç¹å®ã®ç¶æ³ã§ãã·ã³ãæ£ããäŸå€ãã¹ããŒããããšã確èªããããã«å¿ èŠã§ãã確ãã«ãç§ãã¡ã®è²§ããåç©ãå šèœã®ç£ç£è ãèçããããã©ããã¯ãããã«äŸåãç¶ããã¹ããŒãããäŸå€ã«å¯Ÿãã圌ã®æŠç¥ã§é©åãªããŒã¯ãèŠã€ããŸãããç«ã®æ»ã®åå ãšããŠäŸå€ã䜿çšããã®ã¯ããã®ãã¹ããå®èšŒããããã§ããããŸãã¯ãåã«stopïŒïŒãè¿ãããšãã§ããŸã-ããããéåžžã®ç«ã¯ç©ºè ¹ã§ã¯ãªãè霢ã§æ»ã«ãŸãã
ããã§ã«åŒåžãæ¢ããŠãç ããïŒãããŸãã¯ããç¶æ ã§ã®æ»åšæéãå¶éããæ¹æ³
ç§ãã¡èªèº«ãç ãã«èœã¡ãªãããã«ããã€ãã¹ããããªãæåŸã®æ©èœã«ã€ããŠã話ããŸããããã¯ãç¶æ ã®æ倧ã¿ã€ã ã¢ãŠããèšå®ããæ©èœã§ããåçŽã«èšå®ãããŸãïŒwhené¢æ°ã®å°æ°ç¹ã®åŸã«ãç¶æ ã®ååã®çŽåŸã«ç€ºãããŸããããšãã°ã3æéã®ç¡ç ã®åŸãç«ã¯ç®ãèŠãŸãã®ã§ãç®ãèŠãŸãå¿ èŠã¯ãããŸããã
when(State.Sleeping, 3.hours) { case Event(Commands.WakeUp, _) => goto(State.Awake) }
ã©ãæããŸãããèµ·ããŠïŒãããããèªäœã§ã¯ãã¿ã€ã ã¢ãŠãã¯ç¶æ ãããŒã¿ãå€æŽããŸãããããã¯ç«èªèº«ã圌èªèº«ã®ææ決å®ã«ãã£ãŠã®ã¿è¡ãããšãã§ããŸãïŒãŸããããªãããã圌ã¯ããæ»ããªããšèããŸãã;ãããŠå€ããã£ãã¯ã¯ãã¯ãã±ãŒãã§ã¯ãããŸããïŒããããŠãåäžã®å Žæã§-è¡åã®æ©èœã§ïŒããã¯ããªã«ã¯åœãŠã¯ãŸããŸããã圌ã¯è¥ãé ã®ãã£ãã¯ã®ããã«ã©ãã§ãã§ããŸãïŒãããããä»ã§ã¯ãæå®ãããæéã®åŸã誰ãç«ãèµ·ãããªãå ŽåãStateTimeoutã¡ãã»ãŒãžãåãåããŸãããããŠãããã«ã©ã®ããã«åå¿ãããã¯ã圌ã決ããããšã§ãã
when(State.Sleeping, 3.hours) { case Event(Commands.WakeUp | StateTimeout, _) => goto(State.Awake) }
ä»ã圌ã¯2ã€ã®çç±ããç®èŠããããšãã§ããŸãïŒåœŒãååã«é·ãç ããç ã£ãå ŽåããŸãã¯åœŒãåã§èµ·ããããå Žåããããã®2ã€ã®ã€ãã³ããåé¢ããŠãç°ãªãåå¿ãããããšãã§ããŸãã1ã€ã¯ã¢ã¯ãã£ãã§éã³å¿ãããããã1ã€ã¯éªæªãªæªèãæŸã¡ã絶ãã鳎ã声ãäžããŠãã¿ããªãåæºãããŸãããããã«ãããç«ã倢ã®ç¶æ ãæãããšãã¿ã€ã ã¢ãŠãã¯èªåçã«ãã£ã³ã»ã«ããïŒæããªã¹ã±ãžã¥ãŒã©ãŒã¯ããèªäœã§ãã£ã³ã»ã«ããå¿ èŠããããŸãïŒãè¶ èªç¶çãªããšã¯äœãèµ·ãããŸãããã¡ãªã¿ã«ãç«ãã¿ã€ã ã¢ãŠããåä¿¡ããããã¹ãªãŒãç¶æ ãç¶ããå ŽåïŒæ»åšïŒïŒãè¿ãïŒãäºæ³ã©ãã3æéåŸã«åã³ç«ãåä¿¡ããŸããã€ãŸããæ瀺çã«ãã£ã³ã»ã«ãŸãã¯åå²ãåœãŠãããããšãªãïŒstayïŒïŒã䜿çšããŸããForMaxïŒ20.hoursïŒãããã«ïŒãåäœæ©èœã«ææãããstayïŒïŒå¿çã䌎ãã¿ã€ã ã¢ãŠãäžå®ã®æéãçµéãããšãåã³ãã·ã¥ãŒããããŸãã
whené¢æ°ã§ç¶æ ã¿ã€ã ã¢ãŠããæå®ã§ãããšããäºå®ã«å ããŠïŒãããŠããã®ç¶æ ã«å ¥ããã³ã«åäœããŸãïŒãgotoé¢æ°ã«æž¡ããšãã«çŽæ¥æå®ããããšãã§ããŸãããåè¿°ã®forMaxé¢æ°ïŒããšãã°stayïŒïŒãforMaxïŒ1.minuteïŒãŸãã¯gotoïŒState.SleepingïŒ.usingïŒData.SomethingïŒ.forMaxïŒ1.minuteïŒïŒããã®ãããªã¿ã€ã ã¢ãŠãã¯ãã®ç¹å®ã®é·ç§»ã§ã®ã¿æ©èœããŸãïŒå€ã眮ãæããŸãïŒããã«ç€ºãããŠããå ŽåïŒïŒ
when(State.Sleeping, 3.hours) { case Event(Commands.WakeUp, _) => goto(State.Awake).forMax(3.hours) case Event(StateTimeout, _) => goto(State.Awake).forMax(5.hours) }
ä»ãç§ãã¡ã®ç«ã¯åã§ç®èŠãã3æéç®ãèŠãŸãç¶ããéåžžã¯5æéç ããŸãããã¡ãããAwakeç¶æ ã§StateTimeoutã€ãã³ããåŠçããããšãæ¡ä»¶ãšããŸãã
ãäœïŒïŒåœŒã¯ãã³ãããããŸããïŒ!!ãããŸãã¯ç¶æ éã®é·ç§»äžã«èªåã¢ã¯ã·ã§ã³ãèšå®ããæ¹æ³
ãããŠæåŸã«ãæåŸã«ãç§ã¯çŽæããŸããAkka FSMã«ã¯ãonTransitionã¡ãœãããšããå¥ã®äŸ¿å©ãªæ©èœããããŸããç¶æ ããç¶æ ãžã®é·ç§»äžã«ããã€ãã®ã¢ã¯ã·ã§ã³ãèšå®ã§ããŸãã次ã®ããã«äœ¿çšããŸãã
onTransition { case State.Sleeping -> State.Awake => log.warning("Meow!") case _ -> State.Sleeping => log.info("Zzzzz...") }
ãã¹ãŠãæãããªããã«æããŸããã念ã®ããã«èª¬æããŸããç¡ç ç¶æ ããèŠéç¶æ ã«ç§»è¡ããç¬éã«ãåç«ãç¹å¥ãªæ¹æ³ã§äžåºŠã ã鳎ããŸããä»»æã®ç¶æ ããç¡ç ç¶æ ã«ç§»è¡ãããšããã³ãã1ã€ã ãçºããããŸãïŒè±èªã§ã¯ããããã©ã®ããã«èãããŸãã
ãããã®ã¢ã¯ã·ã§ã³ã¯ãFSMActorRef.setStateé¢æ°ã䜿çšããŠãã¹ãã§ç¶æ ãèšå®ããå Žåã§ãæ©èœããŸãïŒãã¡ãããçŸåšã®ç¶æ ããã¿ãŒã²ããç¶æ ãžã®é·ç§»ãonTransitionã§èª¬æãããŠãããã®ã®ããããã«äžèŽããå ŽåïŒããããã£ãŠããããããã¹ãã§ããŸããããŠãããã§gotoé¢æ°ã䜿çšããŠãæå³ããªãããšã«æ³šæããŠãã ãããããã¯ãç¶æ é·ç§»äžã«é åºã«ããŒã¿ãå€æŽããããšããããšããããé·ãéããŒã¿ãæ©èœããªãçç±ãç解ã§ããªãã£ã人ãšããŠããªãã«äŒããŸããç§ãçºèŠããå¥ã®ãã¥ã¢ã³ã¹ïŒé·ç§»ããªã¬ãŒã¯ãåäœé¢æ°ããstayïŒïŒãè¿ãããšã§çŸåšã®ç¶æ ã«ãšã©ãŸãå Žåã§ãæ©èœããŸããããã¯Akkaã®å°æ¥ã®ããŒãžã§ã³ã§ä¿®æ£ããããšçŽæãããŠããŸããããçŸæç¹ã§ã¯ãSleepingç¶æ ããäœãã«åå¿ãããšãã«stayïŒïŒãè¿ããšãonTransitionãæ©èœããåç«ããã³ããããããšã«ãªããŸãã
çµãã
ä»æ¥ã¯ããã ãã«ã€ããŠè©±ãããã£ãã®ã§ãããããŠãç¬ç«ããç 究ã®ã¿ã¹ã¯ãšããŠã質åã«çããããšãææ¡ããŸããåãç¶æ åã«å¯ŸããŠwhené¢æ°ãé£ç¶ããŠæ°ååŒã³åºããããšã©ããªããŸããïŒãŸãã¯ãé£ç¶ããŠwhenUnhandledé¢æ°ãæ°ååŒã³åºããŸãããæž èŽããããšãããããŸããã
PSãã®èšäºãæžããŠããéçšã§ãçããŠããŠãæ»ãã§ãããªããã³ã¯äžäººãããªãã£ãã