Рекурсия
Содержание
Контест №8
Ссылки на контесты
Порция синтаксиса Python
Функции
Функция - объект!
В исходном коде функция встречается несколько раз: во время объявления и во время вызова.
При объявлении функции мы пользуемся блоком def
.
Когда интерпретатор языка прочтёт этот блок, то он создаст объект функции:
>>> def square(x):
... return x**2
...
>>> square
<function square at 0x10a829290>
Этот объект <function square at HEX-ADDRESS>
, как и любой другой, имеет имя-ссылку square
и занимает место в памяти.
Вызов функции осуществляется путём дописывания пары круглых скобок вслед за именем функции. Внутри скобок через запятую перечисляют её аргументы.
>>> square(9)
81
>>> y = 71
>>> square(y)
5041
Поскольку функция это объект (first-class object), её можно присвоить в переменную или передавать в качестве аргумента для другой функции.
>>> g = square
>>> g is square
>>> True
>>> g(9)
>>> 81
Коварство перегруженности
Давайте добавим ещё одну функцию:
>>> def mul(multiplier, element):
... return multiplier * element
...
>>> mul(2, 8)
16
>>> mul(2, 10)
20
Она умножает первый аргумент на второй.
Перед вызовом функции её аргументы необходимо вычислить, Python это делает автоматически. Так, выражение (2*10)2 мы можем записать через наши функции:
>>> square(mul(2, 10))
400
Здесь для вызова square
необходимо получить значение первого аргумента, для этого нужно прежде получить это значение от функции mul
, и она вызывается первой.
Заметьте, что в функции mul
используется оператор умножения *
.
Он является перегруженным, т.е. его поведение зависит от аргументов:
>>> 2 * 12
24
>>> '2' * 12
'222222222222'
>>> 2 * [0, 1, 2]
[0, 1, 2, 0, 1, 2]
По этой причине у функции mul
ожидается аналогичное поведение:
>>> mul(2, 12)
24
>>> mul('2', 12)
'222222222222'
>>> mul(2, [0, 1, 2])
[0, 1, 2, 0, 1, 2]
Python не позволяет явно указать тип аргументов функции, например, невозможно разрешить вызовы mul
(без проверки вручную) только от двух целых чисел.
Однако, при объявлении функции мы можем пометить ожидаемые типы аргументов для тех, кто будет ею пользоваться:
>>> def mul(multiplier:int, element:int):
... return multiplier * element
...
>>> mul(12, 12)
144
>>> mul(12, '12')
'121212121212121212121212'
Значения аргументов по умолчанию
При объявлении функции бывает необходимо указать значения аргументов по умолчанию. Это встречается в ситуациях, когда мы хотим написать функции общего назначения, но с некоторым поведением по умолчанию, чтобы не утомлять пользователя. Например, у функции print
по умолчанию включён разделитель между аргументами (sep=' '
) и символ, записываемый в конец (end='\n'
).
Также аргументы по умолчанию бывают необходимы для корректной работы алгоритма, который функция реализует. Часто это встречается в рекурсивных алгоритмах.
Чтобы задать значение аргумента по умолчанию, необходимо в сигнатуре функции написать =
и значение напротив соответствующего аргумента:
>>> def greet(who, how='Hello'):
... s = how + ', ' + who + '!'
... print(s)
...
>>> greet('Ivan')
Hello, Ivan!
>>> greet('Ivan', 'Privet')
Privet, Ivan!
Развёртывание итерируемых объектов
Рассмотрим функцию скалярного произведения на плоскости:
>>> def dot_product(x1, y1, x2, y2):
... return x1*x2 + y1*y2
Допустим, в своём коде программист хранит координаты точки в структуре данных, например, в массиве размера 2:
>>> point_a = [1, 2]
>>> point_b = [3, 4]
и хочет получить их скалярное произведение. Два вызова ниже эквивалентны:
>>> dot_product(point_a[0], point_a[1], point_b[0], point_b[1])
11
>>> dot_product(*point_a, *point_b)
11
Во втором из них используется операция развёртывания, которая разворачивает значения массива и рассовывает их по аргументам функции.
Также сравните:
>>> print([1, 2, 'cs-mipt-ru', 4])
[1, 2, 'cs-mipt-ru', 4]
>>> print(*[1, 2, 'cs-mipt-ru', 4])
1 2 cs-mipt-ru 4
map
Функция map(func, iterable)
применяет функцию func
к итерируемому объекту iterable
и возвращает map object
- также итерируемый объект, на основе которого мы можем создать свой, если требуется.
Считывание массива чисел:
>>> s = input()
1 2 3 4 5
>>> s
'1 2 3 4 5'
>>> str_arr = s.split()
>>> str_arr
['1', '2', '3', '4', '5']
>>> int_arr = list(map(int, str_arr))
>>> int_arr
[1, 2, 3, 4, 5]
Можно сделать тоже самое в одну строчку, поскольку функции можно вкладывать друг друга:
>>> int_arr = list(map(int, input().split()))
6 7 8 9 10
>>> int_arr
[6, 7, 8, 9, 10]
Последовательность такая: считать строку input
, разбить её по пробелам и сформировать массив строк str.split()
, применить функцию преобразования типа int
к массиву строк map(int, list_of_str)
, сформировать список list(map object)
:
list(map(int, input().split()))
list(map(int, '6 7 8 9 10'.split()))
list(map(int, ['6', '7', '8', '9', '10']))
list(map object)
[6, 7, 8, 9, 10]
Функции максимального и минимального
Python предоставляет функции поиска максимального и минимального. Есть две возможности вызова
- если аргумент один, то он считается итерируемым объектом и поиск осуществляется в нём (например, для списка)
- если аргументов более одного, то поиск осуществляется между ними
>>> l = [-1, 2, 5, 10]
>>> max(l)
10
>>> min(l)
-1
>>> a = 10
>>> b = -2
>>> c = 0
>>> min(a, b, c)
-2
>>> max(a, b, c)
10
Срезы
Срез это операция, формирующая новый массив, на основе элементов существующего.
>>> l = list(range(10))
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[5:]
[5, 6, 7, 8, 9]
>>> l[5:7]
[5, 6]
>>> l[:7]
[0, 1, 2, 3, 4, 5, 6]
>>> l[::2]
[0, 2, 4, 6, 8]
>>> l[2:9:3]
[2, 5, 8]
Работа последнего среза аналогична следующему коду:
>>> slice = []
>>> for i in range(2, 9, 3):
... slice.append(l[i])
...
>>> slice
[2, 5, 8]
>>> l[2:9:3]
[2, 5, 8]