№ 16. Функции, рекурсия

Автор статьи: Андрей Роженцов

Функции

Для решения задач с помощью кода необходимо уметь писать в Python функции.
Функция — это фрагмент программного кода, к которому можно обратиться из другого места программы. Каждая функция выполняет одну конкретную задачу.
Функция задаётся так:

def название_функции(параметр_1, параметр_2):
    тело функции

А вызывается так:

название_функции(аргумент_1, аргумент_2)

Называть функцию можно любым допустимым именем — по аналогии с именами переменных. Функция может принимать различные параметры или не принимать их вовсе.

Важное различие: параметры и аргументы.

  • Параметры — это переменные, указанные в скобках при определении функции, то есть то, что функция «ожидает» получить
  • Аргументы — это конкретные значения, которые передаются в функцию при её вызове, то есть то, что мы фактически «даём» функции

Преимущества использования функций:

  • Не нужно повторять один и тот же код\
    Если одинаковые действия выполняются много раз, можно вынести их в функцию и вызывать при необходимости.
  • Исправления делаются один раз\
    Если найдена ошибка в функции, достаточно исправить её в одном месте — изменения автоматически применятся во всех вызовах.
  • Большую программу легче собирать из частей\
    Программу можно разбить на небольшие функции, написать и проверить их по отдельности, а затем объединить.
  • Функция скрывает детали реализации\
    Мы используем функцию как чёрный ящик: понимаем, что она делает, не удерживая в голове весь алгоритм, потому что при необходимости детали всегда можно посмотреть внутри.

Рассмотрим пример функции без параметров, которая при вызове будет печатать «Hello!»

def say_hello():#создание функции
    print('Hello!')

При запуске программы ничего не произойдёт, потому что функцию необходимо вызвать. При компиляции Python «узнаёт», что есть функция с названием say_hello(), и идёт дальше. Только при вызове мы возвращаемся в ту часть кода, где функция была определена, и тогда выполняется её тело.

Функцию можно вызывать любое количество раз. Главное — вызывать её только после определения.

say_hello()#вызов функции\
say_hello()#вызов функции

Вывод:

Hello!
Hello!

Пример ошибки:

say_hello()
def say_hello():
    print('Hello!')
NameError: name 'say_hello' is not defined

Попытка вызвать функцию до определения.

Проведем параллель с математикой и создадим функцию, зависящую от переменной х.

$f(x) = x^2 + 3x + 4$\
$f(5) = 5^2 + 3 × 5 + 4 = 25 + 15 + 4 = 44$

def f(x):
    y = x**2 + 3*x + 4
    return y

print(f(5))

Вывод:

44

В примере функция f принимает один параметр — x. В месте вызова мы передаём также один аргумент — 5. Ключевое слово return завершает выполнение функции и возвращает значение, которое будет подставлено на место вызова функции.

Рассмотрим пример:

def f(x):
    y = x**2 + 3*x + 4
    return y
    print('конец функции')
print(f(5))

Вывод:

44

Фраза «конец функции» не выводится, т. к. функция всегда завершает выполнение на слове return.
У функции могут быть переменные, которые определены только в её пределах. Такие переменные называются локальными. При попытке обратиться к локальной переменной вне функции получим ошибку:

def f(x):
    y = x**2 + 3*x + 4
    return y
    print('конец функции')
print(f(5))
print(y)
44
Traceback (most recent call last):
  File, line 6, in <module>
    print(y)
NameError: name 'y' is not defined

Переменные, созданные вне функций, называются глобальными. Если внутри функции инициализируются переменные с тем же названием, они всё равно будут локальными. При действиях с ними глобальные переменные никак не изменятся.

Пример:

x = 10 #глобальная
def summator(x): # x — локальная, равная 10
    x = x + 10 # локальный x становится 20
    return x # возвращаем значение 20
print(summator(x))# вызываем summator(10)
print(x) # вывод глобальной, равной 10

Вывод:

20
10

После выполнения функции глобальная переменная х никак не изменилась и осталась равной 10!
Если нам необходимо, чтобы глобальная переменная изменялась внутри функции, используем ключевое слово global.
Пример кода:

x = 10 #глобальная
def summator():
    global x
    x = x + 10 # глобальный x становится 20
    return x # возвращаем значение 20
print(summator())# вызываем summator()
print(x) # вывод глобальной, равной 20

Вывод:

20
20

Глобальная переменная х внутри функции увеличилась на 10.

Рекурсия

Рекурсивная функция — это функция, в теле которой содержится вызов этой же функции. Другими словами, это обычная функция в Python, которая вызывает сама себя.

Пример:

def recursion():
    recursion()
recursion()

Рекурсия не может работать бесконечно, у неё есть максимальная глубина. Глубина рекурсии — количество вложенных вызовов функции. Можно сравнить этот процесс с матрёшкой: её можно открывать определённое количество раз, пока не дойдём до самой последней куклы.

По умолчанию максимальная глубина рекурсии равна 1000, но на слабом компьютере может быть и меньше. Чтобы рекурсия не была бесконечной, необходимо добавить точку выхода.
Рассмотрим функцию:

def F(n):
    print('Вызвали функцию от ',n)
    if n>1:
        F(n-1)
    else:
        return
    print('Закончилась функция от ',n)
F(3)

Вывод:

Вызвали функцию от  3
Вызвали функцию от  2
Вызвали функцию от  1
Закончилась функция от  2
Закончилась функция от  3

Рассмотрим порядок выполнения команд при вызове F(3):

  1. Локальная переменная n = 3, выводится сообщение: «Вызвали функцию от 3», условие n > 1 выполняется, происходит вызов F(2). Важно: F(3) ещё не закончила свою работу! Поэтому, когда закончится F (2), выполнение F(3) продолжится с той строчки, на которой прервалось из-за вызова F(2)
  2. Вызов F(2). Локальная переменная n = 2, выводится сообщение: «Вызвали функцию от 2», условие n > 1 выполняется, происходит вызов F(1)
  3. Вызов F(1). Локальная переменная n = 1, выводится сообщение: «Вызвали функцию от 1». Условие n > 1 не выполняется, поэтому выходим из функции из-за ключевого слова return
  4. Возвращаемся к шагу 2 и продолжаем с того места, где остановились. F(1) успешно вызвана, выводим сообщение: «Закончилась функция от 2»
  5. Возвращаемся к шагу 1. F(2) успешно вызвана. Остаётся вывести сообщение: «Закончилась функция от 3»

Рассмотрим ещё один пример:

def F(n):
    if n>1:
        return F(n-1)+n
    else:
        return 0
print(F(3))

Вывод:

5

Логика работы:

  1. Вызов F(3)\
    Локальная переменная n = 3, условие n > 1 выполняется, и функция должна вернуть значение F(2) + 3. Но она «не знает», чему равно F(2).
  2. Вызов F(2)\
    Локальная переменная n = 2, условие n > 1 выполняется, и функция должна вернуть значение F(1) + 2. Но она «не знает», чему равно F(1).
  3. Вызов F(1)\
    Локальная переменная n = 1, условие n > 1 не выполняется, и функция возвращает 0, то есть F(1) = 0.
  4. Возвращаемся к шагу 2. Теперь мы знаем, что F(2) = F(1) + 2 = 0 + 2 = 2
  5. Возвращаемся к шагу 1. F(3) = F(2) + 3 = 2 + 3 = 5. Вызов print(F(3)) выведет на экран 5

Задача 1: простое условие

Ссылка

# Перепишем функцию F из условия
def F(n):
    if n>0 and n%2==0:
        return n
    elif n>0 and n%2!=0:
        return F(n-1)
# Вызовем её
print(F(11))

Выглядит просто! Закрепим основы ещё на одном примере.

Задача 2: вызов функции от нескольких аргументов

Ссылка

# Перепишем функцию F из условия
def F(n):
    if n<=1:
        return 1
    elif n>1 and n%3==0:
        return F(n-1)+n//3
    else:
        return F(n-1)+F(n-2)
# Вызовем функцию несколько раз и сразу посчитаем значение выражения
print(F(54)-F(52)-F(50))

Вывод:

35

При n, кратных 3, функция содержит слагаемое n / 3. Однако в Python оператор / возвращает число с плавающей точкой (float), даже если результат деления целый. Например, 9 / 3 = 3.0.

Чтобы сохранить целочисленный тип данных (поскольку n по условию — целое число) и избежать потенциальных ошибок при работе с float, используется целочисленное деление (//). Это корректно, так как для чисел, кратных 3, результаты действий n / 3 и n // 3 математически эквивалентны.

Ответ получился верным, но подсчёт значения занял больше минуты. Как этого избежать, рассмотрим в следующих статьях. А пока предлагаем закрепить пройденный материал на простых задачах.

Итог

Мы рассмотрели две темы в программировании: функции и рекурсию. Подробно разобрали, что такое функция — это именованный блок кода, предназначенный для выполнения конкретной задачи. Мы узнали, как правильно объявлять функции с помощью def, передавать им аргументы и возвращать результаты с помощью return.

Важно помнить, что переменные, созданные внутри функции, локальные и не влияют на глобальные.

Также мы познакомились с концепцией рекурсии — это когда функция вызывает саму себя. Ключевое правило рекурсии — условие выхода, которое предотвращает бесконечное выполнение. Знание рекурсивных функций понадобится для решения 16-го задания ЕГЭ по информатике.

Подборка задач

Источник: Яндекс Учебник — № 16. Функции, рекурсия. Каталог разборов: education.yandex.ru.

Назад к статьям Поделиться