Якщо ви будуєте мінімальні операції загального комп'ютера з нуля, "Ітерація" виходить спочатку як складовий блок і менш ресурсоємна, ніж "рекурсія", ерго відбувається швидше.
Ми встановимо ієрархію понять, починаючи з нуля і визначаючи в першу чергу основні, основні поняття, потім будуємо з ними поняття другого рівня тощо.
Перша концепція: комірки пам'яті, сховище, стан . Щоб зробити щось, вам потрібні місця для зберігання кінцевих та проміжних значень результатів. Припустимо, у нас є нескінченний масив "цілих" комірок, що називається " Пам'ять , М [0..Нескінченний].
Інструкції: робити щось - перетворити клітинку, змінити її значення. змінити державу . Кожна цікава інструкція виконує перетворення. Основні інструкції:
a) Встановлення та переміщення комірок пам'яті
- зберігати значення в пам'яті, наприклад: зберігати 5 м [4]
- скопіюйте значення в іншу позицію: наприклад: зберігати m [4] m [8]
б) Логіка та арифметика
- і, або, xor, ні
- додати, sub, mul, div. наприклад, додайте m [7] m [8]
Виконавчий агент : ядро в сучасному процесорі. "Агент" - це те, що може виконувати інструкції. Агент також може бути людьми за алгоритмом на папері.
Порядок кроків: послідовність інструкцій : тобто: зробіть це спочатку, зробіть це після тощо. Обов'язкова послідовність інструкцій. Навіть вирази одного рядка є "обов'язковою послідовністю вказівок". Якщо у вас є вираз із конкретним "порядком оцінювання", то у вас є кроки . Це означає, що навіть один складений вираз має неявні "кроки", а також має неявну локальну змінну (назвемо це "результат"). наприклад:
4 + 3 * 2 - 5
(- (+ (* 3 2) 4 ) 5)
(sub (add (mul 3 2) 4 ) 5)
Вираз, наведений вище, передбачає 3 етапи із неявною змінною "результат".
// pseudocode
1. result = (mul 3 2)
2. result = (add 4 result)
3. result = (sub result 5)
Тому навіть виразні виразки, оскільки у вас є певний порядок оцінювання, є обов'язковою послідовністю інструкцій . Вираз передбачає послідовність операцій, які слід виконати в певному порядку, і оскільки є етапи , також існує неявна проміжна зміна "результат".
Інструкційний покажчик : Якщо у вас є послідовність кроків, у вас є також неявний "покажчик інструкції". Покажчик інструкцій позначає наступну інструкцію та просувається після того, як інструкція буде прочитана, але перед виконанням інструкції.
У цій псевдо-обчислювальній машині вказівник Інструкції є частиною пам'яті . (Примітка: як правило, покажчик інструкції буде "спеціальним реєстром" в ядрі процесора, але тут ми спростимо поняття і припустимо, що всі дані (включені регістри) є частиною "Пам'яті")
Перейти - Після того, як у вас є впорядкована кількість кроків та покажчик інструкцій , ви можете застосувати інструкцію " зберігати ", щоб змінити значення самого вказівника на інструкції. Ми будемо називати це специфічне використання інструкції магазину новою назвою: Jump . Ми використовуємо нову назву, оскільки простіше думати про це як про нову концепцію. Змінюючи покажчик інструкцій, ми доручаємо агенту "перейти на крок x".
Нескінченна ітерація : Відскочивши назад, тепер ви можете змусити агент "повторити" певну кількість кроків. На даний момент ми маємо нескінченну ітерацію.
1. mov 1000 m[30]
2. sub m[30] 1
3. jmp-to 2 // infinite loop
Умовне - Умовне виконання інструкцій. За допомогою пункту "умовний" ви можете умовно виконати одну з декількох інструкцій на основі поточного стану (яку можна встановити за допомогою попередньої інструкції).
Правильна ітерація : Тепер за допомогою умовного пункту ми можемо уникнути нескінченного циклу інструкції про стрибок назад . Зараз у нас є умовний цикл, а потім правильна ітерація
1. mov 1000 m[30]
2. sub m[30] 1
3. (if not-zero) jump 2 // jump only if the previous
// sub instruction did not result in 0
// this loop will be repeated 1000 times
// here we have proper ***iteration***, a conditional loop.
Іменування : присвоєння імен певному розташуванню пам'яті, що містить дані, або тримає крок . Це просто "зручність". Ми не додаємо нових інструкцій, маючи можливість визначати "імена" для місць розташування пам'яті. "Іменування" - це не інструкція для агента, це лише зручність для нас. Ім'я робить код (у цей момент) простішим для читання та легшим зміною.
#define counter m[30] // name a memory location
mov 1000 counter
loop: // name a instruction pointer location
sub counter 1
(if not-zero) jmp-to loop
Однорівнева підпрограма : Припустимо, є ряд кроків, які потрібно часто виконувати. Ви можете зберегти кроки в названій позиції в пам'яті, а потім перейти до цієї позиції, коли потрібно виконати їх (дзвінок). В кінці послідовності вам потрібно буде повернутися до точки заклику продовжити виконання. За допомогою цього механізму ви створюєте нові інструкції (підпрограми), складаючи основні інструкції.
Впровадження: (не потрібно нових концепцій)
- Зберігайте поточний покажчик інструкцій у заздалегідь визначеному положенні пам'яті
- перейти до підпрограми
- в кінці підпрограми ви отримуєте вказівник інструкції з попередньо визначеного місця пам'яті, ефективно повертаючись до наступної інструкції вихідного виклику
Проблема з однорівневою реалізацією: Ви не можете викликати іншу підпрограму з підпрограми. Якщо ви це зробите, ви перезапишете адресу, що повертається (глобальна змінна), тому ви не можете вкладати дзвінки.
Для кращої реалізації для підпрограм: Вам потрібен STACK
Стек : Ви визначаєте простір пам'яті, щоб він працював як "стек", ви можете "натиснути" значення на стеку, а також "спливати" останнє "натиснене" значення. Для реалізації стека вам знадобиться покажчик стека (подібний до вказівника інструкції), який вказує на фактичну "голову" стека. Коли ви "натискаєте" значення, зменшення покажчика стека зменшується, і ви зберігаєте це значення. Коли ви "pop", ви отримуєте значення за фактичним покажчиком стека, а потім покажчик стека збільшується.
Підпрограми Тепер, коли у нас є стек, ми можемо реалізувати належні підпрограми, що дозволяють вкладені виклики . Реалізація аналогічна, але замість того, щоб зберігати покажчик інструкції у заздалегідь визначеному положенні пам'яті, ми «висуваємо» значення IP у стеку . Наприкінці підпрограми ми просто «виводимо» значення зі стека, ефективно повертаючись до інструкції після початкового виклику . Ця реалізація, що має "стек", дозволяє викликати підпрограму з іншої підпрограми. За допомогою цієї реалізації ми можемо створити кілька рівнів абстракції, визначаючи нові інструкції як підпрограми, використовуючи основні інструкції або інші підпрограми як будівельні блоки.
Рекурсія : Що відбувається, коли підпрограма викликає себе ?. Це називається "рекурсія".
Проблема: Перезапис локальних проміжних результатів підпрограма може зберігатися в пам'яті. Оскільки ви викликаєте / повторно використовуєте ті ж дії, якщо проміжний результат зберігається у заздалегідь визначених місцях пам'яті (глобальні змінні), вони будуть перезаписані на вкладені виклики.
Рішення: Щоб дозволити рекурсію, підпрограми повинні зберігати локальні проміжні результати у стеці , тому для кожного рекурсивного виклику (прямого або непрямого) проміжні результати зберігаються в різних місцях пам'яті.
...
Нарешті, зауважте, що у вас є багато можливостей використовувати рекурсію. У вас є рекурсивні структури даних скрізь, ви дивитесь на одну: частини DOM, що підтримують те, що ви читаєте, - це RDS, вираз JSON - RDS, ієрархічна файлова система на вашому комп'ютері - RDS, тобто: у вас є кореневий каталог, що містить файли та каталоги, кожен каталог, що містить файли та каталоги, кожен із цих каталогів, що містять файли та каталоги ...