Что такое рекурсивный символ и какая бывает рекурсия

Как работает рекурсия – объяснение в блок-схемах и видео

Представляю вашему вниманию перевод статьи Beau Carnes How Recursion Works — explained with flowcharts and a video.

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

«Для того чтобы понять рекурсию, надо сначала понять рекурсию»

Рекурсию порой сложно понять, особенно новичкам в программировании. Если говорить просто, то рекурсия – это функция, которая сама вызывает себя. Но давайте попробую объяснить на примере.

Представьте, что вы пытаетесь открыть дверь в спальню, а она закрыта. Ваш трехлетний сынок появляется из-за угла и говорит, что единственный ключ спрятан в коробке. Вы опаздываете на работу и Вам действительно нужно попасть в комнату и взять вашу рубашку.

Вы открываете коробку только чтобы найти… еще больше коробок. Коробки внутри коробок и вы не знаете, в какой из них Ваш ключ. Вам срочно нужна рубашка, так что вам надо придумать хороший алгоритм и найти ключ.

Есть два основных подхода в создании алгоритма для решения данной проблемы: итеративный и рекурсивный. Вот блок-схемы этих подходов:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Какой подход для Вас проще?

В первом подходе используется цикл while. Т.е. пока стопка коробок полная, хватай следующую коробку и смотри внутрь нее. Ниже немного псевдокода на Javascript, который отражает то, что происходит (Псевдокод написан как код, но больше похожий на человеческий язык).

В другом подходе используется рекурсия. Помните, рекурсия – это когда функция вызывает саму себя. Вот второй вариант в псевдокоде:

Оба подхода выполняют одно и тоже. Основный смысл в использовании рекурсивного подхода в том, что однажды поняв, вы сможете легко его читать. В действительности нет никакого выигрыша в производительности от использования рекурсии. Порой итеративный подход с циклами будет работать быстрее, но простота рекурсии иногда предпочтительнее.

Поскольку рекурсия используется во многих алгоритмах, очень важно понять как она работает. Если рекурсия до сих пор не кажется Вам простой, не беспокойтесь: Я собираюсь пройтись еще по нескольким примерам.

Граничный и рекурсивный случай

То, что Вам необходимо принять во внимание при написании рекурсивной функции – это бесконечный цикл, т.е. когда функция вызывает саму себя… и никогда не может остановиться.
Допустим, Вы хотите написать функцию подсчета. Вы можете написать ее рекурсивно на Javascript, к примеру:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Эта функция будет считать до бесконечности. Так что, если Вы вдруг запустили код с бесконечным циклом, остановите его сочетанием клавиш «Ctrl-C». (Или, работая к примеру в CodePen, это можно сделать, добавив “?turn_off_js=true” в конце URL.)

Рекурсивная функция всегда должна знать, когда ей нужно остановиться. В рекурсивной функции всегда есть два случая: рекурсивный и граничный случаи. Рекурсивный случай – когда функция вызывает саму себя, а граничный – когда функция перестает себя вызывать. Наличие граничного случая и предотвращает зацикливание.

И снова функция подсчета, только уже с граничным случаем:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

То, что происходит в этой функции может и не быть абсолютно очевидным. Я поясню, что произойдет, когда вы вызовете функцию и передадите в нее цифру 5.

Сначала мы выведем цифру 5, используя команду Console.Log. Т.к. 5 не меньше или равно 1, то мы перейдем в блок else. Здесь мы снова вызовем функцию и передадим в нее цифру 4 (т.к. 5 – 1 = 4).

Мы выведем цифру 4. И снова i не меньше или равно 1, так что мы переходим в блок else и передаем цифру 3. Это продолжается, пока i не станет равным 1. И когда это случится мы выведем в консоль 1 и i станет меньше или равно 1. Наконец мы зайдем в блок с ключевым словом return и выйдем из функции.

Стек вызовов

Рекурсивные функции используют так называемый «Стек вызовов». Когда программа вызывает функцию, функция отправляется на верх стека вызовов. Это похоже на стопку книг, вы добавляете одну вещь за одни раз. Затем, когда вы готовы снять что-то обратно, вы всегда снимаете верхний элемент.

Я продемонстрирую Вам стек вызовов в действии, используя функцию подсчета факториала. Factorial(5) пишется как 5! и рассчитывается как 5! = 5*4*3*2*1. Вот рекурсивная функция для подсчета факториала числа:

Теперь, давайте посмотрим что же происходит, когда вы вызываете fact(3). Ниже приведена иллюстрация в которой шаг за шагом показано, что происходит в стеке. Самая верхняя коробка в стеке говорит Вам, что вызывать функции fact, на которой вы остановились в данный момент:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Заметили, как каждое обращение к функции fact содержит свою собственную копию x. Это очень важное условие для работы рекурсии. Вы не можете получить доступ к другой копии функции от x.

Нашли уже ключ?

Давайте кратенько вернемся к первоначальному примеру поиска ключа в коробках. Помните, что первым был итеративный подход с использованием циклов? Согласно этому подходу Вы создаете стопку коробок для поиска, поэтому всегда знаете в каких коробках вы еще не искали.

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Но в рекурсивном подходе нет стопки. Так как тогда алгоритм понимает в какой коробке следует искать? Ответ: «Стопка коробок» сохраняется в стеке. Формируется стек из наполовину выполненных обращений к функции, каждое из которых содержит свой наполовину выполненный список из коробок для просмотра. Стек следит за стопкой коробок для Вас!

И так, спасибо рекурсии, Вы наконец смогли найти свой ключ и взять рубашку!

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Вы также можете посмотреть мое пятиминутное видео про рекурсию. Оно должно усилить понимание, приведенных здесь концепций.

Заключение от автора

Надеюсь, что статья внесла немного больше ясности в Ваше понимание рекурсии в программировании. Основой для статьи послужил урок в моем новом видео курсе от Manning Publications под названием «Algorithms in Motion». И курс и статься написаны по замечательной книге «Grokking Algorithms», автором которой является Adit Bhargava, кем и были нарисованы все эти замечательные иллюстрации.

И наконец, чтобы действительно закрепить свои знания о рекурсии, Вы должны прочитать эту статью, как минимум, еще раз.

От себя хочу добавить, что с интересом наблюдаю за статьями и видеоуроками Beau Carnes, и надеюсь что Вам тоже понравилась статья и в особенности эти действительно замечательные иллюстрации из книги A. Bhargav «Grokking Algorithms».

Источник

Что такое рекурсия

Это дом, который построил Джек.

Рекурсия — важный элемент в математике и программировании. С её помощью можно упаковывать большие и сложные конструкции в маленькие и простые, а потом разворачивать обратно, когда нужно. Давайте выясним, как она устроена.

Дом, который построил Джек

Чтобы было понятнее, что такое рекурсия, возьмём стихотворение Самуила Маршака «Дом, который построил Джек»:

Вот дом,
Который построил Джек.

А это пшеница,
Которая в тёмном чулане хранится
В доме,
Который построил Джек.

А это весёлая птица-синица,
Которая часто ворует пшеницу,
Которая в тёмном чулане хранится
В доме,
Который построил Джек.

Вот кот,
Который пугает и ловит синицу,
Которая часто ворует пшеницу,
Которая в тёмном чулане хранится
В доме,
Который построил Джек.

Вот пёс без хвоста,
Который за шиворот треплет кота,
Который пугает и ловит синицу,
Которая часто ворует пшеницу,
Которая в тёмном чулане хранится
В доме,
Который построил Джек. …

Смотрите, что в нём интересного: каждая часть стихотворения состоит из нового начала и точного повторения того, что уже было до этого. Сначала у нас был только дом, который построил Джек — это нулевой уровень рекурсии, или вложенности.

Вот дом,
Который построил Джек.

Обозначим его за [0] и дальше будем увеличивать это число для каждого уровня. Следите за уровнями.

А это пшеница,
Которая в тёмном чулане хранится [0]

Чтобы получить полноценное продолжение, нам нужно взять предыдущий уровень и подставить его сюда:

А это пшеница,
Которая в тёмном чулане хранится
В доме,
Который построил Джек.

А это весёлая птица-синица,
Которая часто ворует пшеницу,
[1]

Если мы будем разворачивать стих, то на первом проходе получим такое:

А это весёлая птица-синица,
Которая часто ворует пшеницу,
Которая в тёмном чулане хранится
[0]

А на втором у нас уже появится полноценный стих:

А это весёлая птица-синица,
Которая часто ворует пшеницу,
Которая в тёмном чулане хранится
В доме,
Который построил Джек.

👉 Общее правило такое: когда есть рекурсия, её последовательно разворачивают на каждом шаге, пока не дойдут до нулевого уровня. Как только дошли — всё готово, рекурсия сделала своё дело. До этого момента она вызывает сама себя с новыми параметрами.

Рекурсия в программировании

Допустим, нам нужно посчитать сумму всех чисел от 1 до какого-то числа. Можно это сделать в цикле, а можно сделать универсальную функцию с рекурсией. Ей будет достаточно указать на входе число, до которого нужно всё посчитать, а она сама сделает всё остальное.

Сначала запишем это на JavaScript, а потом разберёмся с тем, как работает эта магия:

Затем мы организуем нулевой уровень — тот, где рекурсия начинается: if (x == 1) . Он говорит нам: если на вход поступит единица, то возвращаем единицу. Это логично — сумма всех чисел от 1 до 1 равна единице. Это как дом, который построил Джек — всё в итоге сведётся к этому.

А дальше идёт самое интересное — если мы не дошли до единицы, то мы берём значение x и складываем его с результатом этой же функции, но от предыдущего значения. Если мы, например, считаем rec(10), то эта команда сделает так:

Попробуйте сами вставить код в консоль и посмотрите на результат.

Особенности рекурсии

Так как рекурсия — это программа, которая вызывает саму себя, то при неправильном подходе рекурсия может использовать очень много памяти и машинных ресурсов. Чем глубже уровень рекурсии — тем больше ресурсов ей нужно.

Иногда программист увлекается рекурсией и забывает прописать выход из неё. В результате рекурсия бесконечно углубляется саму в себя, забирает в себя все ресурсы и программа падает. Говорят, что так образуются чёрные дыры, в которых всё исчезает.

Бывают моменты, когда рекурсия — это единственный способ выполнить нужную задачу. Например, при парсинге HTML-кода или для построения дерева зависимостей.

Что дальше

Теперь, когда мы достаточно знаем про рекурсию, то сможем сделать интересный новый проект. Например, чтобы наша программа сама решала судоку любой степени сложности или рисовала классные лабиринты.

Источник

Рекурсия. Беглый взгляд

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Ниже речь пойдёт о старушке рекурсии, которую неплохо бы представлять, понимать и применять.

Примечание: Данная небольшая статья написана для беглого ознакомления с рекурсией, некоторыми примерами её применения и опасностями.

Определение

Для начала стоит сказать, что рекурсия относится не только к программированию. Рекурсия — это общее понятие, которое может быть присуще чему угодно и встречаться в повседневной жизни, но больше всего она распространена в информатике и математике. Для программистов же умение применять рекурсию — большой плюс в коллекцию полезных навыков.

Самая большая глупость — это делать то же самое и надеяться на другой результат.

Под рекурсией понимают процесс повторения элементов самоподобным образом. Объект обладает рекурсией, если он является частью самого себя.

Частным случаем рекурсии является хвостовая рекурсия. Если любой рекурсивный вызов является последней операцией перед возвратом из функции, то это оно.

Некоторые примеры

Рекурсию надо бы понять, а определение для этого подходит хуже, чем наглядные примеры. Для лучшего понимания, конечно, всё же следует прочитать определение, посмотреть на пример, снова прочитать определение и снова посмотреть на пример… Повторять, пока не придёт осознание.

Отличный пример вы можете найти тут.

Самое известное программисту применение рекурсии — задачи на вычисление чисел Фибоначчи или факториала. Давайте покажем, как это реализовать на языке C:

Тут же стоит отметить, что декларативная парадигма, в частности парадигма логического программирования, намного лучше позволяет понять рекурсию, так как там это обычное дело.

Fork-бомба
Примечание: Рекурсивное создание процессов крайне быстро (из-за экспоненциального роста их количества) заполняет таблицу процессов, что достаточно опасно для системы.

Reboot кнопкой после такого делать немного не приятно.

Для математика первой ассоциацией, скорее всего, будет фрактал. Фракталы прекрасны и приятно для глаза показывают свойства самоподобия.

Самые известные фракталы:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Ну и в повседневной жизни классическим примером являются два зеркала, поставленных друг напротив друга.

Углубимся глубже

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Проста ли рекурсия? Однозначно нет. На вид кажется, что всё просто, однако рекурсия таит в себе опасности (А иногда она просто не понятна).

Вернёмся к примеру с вычислением чисел Фибоначчи. Сразу заметим, что возвращаемым результатом функции является вызов этой же функции, а если быть точнее, то сумма результатов вызова двух функций (именно поэтому рекурсия не хвостовая). Становится понятно, что второй вызов не произойдёт, пока не завершится первый (в котором также будет вызов двух функций). Тут же пытливый ум заметит, что из рекурсивной функции должен существовать «нормальный» выход, без самовызова, иначе мы познакомимся с переполнением стека вызовов — это один из ключевых моментов, который стоит держать в голове при работе с функциями вызывающими сами себя.

Заметим, что дерево вызовов получится большим, но максимальное количество вызовов в стеке будет заметно меньше (N-1 при N > 2, соответственно).

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Рекурсивные алгоритмы довольно-таки часто встречаются при работе с деревьями, сортировками и задачами на графах. Так что, чтобы лучше вникнуть нужна практика и для этого не плохо подходит вышеупомянутое (в частности, бинарные или общие деревья. Их реализация не так сложна, а опыт работы с рекурсией получится не плохой).

Помимо этого хотелось бы упомянуть Ханойские башни, которые также отлично подойдут для ознакомления с рекурсивными задачами. На Хабре также был отличный разбор этой игры.

Для полноты картины обязательно надо упомянуть о борьбе с рекурсией.

Повышается производительность. Но это не значит, что с ней просто необходимо бороться, ведь применение рекурсии очевиднее, проще и приятнее, чем итерационные варианты.

Под силу ли побороть любую рекурсию?

Однозначно да. Любой рекурсивный алгоритм можно переписать без использования рекурсии, а хвостовую рекурсию же очень легко перевести на итерацию (чем и занимаются некоторые компиляторы для оптимизации). Это также относится и к итерационным алгоритмам.

Самый известный способ — это использование стека. Здесь подробнее, для интересующихся.

Заключение

Спасибо за прочтение статьи. Надеюсь, что большинство не знакомых с рекурсией получили базовое представление о ней, а от знающих людей, конечно, хочется услышать дополнения и замечания в комментариях. Не бойтесь рекурсии и не переполняйте стек!

UPD: Добавлен корректный пример хвостовой рекурсии.

Источник

Разбираемся в рекурсии

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Про рекурсию ходит много шуток, и она традиционно считается одной из сложных для понимания тем в computer science, поэтому давайте сегодня немного о ней поговорим. А именно, давайте обсудим, как выражать доказуемо завершимые вычисления.

Зачем это надо? Рекурсия — один из краеугольных камней ФП, а некоторые из функциональных языков (например, Idris или Agda) обладают достаточно мощной системой типов, чтобы использовать их для проверки доказательств. А чтобы проверенным доказательствам на самом деле можно было доверять, было бы неплохо, чтобы логическая система, которую представляет система типов языка, была консистентна — то есть, если упрощать, чтобы в ней нельзя было доказать ложь.

Один из главных источников неконсистентности — незавершающиеся вычисления (для примера см. КДПВ), поэтому вышеупомянутые языки очень стараются дать возможность убедиться, что вычисления завершаются — соответствующая их часть даже имеет отдельное название, «termination checker». Но, увы, по фундаментальным причинам у любой такой проверки всегда будут ложноположительные или ложноотрицательные срабатывания, поэтому приходится идти на компромиссы. В доказательствах лучше перебдеть, чем недобдеть, поэтому всегда есть завершимые функции, которые этими языками отвергаются. Однако, эти функции можно переписать так, чтобы termination checker был доволен, если можно доказать, что рекурсивный вызов всегда в каком-то смысле «меньше».

Не так давно мне нужно было убедить Агду, что куча взаимно рекурсивных функций всегда завершается. Для этого пришлось почитать стандартную библиотеку и пораскрывать встречающиеся абстракции, чтобы понять, что же там на самом деле происходит. В процессе я делал заметки, а потом понял, что если добавить в эти заметки слова, то может получиться полезный кому-то ещё текст.

Немного вводных

Сначала определим одно слово, которое дальше будет часто встречаться. Фундированное отношение на множестве — это такое отношение, у которого нет бесконечных убывающих цепочек элементов. Стандартное определение немного отличается, но для наших множеств (заведомо счётных, с другими даже идеальные Тьюринг-полные компьютеры не умеют работать) разница невелика и без аксиомы выбора. В качестве синонима в конструктивных контекстах часто используется понятие «доступности» (или как на русский перевести «accessible» в этом контексте?), которым я тоже иногда буду пользоваться.

Кроме того, немного про Агду, на которой мы и будем всё это писать. Это язык из ML-семейства с похожим на хаскель синтаксисом и с поддержкой зависимых типов. Стоит упомянуть пару синтаксических особенностей, которые не встречаются, пожалуй, ни в одном другом языке:

Ещё я иногда буду писать слово «свидетель утверждения X», обозначая этим терм, имеющий тип, соответствующий утверждению X.

Расковыриваем НОД

Один из классических примеров, на котором ломаются стандартные termination checker’ы — алгоритм Евклида для нахождения наибольшего общего делителя двух чисел. При этом он достаточно прост для того, чтобы не тратить внимание на неважные детали, да и, оказывается, в стандартной библиотеке Агды он уже реализован. Нас интересует, в частности, вот эта функция:
Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия
Здесь и дальше я буду приводить код в виде скриншотов, так как Хабр не умеет в раскрашивание Агды (что объяснимо, без запросов к тайпчекеру там не разберёшься), да и весь нужный уникод не у всех есть.

Если мы пройдём по цепочке импортов, то увидим, что Acc определён так:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Кроме того, Acc параметризован:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

В принципе, можно было бы сразу подставить тело этого определения, но давайте для полноты разберёмся с типами.

Пожалуй, смысл этих типов можно было бы понять из контекста, но давайте не будем жульничать и посмотрим на определения.

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

С RecStruct всё чуть сложнее:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

В любом случае, если пристально посмотреть на это определение, то станет ясно, что RecStruct принимает тип A и возвращает какой-то другой тип — функцию из Pred A в себя же, а Pred A по большому счёту является функцией A → Set — как и положено всякому благочестивому предикату в теории типов.

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Но как это вообще связано с завершимостью? Почему этого Acc достаточно, чтобы доказать, что что-то завершается?

Доступность и структурная рекурсия

Оказывается, что это наша старая добрая структурная рекурсия под прикрытием.

В структурной рекурсии всё в порядке, если, сильно упрощая, мы совершаем рекурсивный вызов на синтаксическом субтерме одного из наших термов-аргументов. Вот, например, привычный Фибоначчи:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

В Фибоначчи используется один из самых простых типов данных в мире, тип натуральных чисел:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

но такая же логика работает и для чего-нибудь более сложного. Например, для бинарных деревьев:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Рекурсивные вызовы на каждом из поддеревьев, естественно, вполне себе корректны и доказуемо завершимы:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

А теперь время для ключевого трюка. Заметим, что определение Tree выше изоморфно такому:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Аналогично можно поступить и с натуральными числами, просто в этом случае функция будет принимать аргумент типа, содержащего всего одно значение:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Подход Tree’ и ℕ’ выглядит более тяжеловесно и неуклюже, чем привычный способ описания типов, но у него есть два преимущества. Во-первых, этот подход хорошо обобщается и куда более выразителен. Например, можно представить себе дерево со счётно бесконечным числом поддеревьев в каждом узле:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Или дерево, где в каждом узле конечное, но неизвестное заранее число поддеревьев:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Во-вторых, возвращаемые значения функций, «хранящихся» в конструкторах, считаются termination checker’ом структурно меньшими, так что мы спокойно можем делать рекурсивные вызовы на этих возвращаемых значениях (и метатеория языка гарантирует, что это на самом деле всё ещё консистентно). Например, можно написать что-то в духе подсчёта глубины «диагонали» дерева с бесконечным числом детей в каждом узле:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Визуализировать эту функцию уже не так просто, как написать, но написать можно, и termination checker вполне корректно будет считать её тотальной.

Если вернуться к нашим НОДам, то можно заметить, что Acc имеет примерно ту же форму, что и все эти забавные типы, описанные выше. В частности, конструктор Acc _ принимает функцию, которая по элементу y и свидетелю y возвращает другой Acc (конкретнее, значение типа Acc _ ). Теперь мы знаем, что termination checker считает этот Acc _ как структурно меньший, чем исходный Acc _ (кстати, даже несмотря на то, что его тип отличается). Поэтому мы можем передать это новое значение в рекурсивный вызов, и termination checker с удовольствием посчитает это корректной завершимой рекурсией, даже если ни один из прочих аргументов не стал структурно меньше.

В каком-то смысле Acc позволяет преобразовать «меньше-согласно-отношению» в «структурно-меньше», и нам, программистам, остаётся заполнить пробелы в этом преобразовании.

Примеры

Но как именно выглядит это преобразование?

Натуральные числа

Это доказательство — просто объект типа Acc _ для любого x :

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

После обычных для Агды пасов мы придём к чему-то вроде такого:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Если теперь перейти ко второй дырке, убрать dot pattern и дать имя первому аргументу, то получим что-то такое:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

В дырке при этом будет такая цель и такой контекст:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Пары чисел

Что насчёт чего-нибудь более сложного, как, например, пар натуральных чисел с лексикографическим порядком?

Такой порядок можно определить, например, так:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Доказательство его фундированности предлагается читателю в качестве упражнения.

Ладно, шучу, мне просто лень описывать процесс разработки доказательства. Конечный результат будет выглядеть примерно так:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Контрпримеры

Итак, мы встретились с парой типов и отношений на них, а также доказали их вполне упорядоченность. Но что случится, если мы попытаемся доказать что-то про фундированность отношения, которое таковым не является? Где именно и что именно сломается? Доказывать, что какие-то вещи невыразимы, бывает сложно, но мы попытаемся хотя бы немножко приблизиться к интуитивному пониманию этой самой невыразимости. Да и лично я считаю, что контрпримеры так же важны для понимания, как и обычные, положительные примеры, так что давайте ломать!

Тривиальный тип

Пожалуй, самый простой пример — тип-синглтон с отношением, которое выполняется для любых двух элементов этого типа (тривиальным образом, так как у этого типа всего один элемент):

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Давайте начнём писать доказательство фундированности:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Каков тип этой дырки?

Единственный способ создать значение типа Acc — через конструктор acc :

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Какой тип дырки перед нами после этого?

Ходим кругами

Что насчёт чего-нибудь поинтереснее? Давайте поиграем в камень-ножницы-бумагу, также известные как числа Пеано без аксиомы ∀x. suc(x) ≠ 0 на трёхэлементном множестве:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Из-за конечности Obj представляется разумным попытаться доказать фундированность через case split по аргументу. Затем давайте рассмотрим произвольную ветку после сплита (я люблю метал, поэтому выберу rock ):

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Бесконечные цепи уникальных элементов

Или, другими словами, пусть у нас есть такой свидетель. Тогда мы можем скормить ему 0, заем 1, затем 2, и так далее. Так как мы по факту имеем структурную рекурсию, то мы сможем написать незавершающееся вычисление, но, так как Агда консистентна, этого не может быть!

Или, в виде кода, кратко и ясно:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Наконец-то рекурсия

Как же используется доказательство фундированности? Посмотрим ещё раз на функцию вычисления НОД:

Что такое рекурсивный символ и какая бывает рекурсия. Смотреть фото Что такое рекурсивный символ и какая бывает рекурсия. Смотреть картинку Что такое рекурсивный символ и какая бывает рекурсия. Картинка про Что такое рекурсивный символ и какая бывает рекурсия. Фото Что такое рекурсивный символ и какая бывает рекурсия

Заключение

После этого маленького экскурса у меня получилось написать что-то чуть менее тривиальное, с кучей взаимно рекурсивных функций, бегающих по взаимно рекурсивным деревьям вывода в системе типов, формализацией которой я сейчас занимаюсь. Надеюсь, что если тебе, дорогой читатель, понадобится доказуемо завершимая рекурсия, эти записи помогут тебе с ней разобраться. Тем более, что, на мой взгляд, чаще всего все доказательства завершимости сводятся к введению размера как прямого отображения на множество натуральных чисел, а там с фундированностью всё просто, и 640 килобайт Acc _ хватит всем.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *