Рекурсія
Поряд з методом пошуку з поверненням (backtracking), рекурсія в мові ПРОЛОГ є одним з основних засобів побудови повторюваних дій у програмі. Тому вона вимагає більш детального аналізу в нашому курсі.
Звичайно, усяке нове поняття визначається через інші поняття, що передбачаються уже відомими.
Наприклад, визначення тангенса tg(x) = sin(x) / cos(x). Тобто тангенс визначається через sin, cos та /. Рухаючись від обумовленого поняття до використовуваного у визначеннях, ми, зрештою, приходимо до первинних (не обумовлених у рамках даної математичної теорії) понять (наприклад, крапка, пряма, число і т.і.).
Схожа ситуація виникає й у програмуванні. Кожну програму можна розглядати як набір операцій, кожна з яких визначає деяку нову команду через інші, що передбачаються відомими. Рухаючись від обумовленої команди до використовуваних, ми, зрештою, повинні прийти до невизначуваних понять, тобто до команд, що входять у систему команд виконавця для якого пишеться наша програма.
Але й у математиці й у програмуванні іноді зустрічаються “визначення”, що не укладаються в цю схему.
Наприклад.
fact (n) = 1, якщо n = 0
(*)
fact(n) = n*fact(n-1), якщо n>0
На перший погляд це визначення здається безглуздим, але воно працює:
fact (0) = 1, fact (1) = 1 * fact (1) = 1, fact (2) = 2*fact (1) = 2 і т.і.
Це визначення відрізняється від марних, хоча і вірних визначень
fact (n) = fact (n+2) / ((n+1)*(n+2)) або
fact (n) = fact (n)
У чому відмінності? Перша рівність є насправді не визначенням, а вимогою до функцій. Друге – найбільш слабка вимога – їй задовільняє будь-яка функція. Перша – більш сильна вимога, але йому задовольняє і така функція як
fact1(n) = C*fact1(n)
при будь-якому натуральному C.
І нарешті, вимозі (*) задовольняє лише одна функція. І це можна строго довести. Такі визначення прийнято називати рекурсивними. Рекурсивний – значить той, що використовує самого себе.
Такі визначення часто зустрічаються в житті.
Наприклад.
З'ЯСУВАТИ ЗНАЧЕННЯ СЛОВА:
1) Знайти слово в словнику.
2) Прочитати статтю, що пояснює значення цього слова.
Якщо пояснення зрозуміле, тобто стаття не містить незрозумілих слів, ПРОДОВЖ ЧИТАННЯ З ОСТАННЬОГО ПЕРЕРВАНОГО МІСЦЯ.
Якщо в поясненні зустрічається незнайоме слово, то ПРИПИНИ ЧИТАННЯ, ЗАПАМ'ЯТАЙ МІСЦЕ ПРИПИНЕННЯ і з'ясуй значення незнайомого слова, дотримуючи сукупності правил З'ЯСУВАТИ ЗНАЧЕННЯ СЛОВА.
Мова йде не про порочне коло визначень, а про образ дій, що придатний для використання і цілком виконуємий.
Інший приклад: родинні відносини.
X і Y є РОДИЧАМИ, якщо
а) Y – батько, мати, син, чи дочка X;
б) існує такий Z, що X є РОДИЧЕМ Z, а Z є РОДИЧЕМ Y.
Рекурсія в програмуванні
Рекурсія, тобто можливість ввести у визначення об'єкта посилання на сам об'єкт, часто використовується в програмуванні. Рекурсія - це один з фундаментальних інструментів, що є у розпорядженні програміста і вона заслуговує більш уважного підходу до себе. Рекурсивні визначення часто використовуються в програмуванні для визначення рекурсивних структур даних.
Наприклад, при програмуванні мовою ПАСКАЛЬ.
TYPE /* Лінійний односпрямований список */
ListPtr = ^List;
List = record
Info: InfoType;
Next: ListPtr;
end;
або
TYPE /* Бінарне дерево */
BinTreePtr = ^BinTree;
BinTree = record
Info: InfoType;
Left: BinTreePtr;
Right: BinTreePtr;
end;
Для обробки рекурсивних структур зручно використовувати рекурсивні алгоритми. Наприклад, висота бінарного дерева може бути визначена за допомогою рекурсивної функції
0, якщо t = nil
Висота(t) =
1 + max( Висота( left(t)), Висота( right(t) ) ), якщо t nil
Це визначення легко описується рекурсивною функцією на Паскалі.
function Height(t: BinTreePtr): byte;
begin
if t = nil then
Height := 0
else
Height := max (Height(t^.Left), Height(t^.Right))
end;