В далекі часи застосовували рекурсію для збільшення палацового простору. Через систему спрямованих один на одного дзеркал, можна створити приголомшливі об'ємні просторові ефекти. Але чи так легко зрозуміти, як налаштувати ці дзеркала? А ще більш складно визначити, де знаходиться точка в просторі, відображена через декілька дзеркал.
Рекурсія, рекурсивні алгоритми: зміст і синтаксис
Завдання, яка формулюється повторенням послідовності операцій може бути вирішена рекурсивно. Простий алгоритм обчислення квадратного рівняння, скрипт наповнення веб-сторінки інформацією, читання файлу, передача повідомлення) не вимагає застосування рекурсії. Основні відмінності алгоритму, який допускає рекурсивне рішення:Рекурсивний Алгоритм: коли послідовність операцій виконується багаторазово, на даних, які змінюються кожного разу дає кожен раз новий результат.
Формула рекурсії
Математичне розуміння рекурсії та її аналог у програмуванні відмінні. Математики хоч і властиві ознаки програмування, але програмування - це математика набагато більш високого порядку. Добре написаний алгоритм - як дзеркало інтелекту його автора. Загальна формула рекурсія у програмуванні «f (x)», де «x ~/= f (x)» має, як мінімум, два варіанти інтерпретації. Тут «~» - подібність або відсутність результату, а «=» - наявність результату функції. Перший варіант: динаміка даних.Дані та рекурсія
Програмування рекурсивних алгоритмів - це не обчислення факторіала, в якому функція отримує кожен раз дане, відмінне одиницю в меншу або більшу сторону - варіант реалізації залежить від переваги розробника. Не суть важливо, як вважати факторіал «8!», рухаючись від 012 або навпаки 876 Аналогічно обчислення математичної послідовності, фрактала або нескінченного ряду записується простою математичною формулою і, відповідно, алгоритмом, який суворо дотримується цієї формули. Обробка інформації - це «математика» зовсім іншого порядку. Рекурсивні функції та алгоритми тут оперують літерами, словами, фразами, реченнями та абзацами. Кожен наступний рівень використовує попередній. Вхідний потік даних аналізується по широкому спектру умов, але процес аналізу в цілому рекурсивен. Немає сенсу писати унікальні алгоритми на всі варіанти вхідного потоку. Повинен бути один функціонал. Тут рекурсивні алгоритми приклади того, як сформувати вихідний потік, адекватний вхідного. Це не результат, вступник на вхід рекурсивного алгоритму, але це бажане і необхідне рішення.Абстракція, рекурсія і ООП
Об'єктно-орієнтоване програмування (ООП) і рекурсія - кардинально різні сутності, але вони ідеально доповнюють один одного. Абстракція не має ніякого відношення до рекурсії, але крізь призму ООП створює можливість реалізації контекстної рекурсії. Наприклад, йде розбір інформації та виділяються окремо букви, слова, фрази, речення та абзаци. Очевидно, розробник передбачить створення екземплярів об'єктів цих п'яти типів і запропонує рішення рекурсивних алгоритмів на кожному рівні.Між тим, якщо на рівні літер «немає сенсу шукати сенс», то на рівні слів з'являється семантика. Можна розділити слова на дієслова, іменники, прислівники, прийменники Можна піти далі і визначити відмінки. На рівні фраз семантика доповнюється знаками пунктуації та логікою поєднання слів. На рівні речень виявляється більш досконалий рівень семантики, а абзац можна розглядати як закінчену думку. Об'єктно-орієнтована розробка зумовлює успадкування властивостей і методів і пропонує починати ієрархію об'єктів зі створення абсолютно абстрактного предка. При цьому, поза всяким сумнівом, аналіз кожного нащадка буде мати рекурсивний характер і не надто відрізнятися на технічному рівні по багатьом позиціям (літери, слова, фрази і пропозиції). Абзаци, як закінчені думки, можуть виділятися з цього списку, але не суть. Важливо, що переважну частину алгоритму можна сформулювати на рівні абстрактного предка, уточнюючи її на рівні кожного нащадка даними і методами, що викликаються з абстрактного рівня. В даному контексті абстракція відкриває нові горизонти для рекурсії.
Історичні особливості ООП
ООП приходило у світ програм двічі, хоча деякі фахівці можуть виділити появу хмарних технологій і сучасні уявлення про об'єкти і класи, як новий виток в розвитку ІТ-технологій. Терміни «об'єкт» і «об'єктний» в сучасному контексті ООП, прийнято відносити до 50-х і 60-м рокам минулого століття, але асоціювати їх з 1965 роком і появою мов Simula, Lisp, Algol, Smalltalk. В ті часи програмування не відрізнялося особливим розвитком і не могло адекватно реагувати на революційні концепції. До боротьби ідей і стилів програмування (С/С++ та Pascal - в основному) ще було далеко, а бази даних ще тільки формувалися концептуально. В кінці 80-х і початку 90-х в Pascal з'явилися об'єкти і всі згадали про класи в С/С++ - це ознаменувало новий виток інтересу до ООП і саме тоді інструментальні засоби, насамперед мови програмування стали не тільки підтримувати об'єктно-орієнтовані ідеї, але і розвиватися відповідно їм. Природно, якщо раніше рекурсивні алгоритми представляли собою просто функції, які використовуються в загальному коді програми, то тепер рекурсія могла стати частиною властивостей об'єкта (класу), що в контексті спадкування надавало цікаві можливості.Особливість сучасного ООП
Розвиток ООП спочатку декларувало об'єкти (класи) як сукупності даних і властивостей (методів). Фактично мова йшла про даних, що мають синтаксис і зміст. Але тоді не вдалося представити ООП, як інструмент управління реальними об'єктами. ООП перетворилося в інструмент управління об'єктами «комп'ютерної природи». Скрипт, кнопка, елемент меню, рядок меню, тег у вікні браузера - це об'єкт. Але не верстат, продукт харчування, слово, або пропозицію. Реальні об'єкти залишилися за межами об'єктно-орієнтованого програмування, а комп'ютерні інструменти придбали нове втілення. Внаслідок відмінностей популярних мов програмування, з'явилося безліч діалектів ООП. За семантикою вони практично еквівалентні, а їх орієнтація на інструментальну сферу, а не на прикладну, робить можливим виносити опис реальних об'єктів за межі алгоритмів і забезпечувати їх міжплатформною і міжмовне «існування».Стеки та механізми виклику функцій
Механізми виклику функцій (процедур, алгоритмів) вимагають передачі даних (параметрів), повернення результату і запам'ятовування адреси оператора, який повинен отримати управління після завершення функції (процедури). Зазвичай для цих цілей використовується стек, хоча мови програмування або сам програміст-розробник може передбачити найрізноманітніші варіанти передачі управління. Сучасне програмування припускає, що ім'я функції може бути не тільки параметром: воно може формуватися в процесі виконання алгоритму. Алгоритм також може бути створений у процесі виконання іншого алгоритму. Поняття рекурсивних алгоритмів, коли їх імена і тіла можуть бути визначені в момент утворення задачі вибору потрібного алгоритму) розширює рекурсивность не тільки на те, як щось зробити, але і хто саме це повинен зробити. Вибір алгоритму за його «осмисленому» імені - перспективно, але створює труднощі.Рекурсивность на множині функцій
Не можна сказати, що рекурсивний алгоритм, коли він викликає себе і тільки. Програмування - не догма, а поняття рекурсивности - це не виключне вимогу викликати себе з тіла власного алгоритму. Практичні області застосування не завжди дають чисте рішення. Часто вихідні дані слід готувати, а результат рекурсивного виклику необхідно аналізувати в контексті всієї задачі (всього алгоритму) в цілому. Фактично, не тільки перед викликом рекурсивної функції, але і після її завершення, може або повинна бути викликана інша програма. Якщо з викликом особливих проблем немає: рекурсивна функція A() викликає функцію B(), яка щось робить і викликає A(), то відразу виникає проблема з поверненням управління. Відпрацювавши рекурсивний виклик, функція A() повинна одержати управління, щоб повторно викликати B(), яка знову її викличе. Повернення управління, як слід по черзі в стеку назад в B() - неправильне рішення. Програміст не обмежений у виборі параметрів і може комплектувати їх іменами функцій. Інакше кажучи, ідеальне рішення передати в A() ім'я B() і нехай сама A() робить виклик B(). В такому варіанті не буде проблем з поверненням управління, так і реалізація рекурсивного алгоритму буде прозорішим.Розуміння і рівень рекурсії
Проблема розробки рекурсивних алгоритмів у тому, що потрібно мати уявлення про динаміку процесу. При використанні рекурсії в методах об'єктів, особливо на рівні абстрактного предка, з'являється проблема розуміння власного алгоритму в контексті часу його виконання. В даний час немає обмежень за рівнем вкладеності функцій і місткості стека в механізмах викликів, але є проблема розуміння: в який момент часу який рівень даних або яке саме місце в загальному алгоритмі виконало виклик рекурсивної функції і на якій кількості викликів самої себе, вона знаходиться. Існуючі інструменти налагодження часто безсилі підказати програмісту правильне рішення.Цикли і рекурсія
Вважається, що циклічне виконання еквівалентно рекурсії. Дійсно, в деяких випадках, рекурсивний алгоритм можна реалізувати в синтаксисі умовних і циклічних конструкцій. Однак, якщо є чітке розуміння, що конкретна функція повинна бути реалізована за допомогою рекурсивного алгоритму, слід відмовитися від будь-якого зовнішнього варіанти використання циклу або умовних операторів. Сенс тут полягає в тому, що рекурсивне рішення у вигляді функції, що використовує саму себе буде закінченим, функціонально повним алгоритмом. Цей алгоритм вимагає від програміста зусиль при його створенні, розуміння динаміки роботи алгоритму, але він буде остаточним рішенням, яке не потребує зовнішнього управління. Будь-яка комбінація зовнішніх умовних і циклічних операторів не дозволить представити рекурсивний алгоритм у вигляді повноцінної закінченою функції.Консенсус рекурсії та ООП
Практично у всіх варіантах розробки рекурсивного алгоритму виникає план розробити два алгоритму. Перший алгоритм формує список майбутніх об'єктів (примірників), а другий алгоритм являє собою власне рекурсивні функції. Кращим рішенням буде оформити рекурсію у вигляді одного властивості (методу), власне містить рекурсивний алгоритм, а всю підготовчу роботу винести в конструктор об'єкта. Рекурсивний алгоритм буде тільки тоді правильним рішенням, коли він працює тільки сам, без зовнішнього контролю і управління. Зовнішній алгоритм може тільки дати сигнал на роботу. Результатом цієї роботи має бути очікуване рішення, без зовнішньої підтримки. Рекурсія повинна бути завжди закінченим самостійним рішенням.Інтуїтивне розуміння і функціональна повнота
Коли об'єктно-орієнтоване програмування стало стандартом де-факто, стало очевидно: для ефективного кодування слід змінити власне мислення. Програміст повинен перейти від синтаксису і семантики мови до динаміки семантики в ході виконання алгоритму. Характерна риса рекурсії: вона може бути застосована у всьому: