Tracks
/
Elixir
Elixir
/
Übungen
/
Nummernziehen
Nummernziehen

Nummernziehen

Lernübung

Einführung

Prozesse

In Elixir läuft der gesamte Code in Prozessen ab.

Standardmäßig wird eine Funktion in demselben Prozess ausgeführt, aus dem sie aufgerufen wurde. Wenn du eine bestimmte Funktion ausdrücklich in einem neuen Prozess ausführen möchtest, verwende spawn/1:

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

spawn/1 erzeugt einen neuen Prozess, der die übergebene Funktion ohne Argumente ausführt, und gibt eine Prozesskennung (PID) zurück. Der neue Prozess bleibt am Leben, solange die Funktion läuft, und beendet sich danach still.

Prozesse in Elixir sollte man nicht mit Betriebssystemprozessen verwechseln. Prozesse in Elixir verbrauchen deutlich weniger Speicher und CPU. Es ist völlig in Ordnung, wenn eine Elixir-Anwendung Hunderte von Elixir-Prozessen ausführt.

Nachrichten

Prozesse teilen Informationen nicht direkt miteinander. Prozesse senden und empfangen Nachrichten, um Daten auszutauschen.

Mit send/2 kannst du einem beliebigen Prozess eine Nachricht senden. Das erste Argument von send/2 ist die PID des Empfängers, das zweite Argument ist die Nachricht.

Eine Nachricht kann von beliebigem Typ sein. Oft besteht sie aus Atomen und Tupeln. Wenn du eine Antwort erhalten möchtest, solltest du die PID des Absenders irgendwo in der Nachricht unterbringen. Die PID des aktuellen Prozesses erhältst du mit self().

send/2 prüft nicht, ob die Nachricht beim Empfänger angekommen ist, und auch nicht, ob der Empfänger noch läuft. Die Nachricht landet im Postfach des Empfängers und wird nur gelesen, wenn der Empfänger ausdrücklich darum bittet, Nachrichten zu empfangen.

Mit dem Makro receive/1 kannst du eine Nachricht aus einem Postfach lesen. Es akzeptiert einen do-Block, der einen Musterabgleich mit den Nachrichten durchführen kann.

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

receive/1 nimmt eine Nachricht aus dem Postfach, die auf eines der angegebenen Muster passt, und führt den zu diesem Muster angegebenen Ausdruck aus. Wenn sich keine Nachrichten im Postfach befinden oder keine der Nachrichten auf eines der Muster passt, wartet receive/1 auf eine.

receive-Schleife

Wenn du mehr als eine Nachricht empfangen möchtest, musst du receive/1 rekursiv aufrufen. Ein häufiges Muster ist eine rekursive Funktion, zum Beispiel mit dem Namen loop, die receive/1 aufruft, etwas mit der Nachricht macht und sich dann selbst aufruft, um auf weitere Nachrichten zu warten. Wenn du einen Zustand von einem receive/1-Aufruf zum nächsten mitnehmen musst, kannst du das tun, indem du der loop-Funktion ein Argument übergibst.

PIDs

Prozesskennungen sind ein eigener Datentyp. Sie fungieren als Postfachadressen: Wenn du die PID eines Prozesses hast, kannst du diesem Prozess eine Nachricht senden. PIDs entstehen normalerweise indirekt als Rückgabewert von Funktionen, die neue Prozesse erzeugen, wie spawn.

Anleitung

Du schreibst ein eingebettetes System für einen Wartenummern-Automaten. Es ist ein sehr einfaches Modell. Er kann fortlaufende Nummern ausgeben und melden, welche Nummer zuletzt ausgegeben wurde.

1. Starte den Automaten

Implementiere die Funktion start/0. Sie soll einen neuen Prozess erzeugen und die PID des Prozesses zurückgeben. Der neue Prozess muss noch nichts tun.

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

Beachte, dass die PID bei jedem Ausführen dieses Codes anders sein kann.

2. Melde den Zustand des Automaten

Passe den Automaten so an, dass der neu erzeugte Prozess bereit ist, Nachrichten zu empfangen (starte eine Empfangsschleife), mit einem Anfangszustand von 0. Er soll Nachrichten {:report_state, sender_pid} empfangen können. Als Antwort auf solche Nachrichten soll er seinen aktuellen Zustand (die zuletzt ausgegebene Nummer) an sender_pid senden und dann auf weitere Nachrichten warten.

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. Gib Nummern aus

Passe den Automaten so an, dass er Nachrichten {:take_a_number, sender_pid} empfangen kann. Er soll seinen Zustand um 1 erhöhen, den neuen Zustand an sender_pid senden und dann auf weitere Nachrichten warten.

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. Stoppe den Automaten

Passe den Automaten so an, dass er eine :stop-Nachricht empfangen kann. Er soll aufhören, auf weitere Nachrichten zu warten.

5. Ignoriere unerwartete Nachrichten

Passe den Automaten so an, dass er eine unerwartete Nachricht ignoriert und weiter auf weitere Nachrichten wartet.

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Elixir Exercism

Bereit, mit Nummernziehen zu starten?

Melde dich bei Exercism an, um Elixir mit 58 Konzepte168 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.