Треки
/
Crystal
Crystal
/
Вправи
/
Кухонний калькулятор
Кухонний калькулятор

Кухонний калькулятор

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

Вступ

Кортеж - це скінченний впорядкований список елементів, який не можна змінити. Кортежі вимагають, щоб кожна позиція мала фіксований тип. Це, своєю чергою, означає, що компілятор знає, який тип стоїть на кожній позиції. Типи, які використовуються в кортежі, можуть відрізнятися на різних позиціях, але всі вони повинні бути відомі під час компіляції.

Створення кортежу

Залежно від того, чи можна визначити типи значень кортежу вже під час компіляції, кортеж можна створити різними способами. Якщо значення відомі під час компіляції, кортеж можна створити за допомогою синтаксису літерала кортежу, інакше їх потрібно оголосити явно. Також важливо, щоб типи значень збігалися з типами, указаними в кортежі, і щоб кількість значень збігалася з кількістю вказаних типів. Ось приклад оголошення через синтаксис літерала кортежу:

tuple = {1, "foo", 'c'} # Tuple(Int32, String, Char)

Кортеж можна також створити за допомогою класу Tuple.

tuple = Tuple(Int32, String, Char).new(1, "foo", 'c')

Або ж можна явно вказати тип змінної, якій присвоюється кортеж.

tuple : Tuple(Int32, String, Char) = {1, "foo", 'c'}

Явне зазначення типу кортежу буває корисним, адже воно дає змогу вказати, що позиція має містити обʼєднання типів. Це означає, що одна позиція може містити значення різних типів.

tuple : Tuple(Int32 | String, String, Char) = {1, "foo", 'c'}

Перетворення

Створення кортежу з масиву

Кортеж можна створити з масиву за допомогою методу from класу Tuple. Для цього потрібно вказати тип кортежу.

array = [1, "foo", 'c']
tuple = Tuple(Int32, String, Char).from(array)

Перетворення в масив

Кортеж можна перетворити в масив за допомогою методу to_a. Тип елементів отриманого масиву - це обʼєднання типів усіх полів кортежу.

tuple = {1, "foo", 'c'}
array = tuple.to_a
array # => [1, "foo", 'c']

Доступ до елементів

Як і в масивах, індексація кортежів починається з нуля: перший елемент має індекс 0. Однак, на відміну від масивів, тип кожного елемента фіксований і відомий під час компіляції, тому під час індексації кортежу тип елемента залежить від його позиції. Щоб отримати елемент кортежу, можна скористатися оператором [].

array = [1, "foo", 'c']
array[0]         # => 1
typeof(array[0]) # => Int32 | String | Char

tuple = {1, "foo", 'c'}
tuple[0]         # => 1
typeof(tuple[0]) # => Int32

Ще одна відмінність від доступу до елементів масиву: якщо індекс указано, компілятор перевіряє, чи він не виходить за межі кортежу. Це означає, що ми отримаємо помилку під час компіляції, а не під час виконання.

tuple = {1, "foo", 'c'}
tuple[3]
# => Error: index out of bounds for Tuple(Int32, String, Char) (3 not in -3..2)

Однак, якщо індекс збережено в змінній, компілятор не зможе перевірити під час компіляції, чи не виходить індекс за межі кортежу, і замість цього видасть помилку під час виконання.

Підкортеж

Отримати підкортеж кортежу можна за допомогою оператора [] із діапазоном. Повертається новий кортеж з елементами із зазначеного діапазону. Діапазон потрібно вказати під час компіляції, інакше компілятор не зможе визначити типи елементів підкортежу. Це означає, що діапазон має бути літералом діапазону, а не значенням, присвоєним змінній.

tuple = {1, "foo", 'c'}
subtuple = tuple[0..1] # Tuple(Int32, String)

i = 0..1
tuple[i]
# Error: Tuple#[](Range) can only be called with range literals known at compile-time

Коли використовувати кортеж

Кортежі стають у пригоді, коли потрібно згрупувати фіксовану кількість значень, типи яких відомі під час компіляції. Це тому, що кортежі займають менше памʼяті й працюють швидше за масиви завдяки своїй незмінності. Ще один варіант використання - повернення кількох значень із методу. Це особливо зручно, коли значення мають різні типи, адже кожна позиція кортежу може мати власний тип.

Кортежі не варто використовувати, коли потрібна структура даних, яка може збільшуватися чи зменшуватися або яку часто доводиться змінювати.

Символи - це іменовані ідентифікатори, які можна використати, щоб послатися на значення. Символи створюються за допомогою літерала символу, тобто шляхом додавання знака : перед іменем, наприклад :foo. Їх також можна записати в лапках, наприклад :"foo", що дає змогу, скажімо, використовувати пробіли в імені.

:foo           # => :foo
:"foo boo"     # => :"foo boo"
:foo == :"foo" # => true

Символи використовуються в мові в багатьох місцях: зокрема як ключі в іменованих кортежах і для позначення назв методів та змінних.

Символи в Crystal

Символи в Crystal досить помітно відрізняються від символів у Ruby. У Crystal символ - це різновид константи, тому він задається під час компіляції. Це означає, що символи не можна створювати динамічно, а в Ruby це можливо.

У Crystal символи представлені значеннями Int32, що робить їх дуже ефективними.

Перетворення

Символи можна перетворити на рядки тексту (англ. string), але не навпаки. Це тому, що символи створюються під час компіляції, а рядки тексту - під час виконання.

:foo.to_s # => "foo"

Вказівки

Готуючись спекти печиво для друзів, ми виявили, що деякі міри з рецепта доведеться перевести. Знайомі лише з метричною системою, ми маємо придумати спосіб переводити поширені американські кулінарні міри в мілілітри (мл) для власної зручності.

Скористайтеся цією таблицею переведення у своєму рішенні:

Одиниця для переведення обʼєм у мілілітрах (мл)
мл 1 1
чашка США 1 240
рідка унція США 1 30
чайна ложка США 1 5
столова ложка США 1 15

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

1. Отримати числову складову з пари обʼєму

Реалізуйте метод KitchenCalculator.get_volume. Отримавши кортеж-пару обʼєму, він має повернути лише числову складову.

KitchenCalculator.get_volume({:cup, 2.0})
# => 2.0

2. Перевести пару обʼєму в мілілітри

Реалізуйте метод KitchenCalculator.to_milliliter. Отримавши кортеж-пару обʼєму, він має перевести обʼєм у мілілітри за таблицею переведення.

KitchenCalculator.to_milliliter({:cup, 2.5})
# => {:milliliter, 600.0}

3. Перевести пару обʼєму в мілілітрах в іншу одиницю

Реалізуйте метод KitchenCalculator.from_milliliter. Отримавши кортеж-пару обʼєму та потрібну одиницю, він має перевести обʼєм у потрібну одиницю за таблицею переведення.

KitchenCalculator.from_milliliter({:milliliter, 1320.0}, :cup)
# => {:cup, 5.5}

4. Перевести з будь-якої одиниці в будь-яку

Реалізуйте метод KitchenCalculator.convert. Отримавши кортеж-пару обʼєму та потрібну одиницю, він має перевести заданий обʼєм у потрібну одиницю.

KitchenCalculator.convert({:teaspoon, 9.0}, :tablespoon)
# => {:tablespoon, 3.0}
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Crystal Exercism

Час розпочати Кухонний калькулятор?

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