Структури - це розширення, побудоване поверх мап, яке забезпечує перевірки під час компіляції та типові значення. Структура називається так само, як модуль, у якому її визначено. Щоб визначити структуру, скористаймося конструкцією defstruct. Зазвичай ця конструкція йде відразу після визначення модуля. defstruct приймає або список атомів (для типових значень nil), або список ключових слів (для заданих типових значень). Поля без типових значень мають іти перед полями з типовими значеннями.
defmodule Plane do
defstruct [:engine, wings: 2]
end
plane = %Plane{}
# => %Plane{engine: nil, wings: 2}
Оскільки структури побудовані на мапах, ми можемо використовувати більшість функцій для мап, щоб отримувати значення та працювати з ними. Для структур Поведінка доступу не реалізована. Натомість для доступу до полів структури радимо використовувати оператор статичного доступу ..
отримати або вибрати значення полів:
plane = %Plane{}
plane.engine
# => nil
Map.fetch(plane, :wings)
# => {:ok, 2}
оновити значення полів
plane = %Plane{}
%{plane | wings: 4}
# => %Plane{engine: nil, wings: 4}
Структури можна використовувати в зіставленні зі зразком як із назвою структури, так і без неї.
plane = %Plane{}
%Plane{wings: wings} = plane
%{wings: wings} = plane
Якщо включити назву структури у зразок, ми можемо переконатися, що і ліва, і права частина є структурами одного типу.
defmodule Helicopter do
defstruct [:engine, rotors: 1]
end
%Plane{} = %Helicopter{}
# => (MatchError) no match of right hand side value: %Helicopter{engine: nil, rotors: 1}
Ми можемо скористатися атрибутом модуля @enforce_keys зі списком ключів полів, щоб гарантувати, що значення ініціалізуються під час створення структури. Якщо ключ не вказано в списку, його значенням буде nil, як у прикладі вище. Якщо обовʼязковий ключ не ініціалізовано, виникає помилка.
defmodule User do
@enforce_keys [:username]
defstruct [:username]
end
%User{}
# => (ArgumentError) the following keys must also be given when building struct User: [:username]
У цій вправі ми пограємося з радіокерованою машинкою, на яку ми нарешті назбирали грошей.
Машинки починають із повністю зарядженою батареєю (100%). Щоразу, коли ми керуємо машинкою за допомогою пульта дистанційного керування, вона проїжджає 20 метрів і витрачає один відсоток заряду батареї. Прізвисько машинки невідоме, доки її не створять.
Радіокерована машинка має гарний світлодіодний дисплей, який показує дві частини інформації:
"<METERS> meters"."Battery at <PERCENTAGE>%".Якщо заряд батареї сягає 0%, ми більше не можемо керувати машинкою, і дисплей батареї покаже "Battery empty".
Реалізуйте функцію RemoteControlCar.new/0, яка повертає структуру нової радіокерованої машинки:
RemoteControlCar.new()
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "none"
# }
Прізвисько є обовʼязковим для структури, тож переконайтеся, що значення ініціалізується у функції new, а не в самій структурі.
Реалізуйте функцію RemoteControlCar.new/1, яка повертає структуру нової радіокерованої машинки із заданим прізвиськом:
RemoteControlCar.new("Blue")
# => %RemoteControlCar{
# battery_percentage: 100,
# distance_driven_in_meters: 0,
# nickname: "Blue"
# }
Реалізуйте функцію RemoteControlCar.display_distance/1, яка повертає відстань у тому вигляді, як її показано на світлодіодному дисплеї:
car = RemoteControlCar.new()
RemoteControlCar.display_distance(car)
# => "0 meters"
Переконайтеся, що функція приймає як аргумент лише структуру RemoteControlCar.
Реалізуйте функцію RemoteControlCar.display_battery/1, яка повертає відсоток заряду батареї у тому вигляді, як його показано на світлодіодному дисплеї:
car = RemoteControlCar.new()
RemoteControlCar.display_battery(car)
# => "Battery at 100%"
Переконайтеся, що функція приймає як аргумент лише структуру RemoteControlCar. Якщо заряд батареї сягає 0%, на дисплеї батареї відобразиться «Battery empty».
Реалізуйте функцію RemoteControlCar.drive/1, яка:
RemoteControlCar.new("Red")
|> RemoteControlCar.drive()
# => %RemoteControlCar{
# battery_percentage: 99,
# distance_driven_in_meters: 20,
# nickname: "Red"
# }
Переконайтеся, що функція приймає як аргумент лише структуру RemoteControlCar.
Оновіть функцію RemoteControlCar.drive/1 так, щоб вона не збільшувала пройдену відстань і не зменшувала відсоток заряду батареї, коли батарея розряджена (0%):
%RemoteControlCar{
battery_percentage: 0,
distance_driven_in_meters: 1980,
nickname: "Red"
}
|> RemoteControlCar.drive()
# => %RemoteControlCar{
# battery_percentage: 0,
# distance_driven_in_meters: 1980,
# nickname: "Red"
# }
Зареєструйтеся на Exercism, щоб вивчати й опановувати Elixir, а також 58 концепцій168 вправ та справжнє наставництво від людей, і все це безкоштовно.