# Домашнє завдання · Функції

> Лекція: [lecture.html](lecture.html) · Практика: [practice.ipynb](practice.ipynb) · Тест: [quiz.html](quiz.html)

Працюємо з тим самим кошиком покупок, що в лекції й практиці:

```python
покупки = ["хліб", "молоко", "яблука", "мед", "сіль"]
ціни = {"хліб": 28.5, "молоко": 32.0, "яблука": 19.9, "мед": 145.0, "сіль": 12.0}
кількості = [2, 1, 3, 1, 1]
```

Правило на все ДЗ: **розрахункові функції нічого не друкують**. Друкує той, хто їх
викликав. Кожен рівень закінчується блоком `assert` — якщо він мовчить, рівень зданий.

---

## 🟢 Рівень 1 — База

Напиши три маленькі функції, у кожної з яких є `return` і докстрінг з одного речення.

1. `вартість_доставки(сума)` — повертає число: `79`, якщо сума чека менша за 200;
   `49`, якщо від 200 до 499.99; `0`, якщо 500 і більше.
   Напиши це **кількома `return` без жодного `else`**.
2. `середня_ціна(ціни)` — повертає середню ціну зі словника цін, округлену до двох знаків.
3. `назви_дорожче(ціни, межа)` — повертає **список** назв, чия ціна більша за межу.

Перевір себе:

```python
assert вартість_доставки(0) == 79
assert вартість_доставки(199.99) == 79
assert вартість_доставки(200) == 49
assert вартість_доставки(499.99) == 49
assert вартість_доставки(500) == 0
assert середня_ціна(ціни) == 47.48
assert назви_дорожче(ціни, 100) == ["мед"]
assert назви_дорожче(ціни, 1000) == []
print("✅ рівень 1")
```

**Зроблено, якщо:** усі вісім `assert` мовчать, у `вартість_доставки` немає слова
`else`, і `help(вартість_доставки)` показує твій докстрінг.

---

## 🟡 Рівень 2 — Плюс

Візьми будь-який свій попередній скрипт на 15-30 рядків (згодиться домашка з
[теми 12](../12-loops/homework.md)) і **розріж його на функції**. Умови:

1. Кожна функція робить **одну** річ — в описі того, що вона робить, не має бути «і».
2. Жодна розрахункова функція не містить `print`. Друк — окремою функцією.
3. У кожної функції є докстрінг: що повертає і що означають параметри.
4. Головний сценарій після рефакторингу — це послідовність викликів,
   а не суцільний потік обчислень.

Потім доведи, що розрахунок не зіпсувався. Збережи результат **старого** скрипта
в імʼя й порівняй його з тим, що дають нові функції:

```python
assert новий_результат == старий_результат, "рефакторинг змінив відповідь!"
assert друкувальна_функція(...) is None, "функція, яка тільки друкує, повертає None"
print("✅ рівень 2")
```

Наостанок напиши в комірці markdown **два речення**: скільки місць довелось би
правити в старій версії при зміні однієї формули — і скільки в новій.

**Зроблено, якщо:** обидва `assert` мовчать, у файлі є щонайменше три функції з
докстрінгами, жодна розрахункова не містить `print`, і два речення про місця правки
написані.

---

## 🔴 Рівень 3 — Виклик

Напиши функцію `звіт_кошика(ціни, покупки, кількості)`, яка повертає **кортеж**
з чотирьох значень: `(сума, ставка_знижки, до_сплати, найдорожча_назва)`.

Умови:

1. **Рання відмова першим рядком тіла.** Якщо кошик порожній — одразу
   `return (0, 0.0, 0, None)`, без жодного вкладеного `if`.
2. Функція **чиста**: не друкує, не змінює передані списки й словник, на однакових
   аргументах завжди дає однакове.
3. Усередині вона **викликає інші функції** — щонайменше `ставка_знижки(сума)`
   з практики. Порахувати все в одному тілі не можна.
4. Докстрінг описує, що саме лежить у кожному з чотирьох елементів кортежу.

Перевірка:

```python
сума, ставка, разом, найдорожча = звіт_кошика(ціни, покупки, кількості)
assert (сума, ставка, разом, найдорожча) == (305.7, 0.05, 290.41, "мед")
assert звіт_кошика(ціни, [], []) == (0, 0.0, 0, None)
assert звіт_кошика(ціни, ["сіль"], [1]) == (12.0, 0.0, 12.0, "сіль")
assert покупки == ["хліб", "молоко", "яблука", "мед", "сіль"], "вхідні дані змінились"
assert звіт_кошика(ціни, покупки, кількості) == звіт_кошика(ціни, покупки, кількості)
print("✅ рівень 3")
```

**Зроблено, якщо:** усі пʼять `assert` мовчать, перший рядок тіла — рання відмова
з `return`, у тілі є виклик хоча б однієї іншої функції, і `help(звіт_кошика)`
пояснює всі чотири елементи кортежу.

---

## Підказки

* **Не знаєш, `return` чи `print`?** Спитай себе: «чи знадобиться це число комусь
  далі в програмі?». Якщо так — `return`. Функція завжди може віддати число, а
  надрукувати його можна зовні; навпаки не працює.
* **Кілька `return` — це нормально.** Якщо хочеться написати `else`, спробуй
  спершу перевернути умову й вийти одразу: `if not кошик: return …`. Часто після
  цього `else` стає непотрібним.
* **Кома в `return` створює кортеж.** `return сума, ставка` — це вже пара, дужки
  ставити не обовʼязково. На боці виклику її розпакує
  `сума, ставка = функція(...)` з [теми 07](../07-tuples/lecture.html).
* **Порожній кошик — не дрібниця.** Перевір усі свої функції на `[]` і `{}`
  окремо: саме там ховаються ділення на нуль і `max()` на порожній послідовності.
* **Якщо `assert` падає з незрозумілим повідомленням**, надрукуй те, що функція
  насправді повернула, поруч із тим, що очікувалось. Половина розбіжностей —
  це `None` замість числа, тобто забутий `return`.
