Я новичок в этом сайте, и этот вопрос, конечно, не исследовательский уровень - ну да ладно. У меня есть немного опыта в разработке программного обеспечения и почти нет в CSTheory, но я нахожу это привлекательным. Короче говоря, я хотел бы получить более подробный ответ на следующий вопрос, если этот вопрос приемлем на этом сайте.
Итак, я знаю, что у каждой рекурсивной программы есть итеративный аналог, и я вроде как понимаю популярное объяснение, которое предлагается для нее, поддерживая что-то похожее на «системный стек» и выдвигая настройки среды, такие как адрес возврата и т. Д. Я нахожу этот вид волнистым ,
Будучи немного более конкретным, я хотел бы (формально) посмотреть, как можно доказать это утверждение в тех случаях, когда у вас есть функция, вызывающая цепочку . Кроме того, что если существуют некоторые условные операторы, которые могут привести к вызову F i некоторого F j ? То есть граф вызовов потенциальных функций имеет несколько сильно связанных компонент.
Я хотел бы знать, как можно справиться с этими ситуациями, скажем, с помощью рекурсивного итеративного преобразователя. И действительно ли этого ручного описания, о котором я говорил ранее, достаточно для этой проблемы? Я имею в виду, почему я считаю, что удаление рекурсии в некоторых случаях легко. В частности, удалить рекурсию из обхода бинарного дерева по предварительному заказу очень просто - это стандартный вопрос интервью, но удаление рекурсии в случае почтового заказа всегда было для меня кошмаром.
Что я действительно спрашиваю, так это вопроса
(1) Есть ли действительно более формальное (убедительное?) Доказательство того, что рекурсия может быть преобразована в итерацию?
(2) Если эта теория действительно существует, то почему я нахожу, например, итерацию предзаказа проще, а поступорядочения так сложно? (кроме моего ограниченного интеллекта)
источник
Ответы:
Если я правильно понимаю, вы понимаете, как преобразовывать функции, которые не содержат других вызовов функций, кроме самих себя.
с
f'
иg'
нерекурсивных функций (или, по крайней мере, независимо отf
иg
) становитсяЭто естественно распространяется на большее количество функций и более сложные функции.
источник
f
иg
принимать различные типы типов, требуется более общий прием.f
g
Да, есть убедительные основания полагать, что рекурсия может быть превращена в итерацию. Это то, что делает каждый компилятор, когда переводит исходный код на машинный язык. Для теории вы должны следовать советам Дейва Кларка. Если вы хотите увидеть реальный код, который преобразует рекурсию в нерекурсивный код, взгляните на
machine.ml
язык MiniML в моем PL Zoo (обратите внимание, чтоloop
функция внизу, которая фактически выполняет код, является хвостовой рекурсивной, и поэтому она может быть тривиально преобразован в реальный цикл).Еще кое-что. MiniML не поддерживает взаимно рекурсивные функции. Но это не проблема. Если у вас есть взаимная рекурсия между функциями
рекурсия может быть выражена в виде одной рекурсивной карты
источник
Вы можете посмотреть на SECD-машину . Функциональный язык (хотя это может быть любой язык) переводится в серию инструкций, которые управляют такими вещами, как помещение аргументов стеков, «вызов» новых функций и т. Д., Управляемых простым циклом.
Рекурсивные вызовы никогда не выполняются. Вместо этого инструкции тела вызываемой функции помещаются в стек для запуска.
Связанный подход - машина CEK .
Они оба были вокруг в течение долгого времени, поэтому есть много работы над ними. И, конечно, есть доказательства того, что они работают, и процедура «компиляции» программы в инструкции SECD линейна по размеру программы (ей не нужно думать о программе).
Суть моего ответа в том, что есть автоматическая процедура для того, что вы хотите. К сожалению, преобразование не обязательно будет с точки зрения вещей, которые программисту легко понять. Я думаю, что ключ заключается в том, что когда вы хотите разбить программу на части, вам необходимо сохранить в стеке то, что программа должна делать, когда вы возвращаетесь из вызова с итеризацией (это называется продолжением). Для некоторых функций (таких как хвостовые рекурсивные функции) продолжение тривиально. Для других продолжение может быть очень сложным, особенно если вам придется кодировать его самостоятельно.
источник
В : «Есть ли действительно более формальное (убедительное?) Доказательство того, что рекурсия может быть преобразована в итерацию?»
A : Полнота Тьюринга машины Тьюринга :-)
Помимо шуток, модель машины хранимой программы случайного доступа (RASP) Тьюринга близка к тому, как работают реальные микропроцессоры, и ее набор команд содержит только условный переход (без рекурсии). Возможность динамического самоизменения кода облегчает задачу реализации подпрограмм и рекурсивных вызовов.
Я думаю, что вы можете найти много статей / статей о « рекурсивном и итеративном преобразовании » (см. Ответ Дейва или просто Google ключевые слова), но, возможно, менее известным (и практичным ) подходом является последнее исследование аппаратной реализации рекурсивных алгоритмов ( использование языка VHDL, который «скомпилирован» непосредственно в аппаратную часть). Например, см. Статью В. Склярова « Рекурсивные алгоритмы на основе ПЛИС » ( В статье предлагается новый метод реализации рекурсивных алгоритмов в аппаратном обеспечении. Изучено два практических применения рекурсивных алгоритмов в области сортировки и сжатия данных. подробно .... ).
источник
Если вы знакомы с языками, которые поддерживают лямбда-выражения, то одним из способов является изучение преобразования CPS. Удаление использования стека вызовов (и в частности рекурсии) - это именно то, что делает преобразование CPS. Он преобразует программу, содержащую вызовы процедур, в программу с только хвостовыми вызовами (вы можете думать о них как о gotos, который является итеративной конструкцией).
Преобразование CPS тесно связано с явным хранением стека вызовов в традиционном стеке на основе массива, но вместо массива стек вызовов представлен связанными замыканиями.
источник
по моему мнению, этот вопрос восходит к истокам определений вычислений и давно был строго доказан в то время, когда было доказано, что церковное лямбда-исчисление (которое в высокой степени отражает концепцию рекурсии) эквивалентно машинам Тьюринга и содержится в нем. во все еще используемой терминологии "рекурсивные языки / функции". также, по-видимому, более поздняя ключевая ссылка по этим направлениям выглядит следующим образом
большая часть этой статьи находится на этой странице википедии, посвященной церковному тезису . Я не уверен в точных деталях, но статья в Википедии, кажется, указывает, что именно Россер (1939) первым доказал эту эквивалентность между лямбда-исчислением и машинами Тьюринга. возможно / предположительно его статья имеет стековый механизм для преобразования (возможно, рекурсивных) лямбда-вызовов в конструкцию tm?
Россер, JB (1939). «Неформальное изложение доказательств теоремы Годеля и теоремы Чёрча». Журнал символической логики (Журнал символической логики, том 4, № 2) 4 (2): 53–60. DOI: 10.2307 / 2269059. JSTOR 2269059.
Обратите внимание, конечно, для всех, кто интересуется принципами, современный язык Лисп и вариантная Схема намеренно имеют сильное сходство с лямбда-исчислением. Изучение кода интерпретатора для оценки выражений приводит к идеям, которые изначально содержались в статьях, посвященных завершенности лямбда-исчисления.
источник