Я знаю, что такое рекурсия (когда паттерн повторяется внутри себя, обычно это функция, которая вызывает себя на одной из своих строк после условного прорыва ... правильно?), И я могу понять рекурсивные функции, если внимательно изучу их. Моя проблема в том, что, когда я вижу новые примеры, я всегда изначально растерялся. Если я вижу цикл или отображение, архивирование, вложение, полиморфный вызов и т. Д., Я знаю, что происходит, просто посмотрев на него. Когда я вижу рекурсивный код, мой мыслительный процесс обычно звучит так: сопровождаемый 'о, это рекурсивно' сопровождаемое 'я предполагаю, что это должно работать, если они говорят, что это делает.'
Так есть ли у вас какие-либо советы / планы / ресурсы для развития навыков в этой области? Рекурсия - довольно странная концепция, поэтому я думаю, что способ ее решения может быть столь же странным и неочевидным.
Ответы:
Начните с чего-то простого и обведите его карандашом и бумагой. Seriosuly. Хорошее место для начала - алгоритмы обхода дерева, так как они гораздо легче обрабатываются с помощью рекурсии, чем регулярной итерации. Это не должен быть сложный пример, но что-то простое и с которым можно работать.
Да, это странно и иногда нелогично, но как только он щелкает, когда вы говорите "Эврика!" Вы удивитесь, как не поняли этого раньше! ;) Я предложил деревья, потому что они (IMO) - самая простая структура для понимания в рекурсии, и с ними легко работать, используя карандаш и бумагу. ;)
источник
Я настоятельно рекомендую Схему, используя книгу «Маленький Лиспер». После того, как вы закончите с этим, вы будете понимать рекурсию, глубоко вниз. Почти гарантировано.
источник
Я определенно предлагаю SICP. Кроме того, вы должны проверить видео вводного курса авторов здесь ; они невероятно открывают разум.
Другой путь, не столь тесно связанный с программированием, - это чтение Гёделя, Эшера, Баха: Вечная золотая коса Хофштадтера. Как только вы это сделаете, рекурсия будет выглядеть так же естественно, как арифметика. Кроме того, вы будете убеждены, что P = nP, и вам захочется создавать мыслительные машины - но это побочный эффект, который настолько мал по сравнению с преимуществами.
источник
По сути, все сводится к практике ... Возьмите общие проблемы (сортировка, поиск, математические задачи и т. Д.) И посмотрите, сможете ли вы найти способ решения этих проблем, если несколько раз применить одну функцию.
Например, быстрая сортировка работает в том, что она перемещает элемент в списке на две половины, а затем снова применяет себя к каждой из этих половин. Когда происходит первоначальная сортировка, не нужно беспокоиться о сортировке двух половинок в этой точке. Скорее он берет элемент поворота и помещает все элементы, меньшие, чем этот элемент, с одной стороны, и все элементы, большие или равные с другой стороны. Имеет ли это смысл, как он может рекурсивно вызывать себя в этой точке для сортировки двух новых меньших списков? Они тоже списки. Просто меньше. Но они все еще должны быть отсортированы.
Сила рекурсии - это понятие «разделяй и властвуй». Разбейте проблему несколько раз на более мелкие проблемы, которые идентичны по своей природе, но только меньше. Если вы сделаете это достаточно, в конце концов вы достигнете точки, где единственная оставшаяся часть уже решена, тогда вы просто вернетесь из цикла, и проблема будет решена. Изучите те примеры, которые вы упомянули, пока не поймете их. Это может занять некоторое время, но в конце концов станет легче. Тогда попробуйте взять другие проблемы и создать рекурсивную функцию для их решения! Удачи!
РЕДАКТИРОВАТЬ: Я также должен добавить, что ключевым элементом рекурсии является гарантированная способность функции быть в состоянии остановить. Это означает, что разбивка исходной задачи должна постоянно уменьшаться, и в конечном итоге должна быть гарантированная точка остановки (точка, в которой новая подзадача либо решаема, либо уже решена).
источник
def factorial(number): """return factorial of number""" if number == 0: return 0 elif number == 1: return 1 else: return number * factorial(number - 1)
Лично я думаю, что ваш лучший выбор - через практику.
Я изучил рекурсию с логотипом. Вы могли бы использовать LISP. На этих языках рекурсия естественна. В противном случае вы можете сравнить его с изучением математических наборов и рядов, где вы выражаете то, что будет дальше, на основе того, что было раньше, то есть u (n + 1) = f (u (n)), или более сложных рядов, где у вас есть несколько переменных и множественные зависимости, например, u (n) = g (u (n-1), u (n-2), v (n), v (n-1)); v (n) = h (u (n-1), u (n-2), v (n), v (n-1)) ...
Поэтому я предлагаю вам найти простые (в их выражении) стандартные «проблемы» рекурсии и попытаться реализовать их на выбранном вами языке. Практика поможет вам научиться думать, читать и выражать эти «проблемы». Обратите внимание, что часто некоторые из этих проблем могут быть выражены с помощью итерации, но рекурсия может быть более элегантным способом их решения. Одним из них является расчет факторных чисел.
Графические "проблемы", которые я нахожу, облегчают видеть. Итак, посмотрите на хлопья Коха, Фибоначчи, кривую дракона и фракталы в целом. Но также посмотрите на алгоритм быстрой сортировки ...
Вам нужно завершить работу нескольких программ (бесконечные циклы, пробное использование бесконечных ресурсов) и неправильно обработать конечные условия (чтобы получить неожиданные результаты), прежде чем вы все обдумаете. И даже когда вы получите это, вы все равно будете делать те ошибки, просто реже.
источник
Структура и интерпретация компьютерных программ
Это книга используется для обучения, а не только рекурсии, но программирования в целом в различных университетах. Это одна из основополагающих книг, которая дает больше информации, чем больше вы приобретаете опыта и чем больше вы его читаете.
источник
Как бы мне ни нравились SICP и Гёдель, Эшер, Бах: Вечная золотая коса, LISP Турецкого : Нежное введение в символические вычисления также хорошо справляется с внедрением рекурсии.
Основная концепция такова: во-первых, вы должны знать, когда ваша рекурсивная функция завершена, чтобы она могла вернуть результат. Затем вы должны знать, как взять незаконченное дело и свести его к тому, что вы можете повторить. Для традиционного факториального примера (N) вы закончите, когда N <= 1, и незавершенный случай - N * факториал (N-1).
Для более уродливого примера есть функция Акерманна A (m, n).
источник
Я предлагаю поиграть с некоторыми функциональными языками в стиле ML, такими как OCaml или Haskell. Я обнаружил, что синтаксис сопоставления с образцом действительно помог мне понять даже относительно сложные рекурсивные функции, безусловно, намного лучше, чем схемы
if
иcond
операторы. (Я выучил Хаскель и Схему одновременно.)Вот тривиальный пример для контраста:
и с сопоставлением с образцом:
Этот пример на самом деле не учитывает разницу - у меня никогда не было проблем с любой версией функции. Это просто для иллюстрации того, как выглядят эти два варианта. Как только вы доберетесь до гораздо более сложных функций, используя такие вещи, как списки и деревья, разница станет гораздо более заметной.
Я особенно рекомендую Haskell, потому что это простой язык с очень хорошим синтаксисом. Это также значительно облегчает работу с более продвинутыми идеями, такими как corecursion :
(Вы не поймете приведенный выше код, пока не поиграете немного с Haskell, но будьте уверены, что он в основном магический: P.) Конечно, вы можете сделать то же самое с потоками в Scheme, но в Haskell это гораздо естественнее.
источник
Его нет в наличии, но если вы можете его найти, «Рекурсивные алгоритмы» Ричарда Лоренца - это не что иное, как рекурсия. Он охватывает основы рекурсии, а также конкретные рекурсивные алгоритмы.
Примеры приведены на языке Паскаль, но они не настолько велики, чтобы выбор языка был утомительным.
источник