views:

203

answers:

5

When I create recursive methods, I often include a Depth-parameter, especially when I need some sort of bailout mechanism. The code will usually be something like this

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;

And I call it without the Depth-parameter

Recurse(<Params>);

Is there another way to easily find the depth?

A: 

Have a global variable that would count recursion? Otherwise no - recursion is just calling some method with some parameters.

Eugene Mayevski 'EldoS Corp
Don't use global variables to solve a local problem.
Jeroen Pluimers
Don't make global statements on narrow questions. It depends on the task. The problem can be as global as the application itself (for example, recursive processing of files in certain folder can be the only function of some application).
Eugene Mayevski 'EldoS Corp
@Eugene: The problem with global solutions that work for the 20% of a developers' time is that it will break in the 80% of the maintenance time. That's why a non-global solution is usually better: it is more resilient against mistakes. And that's the reason I warned. Maybe I should have phrased it a bit more friendly though :-)
Jeroen Pluimers
+5  A: 

If you had a way to walk the stack and see how many times your function's entry point was in there, I suppose you could do it that way. But then you'd notice that your aDepth parameter was right there too, and you'd realize that aDepth is just easier and much less trouble than snooping the stack. IMO, the simple solution is best here, it's portable and future-proof, unlike whatever stack snooping solution you could invent.
So yes, there are other ways, but your original solution is best, IMO.

Chris Thornton
Not what I hoped for, but it's always gratifying to hear that "my original solution was best" :-)
Svein Bringsli
The "stack walking" solution could work, of course, but sounds not ideal to me. The original solution is best, or perhaps using a TObject allocated on the stack to share some initial parameters and a depth private value during the recursion. It could be an elegant solution, but it depends on what you're doing in the recursed function.
A.Bouchez
+2  A: 

Declare a typed constant inside your procedure. Make sure you set the compile option to allow changes in constants.

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;
Eduardo Mauro
Interesting approach, but I still have to write code to keep track of current depth, which was kind of what I wanted to avoid. I was hoping for some magic "CurrentDepth" function :-)
Svein Bringsli
Rather than globally enabling assinable constants, wrap the procedure with {$J+}/{$J-}
Gerry
@Eduardo: don't! Those assignable constants (or initialized variables) are a global thing. Using that will limit your method to be only be callable from one site at a time; it will basically kill any multi-threaded or re-entrant use of your method.
Jeroen Pluimers
@Jeroen: You are right. But I assume that who uses such solution knows what he is doing. Is it also possible to use such solution in threads using threadvars.
Eduardo Mauro
A: 

In C++ I am able to do this

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();
}

but since objects in Object Pascal are always allocated on the heap, I wonder if it is possible to use instead String with default argument and access somehow its reference count

const 
    MyRecursionGuardConstString: string = "whatever";
procedure Recurse(RecursionGuard: string = MyRecursionGuardConstString) 
begin
    if GetRefCount(MyRecursionGuardConstString) > 100 then //o_o
        exit;
    end;
    Recurse;
end;
Alsk
Such globals are not threadsafe. The poster's parameter solution is.
Marco van de Voort
If you inc'ed your own treadvar refcount on each recursion would that work - as a threadsafe way of avoiding an extra parameter?
Peter Turner
@Peter Turner: I guess, yes, threadvar would do it
Alsk
In modern Delphi versions, you can implement your recursion guard as a record type. Records can now contain methods and fields. Records are value types and live on the stack.
Jeroen Pluimers
A: 
type
  TMyRecursion = class
  private
    nDepth: integer;
  public
    constructor Create;
    procedure Recursion(...)
  end;

In constructor you must initialize nDepth, of course.

Shreq