Треки
/
C++
C++
/
Вправи
/
Майстер лазаньї
Майстер лазаньї

Майстер лазаньї

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

Вступ

Термінологія

Ми вже використовували і писали функції C++ у кількох концепціях. Час перейти до технічних деталей. Наведений нижче фрагмент коду показує найпоширеніші терміни для довідки. Оскільки C++ ігнорує пробільні символи, форматування змінили так, щоб кожен елемент був на окремому рядку.

// Function declaration:
bool                                              // Return type
admin_detected(string user, string password)      // Type signature
;                                                 // Don't forget the ';' for the declaration

// Function definition:
bool                                              // Return type
admin_detected                                    // Function name
(string user, string password)                    // Parameter list
{ return user == "admin" && password == "1234"; } // Function body
Advanced

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

Кваліфікатор const

Іноді ми хочемо переконатися, що значення не можна змінити після ініціалізації. C++ використовує ключове слово const як кваліфікатор для констант.

const int number_of_dragon_balls{7};
number_of_dragon_balls--; // compilation error
Note

Константи часто записують у UPPER_SNAKE_CASE. Якщо немає іншої конвенції, цей регістр рекомендується залишати для макросів.

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

const також може бути кваліфікатором для параметрів функції.

string guess_number(const int& secret, const int& guess) {
    if (secret < guess) return "lower.";
    if (secret > guess) return "higher.";
    return "exact!";
}

Коли ми передаємо у функцію посилання const, можемо бути впевнені, що його не змінять. Ми часто бачитимемо посилання const для обʼєктів, які може бути дорого копіювати, як-от довші рядки тексту (англ. string). Третім випадком використання кваліфікатора const є функції-члени, які не змінюють екземпляр класу.

class Stubborn {
    public:
    Stubborn(string reply) {
        response = reply;
    }
    string answer(const string& question) const {
        if (question.length() == 0) { return ""; }
        return response;
    }
    private:
    string response{};
};

Функція-член answer класу Stubborn використовує посилання const string& як параметр. Це дозволяє уникнути копіювання вихідного обʼєкта, переданого у функцію.

Перевантаження функцій

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

Заголовок функції без типу повернення називають сигнатурою типу функції. Зміна сигнатури типу призводить до появи нової функції.

Приклад із play_sound має шість різних перевантажень, щоб охопити різні сценарії:

// different argument types:
void play_sound(char note);         // C, D, E, ..., B
void play_sound(string solfege);    // do, re, mi, ..., ti
void play_sound(int jianpu);        // 1, 2, 3, ..., 7

// different number of arguments:
void play_sound(string solfege, double duration);

// different qualifiers:
void play_sound(vector<string>& solfege);
void play_sound(const vector<string>& solfege);
Advanced

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

Типові аргументи

Деякі функції можуть бути дуже довгими, і в багатьох їхніх викликах для більшості параметрів використовуються ті самі значення. Повторення в таких викликах можна уникнути за допомогою типових аргументів.

void record_new_horse_birth(string name, int weight, string color="brown-ish", string dam="Alruccaba", string sire="Poseidon");

record_new_horse_birth("Urban Sea", 130); // color will be brown, dam "Alruccabam", sire "Poseidon"
record_new_horse_birth("Highclere", 175, "off-white", "Fall Aspen");   // sire will be "Poseidon"

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

Вказівки

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

На нас чекає пʼять завдань. Перше з них стосується самого готування, а решта чотири присвячені ідеальній підготовці.

1. Оцініть час підготовки

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

Реалізуйте функцію preparationTime, яка приймає вектор шарів як string і середній час підготовки одного шару в хвилинах як int. Функція має повернути оцінку загального часу підготовки на основі кількості шарів як int. Якщо середній час підготовки не передано, використовується типове значення 2.

std::vector<std::string> layers{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"};
preparationTime(layers, 3)
// => 18
preparationTime(layers)
// => 12

2. Обчисліть потрібну кількість локшини та соусу

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

Визначте функцію quantities, яка приймає як параметр вектор шарів типу string. Функція визначить потрібну кількість локшини та соусу для нашої страви. Результат має повертатися як amount struct, який уже визначено для нас. amount має дві змінні-члени: одна для ваги noodles як int, а друга для обʼєму sauce як double.

struct amount { int noodles; double sauce; };

amount needed{quantities({"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})};
// needed.noodles => 100
// needed.sauce => 0.4

3. Додайте секретний інгредієнт

Нещодавно ми завітали до друга й скуштували там лазанью. Вона була неймовірна й мала в собі щось особливе. Друг надіслав нам список інгредієнтів і сказав, що останній пункт у списку і є тим «секретним інгредієнтом», який зробив страву такою особливою. Тепер нам хочеться додати цей секретний інгредієнт і до власного рецепта.

Напишіть функцію addSecretIngredient, яка приймає два vector of strings як параметри. Перший параметр - це посилання на інгредієнти нашого власного рецепта, а другий вектор рядків містить інгредієнти, які надіслав друг. Останній елемент у нашому списку інгредієнтів завжди "?". Функція має замінити його на останній елемент зі списку друга. Зверніть увагу: addSecretIngredient нічого не повертає, тож список інгредієнтів треба змінювати безпосередньо. Список з інгредієнтами друга теж може бути посиланням (для кращої продуктивності), але його не можна змінювати. Подбайте, щоб список не змінювався, скориставшись const-посиланням.

std::vector<std::string> friendsList{"noodles", "sauce", "mozzarella", "kampot pepper"};
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};

addSecretIngredient(myList, friendsList);
// myList => {"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}

4. Масштабуйте рецепт

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

Реалізуйте функцію scaleRecipe, яка приймає два параметри.

  • Вектор кількостей типу double, потрібних на 2 порції.
  • Кількість порцій, яку ми хочемо приготувати.

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

std::vector<double> quantities{1.2, 3.6, 10.5};
scaleRecipe(quantities, 4);
// => { 2.4, 7.2, 21 }

5. Розкрийте сімейну таємницю

Ми памʼятаємо історії з дитинства, оповіді про нашу прапратітку, охоронницю найглибших кулінарних таємниць родини. Легенда каже, що саме вона має ключ до смаків, які неможливо уявити, але її методи нетрадиційні. Вона не має справи зі списками чи мірками; вона говорить пошепки, відкриваючи щоразу лише один інгредієнт.

Зі сумішшю хвилювання й остраху ми вирішуємо звернутися до нашої прапратітки. Як і очікувалося, вона відповідає своєю загадковою манерою, пропонуючи єдиний секретний інгредієнт, оповитий таємницею.

Внесок нашої прапратітки додає ще один рівень складності, який нам хочеться використати у своєму рецепті. Реалізуйте ще одну версію функції addSecretIngredient, яка приймає два аргументи: посилання на вектор, що представляє наш список інгредієнтів, і один рядок тексту (англ. string), що представляє загадковий дарунок від нашої прапратітки.

std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
std::string secretIngredient{"spice melange"};
addSecretIngredient(myList, secretIngredient);
// myList => {"noodles", "meat", "sauce", "mozzarella", "spice melange"}

Хай щастить, відважний кухарю!

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

Час розпочати Майстер лазаньї?

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