From 0e8e0277b8993967bb9a3e93aa5ce8fad80b407b Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain <olivier.blanvillain@epfl.ch> Date: Sat, 8 May 2021 16:00:03 +0200 Subject: [PATCH] Add exercises/solutions-5.md --- exercises/solutions-5.md | 118 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 exercises/solutions-5.md diff --git a/exercises/solutions-5.md b/exercises/solutions-5.md new file mode 100644 index 0000000..bc65596 --- /dev/null +++ b/exercises/solutions-5.md @@ -0,0 +1,118 @@ +# Exercise 1 : Message Processing Semantics + +For the `Client1` actor, the only possible output is 1. The reason is that messages between two actors are guaranteed to be received in the order they were sent. + +For the `Client2` actor, either 0 or 1 can be printed. There are no restrictions on the order in which messages are processed in this case. It might be the case that the `Memory` actor receives the `Write` message from the `Client2` first, or the Read message from the `Proxy` first. The order in which the messages are sent by the `Client2` doesn't change the possible behaviours of the system. In the case both messages are sent through the `Proxy`, then the only possible output is 1, since in this case the messages between the `Client2` and `Proxy`, as well as between `Proxy` and `Memory`, are guaranteed to be handled in the same order they were sent. + +## Exercise 2 : The Josephus Problem + +```scala +import akka.actor._ + +class Soldier(number: Int) extends Actor { + + import Soldier._ + + def receive: Receive = behavior(None, None, false) + + def behavior(next: Option[ActorRef], + killer: Option[ActorRef], + mustAct: Boolean): Receive = { + + case Death => next match { + case Some(myNext) => + sender ! Next(myNext) + myNext ! Act + println("Soldier " + number + " dies.") + self ! PoisonPill + + case None => + context.become(behavior( + next = None, + killer = Some(sender), + mustAct = mustAct)) + } + + case Next(newNext) => + if (newNext == self) { + println("Soldier " + number + " is last !") + } else if (!killer.isEmpty) { + killer.get ! Next(newNext) + newNext ! Act + println("Soldier " + number + " dies.") + self ! PoisonPill + } else if (mustAct) { + newNext ! Death + context.become(behavior( + next = None, + killer = None, + mustAct = false)) + } else { + context.become(behavior( + next = Some(newNext), + killer = None, + mustAct = false)) + } + } + + + case Act => next match { + case Some(myNext) => + myNext ! Death + context.become(behavior( + next = None, + killer = killer, + mustAct = false)) + + case None => + context.become(behavior( + next = None, + killer = killer, + mustAct = true)) + } + } +} + + +object Soldier { + // The different messages that can be sent between the actors: + + // The recipient should die. + case object Death + + // The recipient should update its next reference. + case class Next(next: ActorRef) + + // The recipient should act. + case object Act + + def props(number: Int): Props = Props(new Soldier(number)) +} + + +object Simulation { + + import Soldier._ + + // Initialization + val system = ActorSystem("mySystem") + + def start(n: Int) { + require(n >= 1) + + // Creation of the actors. + val actors = Seq.tabulate(n) { (i: Int) => + system.actorOf(Soldier.props(i), "Soldier" + i) + } + + // Inform all actors of the next actor in the circle. + for (i <- 0 to (n - 2)) { + actors(i) ! Next(actors(i + 1)) + } + actors(n - 1) ! Next(actors(0)) + + // Inform the first actor to start acting. + actors(0) ! Act + } +} +``` -- GitLab