Треки
/
Elixir
Elixir
/
Вправи
/
Візьміть номерок
Візьміть номерок

Візьміть номерок

Навчальна вправа

Вступ

Процеси

У Elixir весь код виконується всередині процесів.

За замовчуванням функція виконується в тому самому процесі, з якого її викликали. Коли потрібно явно запустити певну функцію в новому процесі, скористаймося spawn/1:

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

spawn/1 створює новий процес, який виконує передану функцію без аргументів і повертає ідентифікатор процесу (PID). Новий процес буде живим, доки виконується функція, а потім тихо завершиться.

Процеси Elixir не варто плутати з процесами операційної системи. Процеси Elixir використовують набагато менше памʼяті та процесорного часу. Цілком нормально мати застосунки Elixir, у яких працюють сотні процесів Elixir.

Повідомлення

Процеси не діляться інформацією безпосередньо один з одним. Щоб обмінюватися даними, процеси надсилають і отримують повідомлення.

Можна надіслати повідомлення будь-якому процесу за допомогою send/2. Перший аргумент send/2 - це PID одержувача, а другий аргумент - саме повідомлення.

Повідомлення може бути будь-якого типу. Часто воно складається з атомів і кортежів. Якщо потрібно отримати відповідь, варто включити PID відправника кудись у повідомлення. PID поточного процесу можна отримати за допомогою self().

send/2 не перевіряє, чи повідомлення було отримано одержувачем, ані чи одержувач ще живий. Повідомлення потрапляє до поштової скриньки одержувача, і його буде прочитано лише тоді, коли одержувач явно попросить отримати повідомлення.

Повідомлення можна прочитати з поштової скриньки за допомогою макроса receive/1. Він приймає блок do, який уміє зіставляти повідомлення зі зразком.

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

receive/1 візьме одне повідомлення з поштової скриньки, яке відповідає будь-якому із заданих зразків, і виконає вираз, заданий для цього зразка. Якщо в поштовій скриньці немає повідомлень або жодне з повідомлень не відповідає жодному зі зразків, receive/1 чекатиме на одне з них.

Цикл отримання

Якщо потрібно отримати більше ніж одне повідомлення, потрібно викликати receive/1 рекурсивно. Поширений прийом - реалізувати рекурсивну функцію, наприклад, з назвою loop, яка викликає receive/1, щось робить із повідомленням, а потім викликає саму себе, щоб чекати на нові повідомлення. Якщо потрібно передати якийсь стан від одного виклику receive/1 до іншого, це можна зробити, передавши аргумент у цю функцію loop.

Ідентифікатори процесів

Ідентифікатори процесів - це окремий тип даних. Вони виконують роль адрес поштових скриньок: якщо у нас є PID процесу, ми можемо надіслати повідомлення цьому процесу. PID зазвичай створюються опосередковано, як повернене значення функцій, що створюють нові процеси, як-от spawn.

Вказівки

Ми пишемо вбудовану систему для машини, що видає номери. Це дуже проста модель. Вона може видавати послідовні номери й повідомляти, який номер було видано останнім.

1. Запустіть машину

Реалізуйте функцію start/0. Вона повинна створити новий процес і повернути PID цього процесу. Новий процес поки що не повинен нічого робити.

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

Зауважте, що щоразу, коли ми запускаємо цей код, PID може бути іншим.

2. Повідомте стан машини

Змініть машину так, щоб новостворений процес був готовий отримувати повідомлення (запустивши цикл отримання) з початковим станом 0. Він повинен уміти отримувати повідомлення {:report_state, sender_pid}. У відповідь на ці повідомлення він повинен надіслати свій поточний стан (номер останнього виданого талона) до sender_pid, а потім чекати на нові повідомлення.

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. Видавайте номери

Змініть машину так, щоб вона могла отримувати повідомлення {:take_a_number, sender_pid}. Вона повинна збільшити свій стан на 1, надіслати новий стан до sender_pid, а потім чекати на нові повідомлення.

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. Зупиніть машину

Змініть машину так, щоб вона могла отримувати повідомлення :stop. Вона повинна перестати чекати на нові повідомлення.

5. Ігноруйте неочікувані повідомлення

Змініть машину так, щоб, коли вона отримує неочікуване повідомлення, вона його ігнорувала й продовжувала чекати на нові повідомлення.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Elixir Exercism

Час розпочати Візьміть номерок?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Elixir, а також 58 концепцій168 вправ та справжнє наставництво від людей, і все це безкоштовно.