Parcours
/
Elixir
Elixir
/
Exercices
/
Prends un numéro
Prends un numéro

Prends un numéro

Exercice d'apprentissage

Introduction

Processus

En Elixir, tout le code s'exécute à l'intérieur de processus.

Par défaut, une fonction s'exécute dans le même processus que celui depuis lequel elle a été appelée. Lorsque tu as besoin d'exécuter explicitement une fonction donnée dans un nouveau processus, utilise spawn/1 :

spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>

spawn/1 crée un nouveau processus qui exécute la fonction d'arité 0 donnée et renvoie un identifiant de processus (PID). Le nouveau processus reste en vie tant que la fonction s'exécute, puis se termine silencieusement.

Il ne faut pas confondre les processus d'Elixir avec les processus du système d'exploitation. Les processus d'Elixir utilisent beaucoup moins de mémoire et de CPU. Il n'y a aucun problème à avoir des applications Elixir qui font tourner des centaines de processus Elixir.

Messages

Les processus ne partagent pas directement d'informations entre eux. Les processus envoient et reçoivent des messages pour partager des données.

Tu peux envoyer un message à n'importe quel processus avec send/2. Le premier argument de send/2 est le PID du destinataire, le second argument est le message.

Un message peut être de n'importe quel type. Souvent, il est composé d'atomes et de tuples. Si tu veux obtenir une réponse, tu dois inclure le PID de l'expéditeur quelque part dans le message. Tu peux obtenir le PID du processus courant avec self().

send/2 ne vérifie pas si le message a été reçu par le destinataire, ni si le destinataire est toujours en vie. Le message se retrouve dans la boîte aux lettres du destinataire et ne sera lu que si celui-ci demande explicitement à recevoir des messages.

On peut lire un message dans une boîte aux lettres à l'aide de la macro receive/1. Elle accepte un bloc do qui peut faire du filtrage par motif sur les messages.

receive do
  {:ping, sender_pid} -> send(sender_pid, :pong)
  :do_nothing -> nil
end

receive/1 va prendre un message dans la boîte aux lettres qui correspond à l'un des motifs donnés et exécuter l'expression fournie pour ce motif. S'il n'y a aucun message dans la boîte aux lettres, ou si aucun des messages de la boîte aux lettres ne correspond à l'un des motifs, receive/1 va en attendre un.

Boucle de réception

Si tu veux recevoir plus d'un message, tu dois appeler receive/1 de manière récursive. C'est un schéma courant d'implémenter une fonction récursive, par exemple nommée loop, qui appelle receive/1, fait quelque chose avec le message, puis s'appelle elle-même pour attendre d'autres messages. Si tu as besoin de transporter un état d'un appel à receive/1 à un autre, tu peux le faire en passant un argument à cette fonction loop.

Les PID

Les identifiants de processus sont leur propre type de données. Ils servent d'adresses de boîte aux lettres : si tu as le PID d'un processus, tu peux envoyer un message à ce processus. Les PID sont généralement créés indirectement, comme valeur de retour de fonctions qui créent de nouveaux processus, comme spawn.

Instructions

Tu écris un système embarqué pour une machine à tickets Take-A-Number. C'est un modèle très simple, qui distribue des numéros consécutifs et indique quel a été le dernier numéro distribué.

1. Démarre la machine

Implémente la fonction start/0. Elle doit lancer un nouveau processus et renvoyer le PID de ce processus. Le nouveau processus n'a pas encore besoin de faire quoi que ce soit.

TakeANumber.start()
# => #PID<0.138.0>

Note que le PID peut être différent chaque fois que tu exécutes ce code.

2. Signale l'état de la machine

Modifie la machine pour que le processus nouvellement lancé soit prêt à recevoir des messages (démarre une boucle de réception) avec un état initial de 0. Il doit pouvoir recevoir des messages {:report_state, sender_pid}. En réponse à ces messages, il doit envoyer son état actuel (le dernier numéro de ticket distribué) à sender_pid, puis attendre d'autres messages.

machine_pid = TakeANumber.start()

# a client sending a message to the machine
send(machine_pid, {:report_state, self()})

# a client receiving a message from the machine
receive do
  msg -> msg
end

# => 0

3. Distribue des numéros

Modifie la machine pour qu'elle puisse recevoir des messages {:take_a_number, sender_pid}. Elle doit augmenter son état de 1, envoyer le nouvel état à sender_pid, puis attendre d'autres messages.

machine_pid = TakeANumber.start()

# a client sending a message to the machine
send(machine_pid, {:take_a_number, self()})

# a client receiving a message from the machine
receive do
  msg -> msg
end

# => 1

4. Arrête la machine

Modifie la machine pour qu'elle puisse recevoir un message :stop. Elle doit cesser d'attendre d'autres messages.

5. Ignore les messages inattendus

Modifie la machine pour que, lorsqu'elle reçoit un message inattendu, elle l'ignore et continue d'attendre d'autres messages.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet
Elixir Exercism

Prêt à commencer Prends un numéro ?

Inscris-toi sur Exercism pour apprendre et maîtriser Elixir avec 58 concepts168 exercices, et un vrai mentorat humain, le tout gratuitement.