LINUX.ORG.RU

рекурсивная => нерекурсивная реализация

 


1

4

любая рекурсия может быть привращена в итеративную запись, правильно? (тезис Черча)

подскажите алгоритм, который умеет символьно производить такую операцию, например для языков Java/C/C++ ? Может кто-то уже налабал готовый тул?

причина вопроса в том, что некоторые вещи интуитивно записывать именно в рекурсивной форме, а в указанных языках рекурсией пользоваться небезопасно (= нельзя)

ps, наверное такую ересь сейчас ляпнул xD Если б такой тул существовал, создатели этих языков давно бы уже бы его туда запилили, они ж не школьники как ТС

★★★★☆

Последнее исправление: maxcom (всего исправлений: 2)
Ответ на: комментарий от nuboquest

Нет, я действительно не понимаю.

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

Debasher ★★★★★
()
Ответ на: комментарий от nuboquest

То что данные кончатся, и рекурсия закончится?

конечно. Но вот узнать заранее, КОГДА кончаться данные — невозможно(в общем случае). Причём невозможно даже в рантайме, пока все данные не обработаешь. Потому рекурсия «опасна», но не более опасна, чем простой цикл (который тоже может быть бесконечным, либо слишком длинным). Потому, задача ТСа — дурная. Он хочет накостылить стек, и надеется, что его костыльный стек будет чем-то лучше, чем железный.

f=function(){f()}

это говно не имеет смысла и не нужно.

emulek
()
Ответ на: комментарий от nuboquest

Ну так ты и говори за свой язык, а не за хвостовой вызов как таковой

В моём языке арийский настоящий Ъ хвостовой вызов. Он так ведёт себя и во всех остальных

Debasher ★★★★★
()
Ответ на: комментарий от nuboquest

ты за все компиляторы не кукарекай.

о да, твой петушиный не осилит, и будет проверять, false==false.

И почему это он должен его выкинуть? там может быть сайд эффект внутри.

какая разница, что там внутри? Это мёртвый код, который НИКОГДА не работает. Ну если не выкинет, то будет какой-то код, который никогда не запускается.

Че сишный компилятор такое реально обязан выкинуть?

по стандарту не обязан. IRL выкидывает конечно.

emulek
()

в указанных языках рекурсией пользоваться небезопасно (= нельзя)

Если ты стек переполнять не собираешься, то почему бы и нет? Скажем, обход дерева директорий вряд ли вызовет переполнение стека (разве что какой-нибудь мазохист найдется со 100500 уровнями вложенности), а выполнять его удобней именно рекурсивно (с итерациями замучишься).

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Debasher

В моём языке арийский настоящий Ъ хвостовой вызов

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

nuboquest
()
Ответ на: комментарий от emulek

какая разница, что там внутри? Это мёртвый код, который НИКОГДА не работает.

То есть, сайдэффекты не работают? Ну-ну.

nuboquest
()
Ответ на: комментарий от nuboquest

ты и совершаешь такие элементарные ошибки в суждениях.

пруф/обоснуй

Debasher ★★★★★
()
Ответ на: комментарий от nuboquest

Я тебе уже привел пруф/пример.

Нет, не привёл

Debasher ★★★★★
()
Ответ на: комментарий от stevejobs

Не хочу видеть StackOverflowError в Java

Прикрой монитор трякпой, например.

loz ★★★★★
()
Ответ на: комментарий от stevejobs

Практически, если конпеляция скопа идет дольше 30 секунд

какой-то ты бред сейчас сказал. Компеляция пройдёт мгновенно, а программа рухнет в рантайме. Когда и почему — не имеет значения. Да, если ты накостылишь стек в памяти, программа будет работать дольше, особенно если у тебя медленный и большой своп. А если возьмёшь железный стек (т.е. в C/C++ будешь юзать рекурсию), то твоя программа рухнет сразу, с сегфолтом. Ну и в чём фишка?

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

Кроме того, старайся все переменные рекурсивной функции сделать static, что-бы была их одна копия, а не множество. Конечно при этом функция станет no reentrant (как это по русски?), но за то, будет жрать меньше стека, который не слишком большой.

emulek
()
Ответ на: комментарий от nuboquest

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

А ты говно компиляторами-то не называй!

while(0){код} никогда не выполнится → его надо выкинуть. Вот ежели ты пишешь do{код}while(0), то это уже смысл — так макросы пишут, компилятор просто раскроет скобки и подставит "код" в нужное место.

Кстати, код вида

some_t flag = 0;
do{код}while(flag)
по умолчанию тоже выкидывается, поэтому нужно пояснить, что flag — volatile переменная. У меня уже на заре погроммирования для МК были такие приколы. Ну и в многопоточном программировании тоже иной раз бывают такие штуки: нужно одному потоку подождать, пока другой поток что-то отдаст, вот и делаешь нечто вроде
volatile some_t flag = DEFVAL;
...
do{
  usleep(10000);
}while(flag == DEFVAL);
Можно и мьютекс завести, конечно, но иной раз проще так.

Eddy_Em ☆☆☆☆☆
()

подскажите алгоритм, который умеет символьно производить такую операцию, например для языков Java/C/C++ ?

В них разве можно работать с символами? Только аст через костыли анализировать.

loz ★★★★★
()
Ответ на: комментарий от Eddy_Em

fix

some_t flag = 0;
do{код}while(flag)

здесь, конечно, наоборот:

some_t flag = 0;
while(flag){код};
Eddy_Em ☆☆☆☆☆
()

CPS-преобразование + трамплин + явный ручной стек протащенный через аргументы вызовов. Можно засунуть потом в один метод и заменить вызовы на goto. Если получается лапша из irreducible control flow, копировать базовые блоки до тех пор, пока CFG не станет reducible, после чего все goto можно заменить на структурные конструкции.

anonymous
()
Ответ на: комментарий от nuboquest

Нет, без ТСО тут еще добавляется опасность исчерпания стека и перерасхода памяти.

лечи рукожопие. Если не можешь — вон из профессии.

emulek
()
Ответ на: комментарий от Eddy_Em

Скажем, обход дерева директорий вряд ли вызовет переполнение стека

С парой примонтированных по сети разделов, с примонтированными в них разделами вполне может.

loz ★★★★★
()
Ответ на: комментарий от loz

Я ж про здравый смысл не зря сказал! А то ведь можно при желании и ведро пропатчить, чтобы хардлинк директорий работал =D

Eddy_Em ☆☆☆☆☆
()
#include <stdio.h>

int main () {
        goto x;

        while (false) {
        x:
                printf("Hello world!\n");
        }

        return 0;                                                                                                                                                                                                                                                              
} 
$ g++ -g main.c
$ ./a.out 
Hello world!
anonymous
()
Ответ на: комментарий от nuboquest

Это не имеет никакого отношения к делу.

Это имеет прямое отношение к делу.

Не все компиляторы поддерживают TCO.

При чём тут TCO?

Для тупых: компилятор, полностью автоматически, переводит рекурсивную программу в программу, рекурсии не содержащую (потому что на уровне машинных кодов рекурсии нет). Это РОВНО то, о чём был вопрос.

Miguel ★★★★★
()
Ответ на: комментарий от Eddy_Em

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

не замучаешься. Просто необходимо будет накостылить список, ну или, если ты любитель STL, то std::list. Да, ты не любитель STL, я в курсе.

А нужда в этом возникает, если ты желаешь сделать итератор для обхода дерева(если не хочешь callback). Вот каждый такой итератор должен тянуть за собой стек возвратов.

emulek
()
Ответ на: комментарий от stevejobs

CPS тебе все вызовы сделает хвостовыми. Делай потом с ними что захочешь, хотьв SSA переводи.

С Жабой убогой, однако, может и не получиться, в ней очень гомосяческое ограничение на размер метода.

anonymous
()
Ответ на: комментарий от nuboquest

То есть, сайдэффекты не работают?

с какого перепугу они будут работать, если код работать не будет? Пример можно?

emulek
()
Ответ на: комментарий от Miguel

А стек куда ты денешь? Да ты вообще нулевой, оказывается, да, это тебе не прохашкель бредни плести, тут понимать надо.

nuboquest
()
Ответ на: комментарий от emulek

итератор для обхода дерева

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

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Miguel

она ее хреново переводит, т.к. там случается StackOverflowException, которого в изначальном алгоритме не было вообще

stevejobs ★★★★☆
() автор топика
Ответ на: комментарий от emulek

Анонимус ниже привел пример с goto внутрь цикла while(false). На что я и посоветовал вспомнить longjmp.

В общем, степени ССЗБизма все-таки рассматривать не стоит. Так что пусть nuboquest такое не выдумывает, а считает априори, что погроммист все-таки не настолько тупой.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от stevejobs

Ты хочешь stackless JVM. Таких нет. Пиши сам.

Перевода рекурсивных (особенно виртуальных) вызовов в один метод с goto ты не увидишь, потрму как Ябка твоя убогая не умеет в методы длиннее чем hello, world.

anonymous
()
Ответ на: комментарий от Eddy_Em

абсолютно легальный код, средствами представляемыми С. вот и получился while (false) с сайд-эффектом. хотя main.s вроде говорит о том что код просто трансформировался в

#include <stdio.h>

int main() {
    printf("Hello world\n");
    
    return 0;
}
anonymous
()
Ответ на: комментарий от anonymous

Вот я и говорю: не нужно придумывать такие примеры, т.к. они вообще алогичны. Давай-таки не будем считать погроммистов совсем уж дятлами!

goto x имеет смысл лишь тогда, когда требуется выйти из вложенного цикла или в случае ошибки перейти в хвост функции, где освобождается выделенная память, чтобы ничего никуда не текло + меньше писанины было.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

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

anonymous
()
Ответ на: комментарий от anonymous

Не умеешь ты программировать

А мне насрать. Посмотри на мои репы на гитхабе и сосфорже. Работают и мои веб-морды, и лог-демон уже пятый год крутится, и всякие софтинки для работы с ПЗС-ками и т.п.

Так что отвали. А уж goto я понимаю.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от loz

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

anonymous
()
Ответ на: комментарий от anonymous

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

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Miguel

на уровне машинных кодов рекурсии нет

белены объелся? А это тогда что?

0804851d <_Z9factoriali>:
 804851d:   55                      push   ebp
 804851e:   89 e5                   mov    ebp,esp
 8048520:   83 ec 18                sub    esp,0x18
 8048523:   83 7d 08 01             cmp    DWORD PTR [ebp+0x8],0x1
 8048527:   7e 12                   jle    804853b <_Z9factoriali+0x1e>
 8048529:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 804852c:   48                      dec    eax
 804852d:   89 04 24                mov    DWORD PTR [esp],eax
 8048530:   e8 e8 ff ff ff          call   804851d <_Z9factoriali>
 8048535:   0f af 45 08             imul   eax,DWORD PTR [ebp+0x8]
 8048539:   eb 05                   jmp    8048540 <_Z9factoriali+0x23>
 804853b:   b8 01 00 00 00          mov    eax,0x1
 8048540:   c9                      leave
 8048541:   c3                      ret

emulek
()
Ответ на: комментарий от Eddy_Em

goto x имеет смысл лишь тогда, когда требуется выйти из вложенного цикла или в случае ошибки перейти в хвост функции, где освобождается выделенная память, чтобы ничего никуда не текло + меньше писанины было.

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

anonymous
()
Ответ на: комментарий от emulek

Иди в жопу со своим CISC-ом. Ты б еще байткод JVM показал. У меня вот CPU никаких call и ret не знает.

anonymous
()
Ответ на: комментарий от loz

С чего бы это он был один? Как раз важно только местоположение.

не, ну прям перепись пхпшников! В C/C++ не имеет значения, в каком месте выход из функции. Если выход единственный, то это хвост и есть. Мало того, gcc может переставляет хвост в середину, если например ты выходишь из безусловного цикла через break.

emulek
()
Ответ на: комментарий от Eddy_Em

goto x имеет смысл лишь тогда, когда требуется выйти из вложенного цикла или в случае ошибки перейти в хвост функции, где освобождается выделенная память, чтобы ничего никуда не текло + меньше писанины было.

лучше используй функции. Делай их inline, впрочем, gcc и так их разворачивает.

emulek
()
Ответ на: комментарий от anonymous

У меня вот CPU никаких call и ret не знает.

скорее это ты мало что знаешь про CPU. Т.ч. твоё мнение не очень интересно.

emulek
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.