Чтобы объяснить рекурсию , я использую комбинацию разных объяснений, обычно для того, чтобы попытаться:
- объяснить концепцию,
- объясните, почему это важно,
- объясните как это получить.
Для начала, Wolfram | Alpha определяет его в более простых терминах, чем Википедия :
Выражение такое, что каждый член генерируется путем повторения определенной математической операции.
математика
Если ваш ученик (или человек, которого вы тоже объясняете, отныне я буду говорить ученик) имеет хоть какое-то математическое образование, он, очевидно, уже сталкивался с рекурсией, изучая серии и свои представления о рекурсивности и их рекуррентных отношениях .
Очень хороший способ начать - это продемонстрировать серию и сказать, что речь идет просто о рекурсии:
- математическая функция ...
- ... который вызывает себя для вычисления значения, соответствующего n-му элементу ...
- ... и который определяет некоторые границы.
Обычно, вы либо получаете «да, ну, что угодно» в лучшем случае, потому что они все еще не используют его, или, скорее всего, просто очень глубокий храп.
Примеры кодирования
В остальном, это на самом деле подробная версия того, что я представил в Дополнении к моему ответу на вопрос, на который вы указали относительно указателей (плохой каламбур).
На этом этапе мои ученики обычно знают, как распечатать что-то на экране. Предполагая, что мы используем C, они знают, как напечатать один символ, используя write
или printf
. Они также знают о контурах управления.
Я обычно прибегаю к нескольким повторяющимся и простым проблемам программирования, пока они не получат:
Факториал
Факториал - это очень простая математическая концепция для понимания, и реализация очень близка к ее математическому представлению. Однако, они могут не получить это сначала.
алфавиты
Алфавитная версия интересна для того, чтобы научить их думать о порядке их рекурсивных утверждений. Как и с указателями, они будут просто случайным образом бросать строки в вас. Дело в том , чтобы привести их к осознанию того, что цикл может быть перевернутой либо изменения условий или , просто перевернув порядок отчетности в вашей функции. Вот где печать алфавита помогает, потому что это что-то визуальное для них. Просто попросите их написать функцию, которая будет печатать один символ для каждого вызова, и рекурсивно вызывать себя, чтобы написать следующий (или предыдущий).
Поклонники FP, пропустите тот факт, что печать материала в выходной поток пока является побочным эффектом ... Давайте не будем слишком раздражать на фронте FP. (Но если вы используете язык с поддержкой списков, не стесняйтесь объединять список на каждой итерации и просто печатать конечный результат. Но обычно я начинаю их с C, который, к сожалению, не самый лучший для такого рода проблем и концепций) ,
Возведение
Проблема возведения в степень немного сложнее ( на данном этапе обучения). Очевидно, что концепция точно такая же, как и для факториала, и здесь нет дополнительной сложности ... за исключением того, что у вас есть несколько параметров. И этого обычно достаточно, чтобы сбить с толку людей и отбросить их с самого начала.
Его простая форма:
можно выразить так повторением:
Сильнее
После того, как эти простые проблемы были показаны И повторно реализованы в руководствах, вы можете выполнить несколько более сложные (но очень классические) упражнения:
- В Фибоначчи числа,
- Наибольший общий делитель ,
- Проблема 8-Куинс ,
- Игра Башни Ханоя ,
- И если у вас есть графическое окружение (или вы можете предоставить заглушки для кода или для вывода терминала, или они уже могут управлять этим), такие вещи, как:
- А для практических примеров рассмотрим написание:
- алгоритм обхода дерева,
- простой математический синтаксический анализатор выражений,
- тральщик
Примечание: Опять же, некоторые из них на самом деле не сложнее ... Они просто подходят к проблеме под точно таким же или немного другим углом. Но практика делает идеальным.
Помощники
Ссылка
Некоторое чтение никогда не повредит. Ну, это поначалу, и они будут чувствовать себя еще более потерянными. Это такая вещь, которая растет на тебе и сидит у тебя в голове, пока однажды ты не осознаешь, что наконец-то это понял. А потом вы вспоминаете эти вещи, которые вы прочитали. Рекурсии , рекурсии в области компьютерных наук и рекуррентное соотношение страниц в Википедии будет делать сейчас.
Уровень / Глубина
Предполагая, что ваши студенты не имеют большого опыта программирования, предоставьте заглушки кода. После первых попыток дайте им функцию печати, которая может отображать уровень рекурсии. Печать числового значения уровня помогает.
Диаграмма "Стек как ящики"
Также полезен отступ для напечатанного результата (или вывода уровня), так как он дает другое визуальное представление о том, что делает ваша программа, открывая и закрывая контексты стека, такие как ящики, или папки в проводнике файловой системы.
Рекурсивные Сокращения
Если ваш студент уже немного разбирается в компьютерной культуре, он может уже использовать некоторые проекты / программы с именами, использующими рекурсивные аббревиатуры . Это стало традицией в течение некоторого времени, особенно в проектах GNU. Вот некоторые примеры:
Рекурсивный:
- GNU - "GNU - это не Unix"
- Nagios - "Nagios не собирается настаивать на святости"
- PHP - «PHP Hypertext Preprocessor» (и оригинальная «Персональная домашняя страница»)
- Wine - "Вино не эмулятор"
- Zile - "Zile Is Lossy Emacs"
Взаимно рекурсивный:
- HURD - «HIRD демонов, заменяющих Unix» (где HIRD - «HURD интерфейсов, представляющих глубину»)
Пусть они попробуют придумать свое.
Точно так же, есть много случаев рекурсивного юмора, например , рекурсивная коррекция поиска Google . Для получения дополнительной информации о рекурсии, прочитайте этот ответ .
Подводные камни и дальнейшее обучение
Некоторые вопросы, с которыми обычно борются люди, и на которые вам нужно знать ответы.
Почему, о Боже, почему ???
Почему ты бы так поступил? Хорошая, но неочевидная причина заключается в том, что часто проще так выразить проблему. Не очень хорошая, но очевидная причина в том, что часто требуется меньше печатать (хотя не заставляйте их чувствовать себя слишком просто за использование рекурсии ...).
Некоторые проблемы определенно легче решить, используя рекурсивный подход. Как правило, любая проблема, которую вы можете решить с помощью парадигмы « Разделяй и властвуй», будет соответствовать разветвленному алгоритму рекурсии.
Что снова N?
Почему мой n
или (независимо от имени вашей переменной) каждый раз разные? У новичков обычно возникают проблемы с пониманием, что такое переменная и параметр, и как вещи, названные n
в вашей программе, могут иметь разные значения. Так что теперь, если это значение находится в цикле управления или рекурсии, это еще хуже! Будьте добры и не используйте одинаковые имена переменных везде, и дайте понять, что параметры - это просто переменные .
Конечные условия
Как мне определить мое конечное состояние? Это легко, просто попросите их произнести шаги вслух. Например, для факториала начните с 5, затем 4, затем ... до 0.
Дьявол кроется в деталях
Не говорите о таких ранних вещах, как оптимизация хвостовых вызовов . Я знаю, я знаю, ТШО хорош, но поначалу им все равно. Дайте им некоторое время, чтобы обернуть голову вокруг процесса так, чтобы он работал для них. Не стесняйтесь разрушить их мир позже, но дайте им отдохнуть.
Точно так же не говорите прямо из первой лекции о стеке вызовов и его потреблении памяти и ... ну ... переполнении стека . Я часто в частном порядке репетирую студентов, которые показывают мне лекции, где у них есть 50 слайдов обо всем, что можно знать о рекурсии, когда они едва могут правильно написать цикл на данном этапе. Это хороший пример того, как справка поможет позже, но сейчас просто глубоко смущает вас.
Но, пожалуйста, своевременно проясните, что есть причины идти итеративным или рекурсивным путем .
Взаимная рекурсия
Мы видели, что функции могут быть рекурсивными, и даже то, что они могут иметь несколько точек вызова (8 ферзей, Ханой, Фибоначчи или даже алгоритм разведки для тральщика). Но как насчет взаимно рекурсивных вызовов ? Начните с математики здесь. f(x) = g(x) + h(x)
где g(x) = f(x) + l(x)
и h
и l
просто делать вещи.
Начиная только с математических рядов, легче писать и реализовывать, так как контракт четко определен выражениями. Например, женские и мужские последовательности Хофштадтера :
Однако с точки зрения кода следует отметить, что реализация взаимно рекурсивного решения часто приводит к дублированию кода и должна быть скорее сведена в единую рекурсивную форму (см. « Решение загадки каждого судоку» Питера Норвига ) .