2

I have an algorithm I need to implement, which jumps around it's code a lot, lot's of conditional check and skipping steps, going back to previous steps, and all that jazz.

It's probably implementable with loop and ifs, but it would be a huge mess which would be terribly hard to maintain and debug.

So than I though of writing each 'subsection' of the algorithm as a separate function and handling it like that, with all those functions calling each other. but this algorithm will be running a long time (it's for a NPcomplete problem, so yea...), so I'm pretty sure this will at some point result in a stack overflow.

So than the last option I could think of was using goto's. Now I always hear that once you start using goto's you should seriously reconsider your design, so that's why I'm asking if there's a better way to do this?

Oke here is the pseudo code:

enter image description here

As you can see it jumps around a and does lot's of check and conditional skips and stuff. I though about just adding the labels exactly as described in this pseudo code and using goto's exactly as described in the code. The alternative I was talking about with functions would be putting each of the point of the algorithm in a different function, but for reasons mentioned I do not think this is a good idea

Community
  • 1
  • 1
user2520938
  • 411
  • 3
  • 15
  • TL;DR; No! Probably not ... – πάντα ῥεῖ May 27 '14 at 18:33
  • Is recursion involved? – Robert Harvey May 27 '14 at 18:34
  • No recursion involved – user2520938 May 27 '14 at 18:35
  • But you said you had functions calling each other, which means you do. – ikegami May 27 '14 at 18:35
  • Show us some code. There's no hope of getting an answer without seeing some code. We don't need the whole Bible, just a few verses. – Robert Harvey May 27 '14 at 18:36
  • Yes if I implement all the different 'subsection' of the algorithm in different functions, they will all be calling each other to go back and forth between different parts of the algorithm. The 'main' algorithm as a whole does not have any recursion – user2520938 May 27 '14 at 18:36
  • 4
    Still, it could be mutually recursive, and your problem is there is no exit path. It's hard to imagine an ordinary call tree being so deep that it is causing stack overflows without recursion. – Robert Harvey May 27 '14 at 18:37
  • @user2520938 _'... to go back and forth between different parts of the algorithm ...'_ This seriously sounds like you need a redesign :-/ ... – πάντα ῥεῖ May 27 '14 at 18:39
  • It's hard to see how you'd run out of stack without recursion, and it's hard to see how using goto could be used to avoid this recursion where better approaches couldn't. (If you can use goto, you got tail-call recursion, which is trivial to eliminate without goto.) – ikegami May 27 '14 at 18:39
  • If the issue involves errors, you may want to use the *exception* mechanism or `throw / try / catch`. – Thomas Matthews May 27 '14 at 18:40
  • It is not difficult to imagine an ordinary call tree being so deep that it overflows stacks. For instance, dumb linear recursion on a large input. That could happen in parsers. – Kaz May 27 '14 at 18:42
  • 1
    @Kaz, Read what I said again. "It's hard to see how you'd run out of stack **without recursion**". – ikegami May 27 '14 at 18:44

1 Answers1

3

This is exactly what tail recursion is for. It's goto disguised to look like a function call.

Use a language that has guaranteed semantics for tail calls, or a compiler with good tail recursion optimization for a language that doesn't.

For instance, suppose we have a state machine of this form:

{
  int state = S0       // main state variable
  int v1 = v1_initval, v2 = ..., ..., vn; // additional state variables

  while (state != S_ACCEPT) {
    switch (state) {
    case S0:
      // do whatever
      state = S5;
      break;
    case S2;
       ...
    case SN;
      ...
    }
  }
}

That can turn into tail recursion:

// case S0:
void S0(int v1, int v2, ..., int vn) {
  // do whatever
  S5(v1, v2, ..., vn);  // state = S5
}

The machine is started by a call to S0, which passes the correct initial values for the state variables. Then tail calls do the state transitions directly.

Under the tail call optimization, S5 call becomes an unconditional branch, and the argument passing is just assignment, which the compiler may be able to work out to a noop, since in the target function, the v1 parameter occupies the same storage location as the v1 argument in the caller.

Also, possible skeleton of an iterative state machine in C++:

class statemachine {
private:
  int sv1, sv2, ... , svn; // extra state variables
  void (statemachine::* state)(); // pointer-to-member: main state var

  void S0();
  void S1();
  // ...
  void S_ACCEPT();
public:
  statemachine();
  void run();
};

statemachine::statemachine()
: state(&statemachine::S0)  // initial state is S0
, sv1(...)                  // initializes for state vars
...
{
}

void statemachine::run()
{
   while (state != &statemachine::S_ACCEPT)
      (this->*state)();
}

void statemachine::S0()
{
   // modify state vars

   state = &statemachine::S5;
}

void statemachine::S_ACCEPT()
{
  abort(); // never called
}

You could almost make this look like recursion by adding a memer function which is called in order to set the next values of the member variables. The functions could end with this:

next(&statemachine::S42, v3, v1 + v2, ...);

where next is just a wrapper function that initializers the state variable and others from its arguments. So here, the next state will be S42, and v1 will take on the value of v3, v2 takes on v1 + v2 and so on.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • Surely C++ supports tail call optimization, in all of the available compilers. – Robert Harvey May 27 '14 at 18:39
  • Take a look [here](http://stackoverflow.com/questions/34125/which-if-any-c-compilers-do-tail-recursion-optimization) – Christopher Stevenson May 27 '14 at 18:44
  • Hm interesting. I'll give it a go with functions than. It does indeed seem like this problem will be dealt with by tail recursion. – user2520938 May 27 '14 at 18:47
  • 1
    @user2520938 We can guess that just from your high level description. The problem is not inherently recursive. You have an iterative graph structure that you're just trying to break up into functions. That almost certainly means that tail recursion is applicable. – Kaz May 27 '14 at 18:55
  • @Kaz I looked at it like a big state machine, very similar to the edit you just made in your post. I could implement it like that with a huge switch as well, instead of a separate function for each of the states. I'd imagine it is easier for the compiler to optimize it than. – user2520938 May 27 '14 at 18:58
  • @user2520938: I don't think a state machine would be better than seperate functions here. – Mooing Duck May 27 '14 at 19:00
  • 2
    You can use a switch, but with functions to hide the details. The state variables can be encapsulated into an object, as member variables, and the various cases can be member functions. The switch statement is still there, but it is condensed since it just dispatches member functions. – Kaz May 27 '14 at 19:01
  • 2
    Also, a pointer-to-member-function type member variable can be used to represent the state. Each state function then just sets that variable to point to the next function. – Kaz May 27 '14 at 19:01
  • Thanks a lot, that's a VERY elegant design imo. Definitely going to implement it this way. – user2520938 May 27 '14 at 19:22
  • Came here from a search. Pointers to functions are one of the most powerful things. You can have collections of these function pointers, use collections to store any data or state, use enums for easier function and state representation, rely heavily on `switch` (as c++ will optimize it anyways, e.g. with jump tables), utilize pointers to function pointers (swap em out), etc. All kinds of good options are available. It did seem to me like they basically wanted a fancy state machine of one kind or another though, and probs just to set the state + pointer for the next function ("state") to call. – Andrew Mar 21 '22 at 04:40