2010-09-23 4 views
2

Когда я создаю рекурсивные методы, я часто включаю параметр Depth, особенно когда мне нужен какой-то механизм спасения. Код, как правило, что-то вроде этогоВ Delphi: Как найти глубину рекурсии без использования параметра?

procedure Recurse(<Params>; aDepth : integer = 0); 
begin 
    if aDepth > SomeLimit then 
    begin 
    //Tidy up, return best result found> 
    exit; 
    end; 

    <stuff> 

    if <Condition> then 
    Recurse(<Params>; aDepth+1) 
    else 
    begin 
    //Tidy up, return result of endnode> 
    end; 
end; 

И я называю это без глубины параметра

Recurse(<Params>); 

Есть еще один способ легко найти глубину?

+0

Вы хотите, чтобы чистое решение Delphi, или вы готовы отказаться от ассемблера? – dthorpe

+0

Нет, мне не нужен ассемблер. Я надеялся на решение, которое было _simpler_, тогда мой нынешний подход. –

ответ

7

Если у вас был способ пройти стек и посмотреть, сколько раз там была точка входа вашей функции, я полагаю, вы могли бы сделать это именно так. Но тогда вы заметили бы, что ваш параметр aDepth тоже был там, и вы поймете, что aDepth просто легче и гораздо меньше проблем, чем отслеживать стек. IMO, простое решение лучше всего здесь, оно переносимо и перспективно, в отличие от любого решения для отслеживания стека, которое вы могли бы придумать.
Так что да, есть и другие способы, но ваше оригинальное решение лучше всего, ИМО.

+1

Не то, на что я надеялся, но всегда приятно слышать, что «мое оригинальное решение было лучше» :-) –

+0

Решение «стежка» могло бы работать, конечно , но звучит не идеально для меня. Исходное решение лучше всего, или, возможно, использование TObject, выделенного в стеке, для обмена некоторыми начальными параметрами и частным значением глубины во время рекурсии. Это может быть изящное решение, но это зависит от того, что вы делаете в рекурсивной функции. –

0

Есть глобальная переменная, которая учитывала бы рекурсию? В противном случае no-recursion просто вызывает некоторый метод с некоторыми параметрами.

+1

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

+0

Не делайте глобальных заявлений по узким вопросам. Это зависит от задачи. Проблема может быть такой же глобальной, как и само приложение (например, рекурсивная обработка файлов в определенной папке может быть единственной функцией какого-либо приложения). –

+2

Проблема с глобальными решениями, которые работают на 20% времени разработчиков, заключается в том, что она сломается в 80% времени обслуживания. Вот почему неглобальное решение обычно лучше: оно более устойчиво к ошибкам. Вот почему я предупредил. Может быть, я должен был сформулировать это немного более дружелюбно, хотя :-) –

2

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

procedure Recurse; 
const 
    aDepth : integer = 0; 
begin 
    aDepth := aDepth + 1; 

    try 
    if aDepth > SomeLimit then 
    begin 
     //Tidy up, return best result found> 
     exit; 
    end; 

    <stuff> 

    if <Condition> then 
     Recurse 
    else 
    begin 
     //Tidy up, return result of endnode> 
    end; 
    finally 
    aDepth := aDepth - 1; 
    end; 
end; 
+0

Интересный подход, но мне все еще нужно писать код, чтобы отслеживать текущую глубину, что было вроде того, чего я хотел избежать. Я надеялся на какую-то магическую функцию «CurrentDepth» :-) –

+1

Вместо того, чтобы глобально включать assinable константы, оберните процедуру с помощью {$ J +}/{$ J-} ​​ –

+1

, нет! Эти назначаемые константы (или инициализированные переменные) являются глобальными.Использование этого ограничит ваш метод только возможностью вызова с одного сайта за раз; он в основном убьет любое многопоточное или повторное использование вашего метода. –

0

В C++ я могу сделать это

class recursion_guard 
{ 
public: 
    recursion_guard() { depth_count++; } 
    ~recursion_guard() { depth_count--; } 
    static int depth_count; 
}; 
int recursion_guard::depth_count = 0; 
void recurse(recursion_guard a = recursion_guard()) 
{ 
    if(recursion_guard::depth_count > 100) 
     return; 
    recurse(); 
} 

, но так как объекты в Object Pascal всегда выделяется в куче, мне интересно, если это возможно использовать вместо строки с аргументом по умолчанию и доступа как-то его счетчик ссылок

const 
    MyRecursionGuardConstString: string = "whatever"; 
procedure Recurse(RecursionGuard: string = MyRecursionGuardConstString) 
begin 
    if GetRefCount(MyRecursionGuardConstString) > 100 then //o_o 
     exit; 
    end; 
    Recurse; 
end; 
+0

Такие глобалы не являются потокобезопасными. Решение параметра плаката. –

+0

Если вы указали на свой реферанс treadvar на каждой рекурсии, это сработает - как потокобезопасный способ избежать дополнительного параметра? –

+0

@Peter Turner: Я думаю, да, threadvar сделал бы это – Alsk

0
type 
    TMyRecursion = class 
    private 
    nDepth: integer; 
    public 
    constructor Create; 
    procedure Recursion(...) 
    end; 

В конструкторе вы должны инициализировать nDepth, конечно.

Смежные вопросы