EduBrick
классная работа

Список: ввод и первые действия

Программирование на Python: от нуля до олимпиад

0/18решено 0 из 18до зачёта осталось 10

Чему научитесь

  • Читать строку чисел в список одним выражением
  • Обращаться к элементам по номеру и пользоваться срезами
  • Считать сумму, минимум и максимум готовыми функциями
  • Отличать копию списка от второго имени того же списка

Как устроено занятие

Сначала разбор с примерами и короткими проверками понимания — они нужны, чтобы поймать непонятое сразу, а не через три темы.

Дальше 15 задач лестницей: разминка, основа, со звёздочкой. Занятие засчитывается, когда решено 10 — остальные не пропадают и учитываются отдельно.

После занятия — вторая часть, ещё 15 задач на те же приёмы в новых сюжетах.

Сколько это займёт

Примерно час-полтора вместе с задачами. Сроков нет: можно закрыть вкладку и вернуться когда удобно — прогресс сохранится.

теория

Когда числа нужны все сразу

В занятии про накопление был важный приём: читать числа по одному и сразу обрабатывать. Он экономит память и решает половину задач.

Но не все. Попробуйте так вывести числа в обратном порядке — не выйдет: чтобы напечатать последнее первым, надо дождаться конца, а к тому времени начало уже забыто.

Для таких задач нужен список — пронумерованный набор значений, который живёт целиком.

a = [3, 8, 1, 9]

print(len(a))      # 4 — сколько элементов
print(a[0])        # 3 — первый
print(a[3])        # 9 — четвёртый
print(a[-1])       # 9 — последний

Всё это вы уже видели у строк: нумерация с нуля, квадратные скобки, отрицательные номера, len. Список устроен так же — разница в том, что элементы не символы, а любые значения, и список можно менять.

a[0] = 100         # у строки так было нельзя, у списка можно
теория

Как прочитать список

Числа в олимпиадных задачах обычно идут одной строкой через пробел. Читается такая строка одним выражением:

a = list(map(int, input().split()))

Выражение выглядит громоздко, но разбирается по шагам справа налево:

шаг что получается
input() строка "3 8 1 9"
.split() список строк ["3", "8", "1", "9"]
map(int, …) те же значения, но числами
list(…) список чисел [3, 8, 1, 9]

map применяет функцию к каждому элементу — здесь превращает каждую строку в число. Без list результат тоже работает в цикле, но списком ещё не является, поэтому его оборачивают.

Если в первой строке дано количество, его читают отдельно:

n = int(input())
a = list(map(int, input().split()))

Заметьте, что n после этого почти не нужен: длину списка всегда можно спросить у него самого через len(a). Полагаться лучше на len — если в тесте окажется лишний пробел или число, расхождение выявится сразу.

тест

Проверка: что читает эта строка

a = input().split()
print(a[0] + a[1])

На вход подали 3 8. Что выведет программа?

Войдите, чтобы ответить.
теория

Готовые действия со списком

Сумму, минимум и максимум вы писали циклом в занятии 8. Для списка всё это уже есть:

print(sum(a))      # сумма
print(min(a))      # наименьший
print(max(a))      # наибольший
print(len(a))      # количество

Пользуйтесь ими: короче, быстрее и без риска ошибиться в начальном значении накопителя.

Срезы работают так же, как у строк:

print(a[:3])       # первые три элемента
print(a[-2:])      # последние два
print(a[::-1])     # список в обратном порядке
print(sum(a[:k]))  # сумма первых k

Как вывести список

Печать print(a) выдаст [3, 8, 1, 9] — со скобками и запятыми. Проверяющей системе нужны числа через пробел, поэтому пишут иначе:

print(*a)                        # 3 8 1 9
print(" ".join(map(str, a)))     # то же самое

Звёздочка перед списком означает «передай элементы по отдельности». Это самый короткий способ, и в задачах используется именно он.

теория

Ссылка против копии

А теперь то, чего не было у строк и что удивляет всех.

a = [1, 2, 3]
b = a
b[0] = 100

print(a)      # [100, 2, 3] — изменился и a!

Присваивание b = a не создаёт второй список. Оно даёт второе имя тому же самому списку. Меняя b, вы меняете a — это один объект, у которого две этикетки.

Со строками такого не бывало просто потому, что строку вообще нельзя изменить: любое действие создавало новую. Список изменяемый, и разница становится видна.

Как сделать настоящую копию

b = a[:]          # срез создаёт новый список
b = list(a)       # то же самое

Теперь b — отдельный список, и его изменения на a не влияют.

запись что делает
b = a второе имя того же списка
b = a[:] новый список с теми же значениями
b = a[::-1] новый список, перевёрнутый
a.reverse() переворачивает сам a, ничего не возвращая

Последняя строка — частая ловушка: b = a.reverse() положит в b пустоту, а a окажется перевёрнутым. Если исходный порядок ещё нужен, берите срез.

расчёт

Проверка: сколько элементов

Список a содержит 10 элементов. Сколько элементов в срезе a[2:7]?

Введите целое число.

Войдите, чтобы ответить.
теория

Три ошибки этого занятия

Забытый int. a = input().split() даёт список строк. Сложение таких «чисел» их склеит, а сравнение будет по алфавиту: "10" < "9" истинно. Признак: ответ выглядит бессмысленно, но программа не падает.

Выход за границу. Последний допустимый номер — len(a) - 1. Обращение к a[len(a)] роняет программу с list index out of range. В отличие от среза, обращение по номеру не прощает.

Список испорчен по дороге. Отсортировали, перевернули на месте или изменили элементы — а исходный порядок ещё был нужен. Признак: первая часть ответа верна, вторая нет.

Как проверять себя

  • список из одного элемента — первый и последний совпадают, соседних пар нет вовсе;
  • все элементы одинаковые — проверяет строгие сравнения;
  • отрицательные значения — ловят накопитель, начатый с нуля;
  • вывод после изменения — если список нужен дважды, не испортили ли его в первый раз.
теория

Практика: пятнадцать задач

Лестница прежняя: пять разминочных, семь основных, три со звёздочкой. Зачёт при десяти решённых.

Большая часть решается в две-три строки: прочитать, применить готовую функцию или срез, вывести через print(*a). Это нормально — смысл занятия в том, чтобы освоить чтение и индексацию, а не в сложных алгоритмах.

В нескольких задачах список нужно вывести дважды — до и после изменения. Там и проверяется, поняли ли вы разницу между ссылкой и копией.

лестница задач
развёрнутый ответ

Список, который изменился сам

Задача: «дан список чисел; выведите его в обратном порядке, а затем в исходном».

Ученик написал:

a = list(map(int, input().split()))
b = a
b.reverse()

print(*b)
print(*a)

На входе 1 2 3 программа выводит две одинаковые строки 3 2 1, хотя вторая должна быть 1 2 3. Объясните, почему так вышло, и приведите два способа исправить.

Войдите, чтобы ответить.