Домашнє завдання

Цикли

Три рівні. Кожен наступний спирається на попередній, але братися можна за будь-який. Пиши в окремому файлі homework_12.py або в новому зошиті — головне, щоб код запускався й друкував результат.

Дозволено з бібліотек: math, sys, collections. Більше нічого не треба. Функцій (тема 14) і спискових включень (тема 13) ми ще не проходили — усі завдання розвʼязуються звичайними циклами, і саме так їх і треба розвʼязувати.


рівень 1Рівень 1 — База

1.1 · Свій кошик і свій чек

Створи власний список покупок (щонайменше шість позицій), словник цін до нього й список кількостей тієї самої довжини. Потім:

  1. одним циклом надрукуй пронумерований чек через enumerate(…, start=1) — у форматі 1. хліб × 2 = 57.00;
  2. другим циклом накопич суму до сплати;
  3. третім циклом порахуй, скільки позицій дорожчі за середню ціну кошика.

Зроблено, якщо: чек друкується з нумерацією від 1, а наприкінці виконуються

assert round(до_сплати, 2) == round(sum(ціни[н] * к for н, к in zip(покупки, кількості)), 2)
assert len(покупки) == len(кількості) == len(ціни)

(перший assert — єдине місце в цьому ДЗ, де можна взяти генератор: він тут не для розвʼязку, а для перевірки твого циклу незалежним способом).

1.2 · Три форми range без підглядання

Не запускаючи код, випиши на папірці, що дадуть ці пʼять викликів, і лише потім перевір себе:

range(4)
range(1, 5)
range(0, 10, 3)
range(10, 0, -3)
range(5, 5)

Зроблено, якщо: для кожного виклику ти надрукував list(...) і len(...), а для двох останніх — письмово пояснив одним реченням, чому вийшло саме стільки значень. Перевір формулу на всіх пʼятьох:

import math
for старт, стоп, крок in [(0, 4, 1), (1, 5, 1), (0, 10, 3), (10, 0, -3), (5, 5, 1)]:
    очікувано = max(0, math.ceil((стоп - старт) / крок))
    assert len(range(старт, стоп, крок)) == очікувано

1.3 · Слово навпаки й палiндром

Візьми будь-яке слово. Одним циклом збери його перевернуту версію (як у практиці), другим — порахуй, скільки в ньому голосних ("аеєиіїоуюя").

Зроблено, якщо: виконуються

assert навпаки == слово[::-1]
assert голосних == sum(1 for с in слово if с in "аеєиіїоуюя")

і твій код правильно каже про "око" — що це паліндром, а про "мед" — що ні.


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

2.1 · Максимум без max()

Напиши цикл, який знаходить найдорожчу позицію в словнику цін: заведи найдорожча_назва й найдорожча_ціна до циклу й оновлюй їх усередині.

Окремо подумай, чим ініціалізувати найдорожча_ціна. Нуль — погана ідея (а якщо всі ціни відʼємні?). Варіант із None і перевіркою на першому кроці — хороша.

Зроблено, якщо: виконуються

assert найдорожча_назва == max(ціни, key=ціни.get)
assert найдорожча_ціна == max(ціни.values())

а на порожньому словнику твій код не падає з помилкою, а друкує «кошик порожній».

2.2 · Скільки разів трапляється кожна літера

Візьми речення щонайменше на 60 символів. Одним циклом по символах побудуй словник «літера → кількість», використовуючи ідіому лічильник[с] = лічильник.get(с, 0) + 1. Пробіли не рахуй.

Потім другим циклом знайди три найчастіші літери — без sorted і без Counter, просто тричі пройшовши по словнику й щоразу забираючи максимум.

Зроблено, якщо: виконується

from collections import Counter
assert лічильник == dict(Counter(текст.replace(" ", "")))
assert трійка == [пара[0] for пара in Counter(текст.replace(" ", "")).most_common(3)]

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

2.3 · while, який точно завершиться

Гра «вгадай число» без введення з клавіатури: задумане число зашите в коді, а «гравець» щоразу називає середину поточного проміжку (це двійковий пошук). Цикл крутиться, поки не вгадав.

задумане = 73
низ, верх = 1, 100
спроб = 0

Зроблено, якщо: цикл завершується, друкує кожну спробу й наприкінці виконуються

assert спроба == задумане
assert спроб <= 7, "двійковий пошук на 100 числах не потребує більше 7 спроб"

і ти письмово відповів на два питання: (1) який саме рядок у тілі наближає умову до хибності; (2) що станеться, якщо забути оновлювати низ і верх — і чому це та сама помилка, що й нульовий крок в інтерактиві 6.

2.4 · Таблиця, яку читає людина

Побудуй вкладеними циклами таблицю множення 9×9 із шапкою: перший рядок і перший стовпець — це самі множники, відділені рискою.

Зроблено, якщо: таблиця вирівняна по колонках (підказка: f"{x:>4}"), а збережений паралельно двовимірний список проходить

assert таблиця[0][0] == 1 and таблиця[8][8] == 81
assert таблиця[5][2] == 6 * 3
assert len(таблиця) == 9 and len(таблиця[3]) == 9

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

3.1 · Прибрати дорогі позиції на місці

Напиши цикл, який видаляє зі списку всі числа, більші за задану межу, змінюючи оригінальний список — тобто не створюючи нового й не роблячи присвоєння список = щось_інше. Ім'я має вказувати на той самий обʼєкт до й після.

Головна складність — там, де «дорогі» стоять підряд: саме на цьому ламається наївний обхід.

Зроблено, якщо: виконуються всі перевірки

дані = [10, 200, 300, 20]
той_самий = дані                 # другий ярлик на той самий обʼєкт
# ... твій код ...
assert дані == [10, 20]
assert той_самий == [10, 20], "ти створив новий список замість того, щоб змінити наявний"
assert id(дані) == id(той_самий)

і ще на трьох наборах: [500, 600, 700][], [1, 2, 3][1, 2, 3], [][]. Плюс письмове пояснення (2–3 речення), чому наївний варіант for x in дані: if x > межа: дані.remove(x) дає для [10, 200, 300, 20] результат [10, 300, 20].

3.2 · Свій enumerate і свій zip

Не використовуючи вбудовані enumerate і zip, збери такі самі результати звичайними циклами:

Функцій ми ще не вчили, тому просто напиши два блоки коду, які кладуть результат у змінні мій_enumerate і мій_zip.

Зроблено, якщо: виконуються

покупки = ["хліб", "молоко", "яблука", "мед", "сіль"]
кількості = [2, 1, 3, 1]

assert мій_enumerate == list(enumerate(покупки, start=1))
assert мій_zip == list(zip(покупки, кількості))
assert len(мій_zip) == 4, "обрізати треба по коротшому, а не по довшому"

і ти пояснив, звідки в твоєму мій_zip береться число 4 — яким саме виразом ти його отримав.

3.3 · Найдовша серія підряд

Дано список результатів матчів, наприклад ["П", "П", "Н", "П", "П", "П", "Л", "П"] («П» — перемога). Одним проходом знайди найдовшу серію перемог підряд та її початковий індекс. Одним — означає, що список можна обійти рівно один раз, без вкладених циклів.

Зроблено, якщо: виконуються

матчі = ["П", "П", "Н", "П", "П", "П", "Л", "П"]
assert найдовша_серія == 3
assert початок_серії == 3

а також на крайніх випадках: порожній список → серія 0; список суцільних «П» довжини 5 → серія 5 з початком 0; список без жодної «П» → серія 0. І поясни письмово, навіщо тут дві пари змінних — поточна серія й найкраща з побачених.


Підказки