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

Рядки

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

З бібліотек не потрібно нічого: усе робиться вбудованими можливостями рядка. Єдиний виняток — unicodedata у завданні 3.2, і то лише для перевірки.


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

1.1 · Розбір «брудного» запису

Дано рядок, у якому поля розділені крапкою з комою, а навколо кожного — випадкові пробіли:

сирий = "   Львів ;  чай зелений ; 1250.00 ;  2   "

Розбери його на чотири значення: місто (рядок), товар (рядок), ціна (float) і кількість (int). Кожне поле мусить бути очищене від пробілів з обох боків. Надрукуй усі чотири по одному в рядок разом із типом.

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

assert місто == "Львів"
assert товар == "чай зелений"          # пробіл ВСЕРЕДИНІ має лишитися
assert ціна == 1250.0 and isinstance(ціна, float)
assert кількість == 2 and isinstance(кількість, int)

Пильнуй за третім рядком: .strip() знімає пробіли лише з країв. Якщо в тебе вийшло "чайзелений" — ти десь застосував .replace(" ", ""), а це вже інша операція.

1.2 · Символи проти байтів

Для тих самих чотирьох полів надрукуй таблицю: поле, len() у символах, len(поле.encode()) у байтах і різницю між ними.

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

assert len("Львів") == 5
assert len("Львів".encode()) == 10
assert len("1250.00") == len("1250.00".encode())   # чиста ASCII — байт у байт

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

2.1 · Рядок чека фіксованої ширини

Склади однорядковий чек рівно 46 символів завширшки:

Для ("чай зелений", 2, 1250.00) має вийти:

чай зелений...........................2,500.00

Зроби це однією f-специфікацією для проміжку — тобто спершу порахуй, скільки символів лишилось під крапки, а потім скористайся заповнювачем у міні-мові формату.

Зроблено, якщо:

assert len(чек) == 46
assert чек.startswith("чай зелений")
assert чек.endswith("2,500.00")
assert чек.count(".") == 27 + 1        # 27 крапок-заповнювачів плюс десяткова

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

2.2 · Скільки коштує неохайність

Візьми той самий сирий рядок із завдання 1.1 і не роби .strip(). Розбий його по ";" і надрукуй порівняння: для кожного поля покажи repr(поле), поле == очікуване і поле.strip() == очікуване.

Зроблено, якщо: у виводі чітко видно, що без .strip() жодне з чотирьох порівнянь не спрацювало, і виконується

assert поля_без_strip[0] != "Львів"
assert поля_без_strip[0].strip() == "Львів"

І окремо: поясни двома реченнями, чому помилка такого роду найдорожча — дані виглядають правильними на екрані, а порівняння дає False.


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

3.1 · Розвертання слів без циклів і без reversed

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

розвернути_слова("кава мелена холодна")   # -> "холодна мелена кава"

Дозволено лише: .split(), зрізи (у тому числі [::-1]), .join(). Заборонено: for, while, reversed, list.reverse, sorted.

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

assert розвернути_слова("кава мелена холодна") == "холодна мелена кава"
assert розвернути_слова("одне") == "одне"
assert розвернути_слова("  зайві   пробіли  ") == "пробіли зайві"
assert розвернути_слова("") == ""

Третя перевірка не випадкова: вона проходить лише тоді, коли ти взяв .split() без аргументу. З аргументом " " там з'являться порожні рядки.

3.2 · Свій casefold для української

Порівняння рядків без урахування регістру виглядає просто, поки не з'явиться ß. Досліди межу застосовності на конкретних прикладах.

Напиши функцію однакові_без_регістру(a, b), яка повертає True, якщо рядки збігаються з точністю до регістру. Перевір її на п'яти парах і склади таблицю: пара, результат через .lower(), результат через .casefold(), і чи вони розійшлись.

Обов'язкові пари:

("Київ", "КИЇВ")             # тут усе нудно й правильно
("Straße", "STRASSE")        # німецька ß
("file", "file")              # типографська лігатура fi — один символ!
("Σ", "ς")                   # грецька сигма: велика одна, малих дві
("İstanbul", "istanbul")     # турецька І з крапкою

Зроблено, якщо: у таблиці видно рівно три пари, де .lower() і .casefold() дають різні відповіді, і ти назвав ту єдину пару, де обидва способи однаково помиляються. Поясни двома-трьома реченнями, чому «зробити маленькими й порівняти» — це не універсальне правило.

Для перевірки, що саме сталося з символом, стане в пригоді:

import unicodedata
print(unicodedata.name("ß"), "->", [unicodedata.name(c) for c in "ß".upper()])

3.3 · Скільки насправді коштує +=

Не міряй час — він залежить від машини. Порахуй роботу: напиши функцію скопійовано_символів(n, L), яка повертає, скільки символів скопіює результат += шматок на n кроках при довжині шматка L.

Зроби це двома способами: покроковим підрахунком і замкненою формулою.

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

assert скопійовано_формулою(1, 6) == 6
assert скопійовано_формулою(60, 6) == 10980
assert скопійовано_формулою(1000, 6) == 3_003_000
assert скопійовано_кроками(60, 6) == скопійовано_формулою(60, 6)

Додатково: порахуй, у скільки разів це більше за join при n = 10_000, і напиши одним реченням, що саме росте — кількість об'єктів чи обсяг копіювання.


Підказки