Рекурсия в Python
Конспект посвящён рекурсивным функциям в Python
Теория
Рекурсивная функция — функция, вызывающая саму себя.
Следующая функция является рекурсивной:
|
|
|
|
RecursionError
Во время каждого вызова функции message()
в оперативной памяти создается новый экземпляр переменной times
. При первом вызове функции times
имеет значение 3. Когда функция себя вызывает, создается новый экземпляр переменной times
и в него передается значение 2. Этот цикл повторяется до тех пор, пока в функцию в качестве аргумента не будет передан 0.
Количество раз, которые функция вызывает саму себя, называется глубиной рекурсии. Глубина функции message()
равняется 3.
В общем случае рекурсивная функция устроена следующим образом:
- Если в настоящий момент задача может быть решена без рекурсии, то функция ее решает (условие выхода).
- Если в настоящий момент задача не может быть решена, то функция ее сводит к уменьшенной и при этом аналогичной задаче и вызывает саму себя для решения этой уменьшенной задачи (рекурсивный случай).
Хвостовая рекурсия — частный случай рекурсии, при котором любой рекурсивный вызов является последней операцией перед возвратом из функции.
Вот, к примеру, хвостовая рекурсивная функция:
|
|
А вот не хвостовая:
|
|
Рекурсия также может быть сделана с помощью lambda
-функций:
|
|
Примеры рекурсий
Задача 1
Пусть требуется реализовать функцию draw_rect()
с использованием рекурсии, которая печатает звёздный прямоугольник размерами width * height
. Чтобы явно не передавать шаг в вызов функции, можно воспользоваться вложенной функцией и механизмом замыкания
|
|
Функция draw_rect()
определяет внутри себя рекурсивную функцию rec()
, которая имеет один параметр step
и имеет доступ к переменным width
и height
внешней функции. Затем функция draw_rect()
вызывает внутреннюю рекурсивную функцию, передавая в качестве step
значение 0.
Задача 2
Пусть требуется реализовать функцию factorial()
с использованием рекурсии, которая возвращает факториал неотрицательного числа n
:
|
|
Задача 3
Дан список, элементами которого могут быть только строки или аналогичные списки, содержащие строки и вложенные списки. С помощью рекурсивной функции get_all_str()
необходимо вывести все строки из данного списка и из всех вложенных, разделив пробелом:
|
|
|
|
Задача 4
Дан словарь произвольной вложенности, то есть значениями в словаре могут быть другие словари. С помощью рекурсивной функции find_key()
необходимо определить значение, которое соответствует заданному ключу, и вернуть его. При этом гарантируется, что такой ключ имеется в словаре, причем он единственный:
|
|
Задача 5
Пусть требуется реализовать функцию fib()
с использованием рекурсии, которая находит n
-ное число Фибоначчи:
|
|
При такой реализации происходит многократное вычисление одних и тех же значений.
Мемоизация
Кэширование — сохранение равнее полученных/вычисленных данных с целью ускорения доступа в будущем.
Мемоизация — частный случай кэширования, при котором результат выполнения функции сохраняется и используется в следующем вызове.
Перепишем функцию из задачи 3 с использованием мемоизации:
|
|
Стратегии кэширования
Стратегия | Удаляемая запись | Повторно используемые записи |
---|---|---|
First-In, First-Out (FIFO) | Самая старая | Новые |
Last-In, First-Out (LIFO) | Самая новая | Старые |
Least Recently Used (LRU) | Которая не использовалась дольше остальных | Недавно прочитанные |
Most Recently Used (MRU) | Которая использовалась последней | Прочитанные первыми |
Least Frequently Used (LFU) | Которая использовалась наиболее редко | Часто используемые |
Настройка глубины рекурсии
По умолчанию Python имеет ограничение на максимальную глубину рекурсивных вызовов, которое не позволяет бесконечной рекурсии вызывать переполнение стека.
Получить значение по умолчанию (1000) для максимальной глубины рекурсии можно с помощью функции getrecursionlimit()
из модуля sys
.
Чтобы явно указать значений максимальной глубины рекурсии, нужно воспользоваться функцией setrecursionlimit()
из того же sys
.
|
|
|
|
Основной источник: https://stepik.org/course/82541
