Чему научитесь
- Обращаться к символу по номеру и знать, где кончается строка
- Перебирать символы двумя способами и выбирать подходящий
- Считать символы по условию: буквы, цифры, пробелы
- Сравнивать символы между собой и собирать новую строку
Как устроено занятие
Сначала разбор с примерами и короткими проверками понимания — они нужны, чтобы поймать непонятое сразу, а не через три темы.
Дальше 15 задач лестницей: разминка, основа, со звёздочкой. Занятие засчитывается, когда решено 10 — остальные не пропадают и учитываются отдельно.
После занятия — вторая часть, ещё 15 задач на те же приёмы в новых сюжетах.
Сколько это займёт
Примерно час-полтора вместе с задачами. Сроков нет: можно закрыть вкладку и вернуться когда удобно — прогресс сохранится.
Данные, которые не число
Двенадцать занятий подряд на входе были числа. И всё это время input() возвращал вовсе не число, а строку — просто мы сразу писали int(input()) и превращали её в число.
Теперь превращать не будем:
s = input() # строка, как есть
print(len(s)) # сколько в ней символов
Строка — это последовательность символов, у каждого из которых есть номер. Нумерация начинается с нуля, как и у range:
| строка | h |
e |
l |
l |
o |
|---|---|---|---|---|---|
| номер | 0 | 1 | 2 | 3 | 4 |
Символ по номеру берут квадратными скобками: s[0] — первый, s[4] — последний. Длина здесь 5, а номер последнего — 4: последний символ всегда s[len(s) - 1].
Это единственное новое понятие занятия. Всё остальное — циклы, условия, накопители — вы уже умеете, и они работают со строкой ровно так же, как работали с числами.
Два способа перебрать строку
Перебирать символы можно двумя способами, и выбор между ними — не дело вкуса.
for ch in s: # даёт сами символы
if ch == "a":
count += 1
for i in range(len(s)): # даёт номера
if s[i] == "a":
count += 1
Первый короче и читается лучше. Но в нём нет номера — а он нужен, как только задача про положение или про соседей.
| что нужно | какой цикл |
|---|---|
| посчитать символы по условию | for ch in s |
| найти позицию символа | for i in range(len(s)) |
| сравнить соседние символы | for i in range(1, len(s)) и s[i] с s[i - 1] |
| пройти с конца | for i in range(len(s) - 1, -1, -1) |
Третья строка таблицы — знакомый приём: так же вы сравнивали соседние числа в потоке. Цикл начинается с единицы, потому что у самого первого символа предыдущего нет.
Проверка: номера и длина
Строка s равна "python". Какое выражение даст последний символ?
Отрицательные номера
Писать s[len(s) - 1] каждый раз, когда нужен последний символ, утомительно. Поэтому в Python есть сокращение:
s = "hello"
print(s[-1]) # o — последний
print(s[-2]) # l — предпоследний
Отрицательный номер отсчитывается с конца: -1 — последний символ, -2 — предыдущий, и так далее. Полностью равнозначно s[len(s) - 1] и s[len(s) - 2].
Это удобство, а не новая возможность: всё то же самое можно писать через длину. Но в задачах про начало и конец строки отрицательные номера заметно короче.
Границы существуют в обе стороны. У строки длиной 5 допустимы номера от -5 до 4. Обращение к s[5] или s[-6] — ошибка, программа остановится с сообщением string index out of range. Это самая частая поломка занятия, и почти всегда причина одна: цикл дошёл до len(s) включительно.
Символы сравниваются
Символы можно сравнивать между собой — <, >, == работают и с ними:
print("a" < "b") # True: в алфавите a раньше
Отсюда главный приём занятия. Латинские буквы идут подряд, цифры тоже, поэтому проверка «символ — строчная буква» пишется одним условием:
if "a" <= ch <= "z":
...
Так же проверяются цифры ("0" <= ch <= "9") и заглавные буквы ("A" <= ch <= "Z"). Двойное сравнение вы уже видели в занятии про условия — здесь оно работает точно так же.
Символ-цифра — не число
"7" и 7 — разные вещи. Первое — символ, второе — число, и складывать их нельзя:
total = 0
for ch in s:
if "0" <= ch <= "9":
total += int(ch) # int превращает символ в число
Без int программа либо упадёт, либо — что хуже — начнёт склеивать строки вместо сложения чисел.
Проверка: сколько сравнений
В строке из 10 символов нужно сравнить каждый символ с соседом слева.
Сколько получится сравнений? Введите целое число.
Три ошибки этого занятия
Выход за границу. for i in range(len(s) + 1) или обращение к s[i + 1] на последнем шаге. Признак: программа падает с string index out of range. Проверьте, до какого номера доходит цикл: последний допустимый — len(s) - 1.
Символ вместо числа. "5" + 1 — ошибка, "5" + "1" даёт "51", а не 6. Если в строке цифры и нужна арифметика, каждый символ пропускается через int.
Попытка изменить строку. s[0] = "x" не работает: строку в Python поменять нельзя, можно только собрать новую. Поэтому задачи вида «уберите символ» решаются накоплением: заводится пустая строка и к ней по одному прибавляются нужные символы.
result = ""
for ch in s:
if ch != "a":
result += ch
Как проверять себя
- строка из одного символа — ломает всё, где есть «соседи» или «первый и последний»;
- символа нет вовсе — задача про поиск должна что-то ответить и в этом случае;
- все символы одинаковые — проверяет строгие сравнения.
Практика: пятнадцать задач
Лестница прежняя: пять разминочных, семь основных, три со звёздочкой. Зачёт при десяти решённых.
Готовых средств для строк в Python много, и дальше мы их разберём. Но в этом занятии всё делается вручную: перебором, сравнением и накоплением. Так становится видно, что происходит внутри, — а метод, вызванный без понимания, потом подводит на первой же нестандартной задаче.
Строки в задачах — латинские буквы, цифры и одиночные пробелы. Русские буквы дальше тоже появятся, но проверка «между a и z» на них не работает, и до кодов символов мы ещё не дошли.
Программа, которая падает на каждой строке
Задача: «дана строка, посчитайте, сколько в ней пар соседних одинаковых символов».
Ученик написал:
s = input()
count = 0
for i in range(len(s)):
if s[i] == s[i + 1]:
count += 1
print(count)
Программа падает с сообщением string index out of range. Объясните, на каком шаге это происходит и почему, и предложите два разных исправления.