2

I want to be able to have a (pretty) large FSM where I can debug it by jumping to a point and watching the execution.

For example, lets say I'm making a FSM that uses some probability on whether I go left or right at a junction. Maybe I have a bunch of things like "people I've seen go left" and "people I've seen go right".

Suppose the simple code above looks like this:

if (somethingA()) {
    if (somethingB()) {
        if (somethingX() && somethingY()) {
            // ...
        } else {
            // ...
        }
    } else if (somethingC()) {
        // ...
    }
} else {
    // ...
}

Further, assume that all the something...() functions are probability based!

The above code is ugly, but just for illustration.

Is there any kind of design that would allow me to go arbitrarily deep in a tree? Since it would be spread over multiple functions (again this is no trivially-sized FSM), could a design pattern help me here?

Ideally I'd like to probe how it works by jumping right to the point before somethingX() && somethingY() if I change some probability parameters to see how it reacts after, and see how it works going from there. For example if I have 10 nested functions like the above, I'd like to jump to layer 8, adjust the probability of one of the calls to see how it runs, and then split off a bunch of calls from that.

By this I mean jump to the point above, run a simulation, but call this simulation maybe 1000 times from that point (and I'd want to run this as quickly as possible and multithread it).

The only solution I can think of is to add some kind of POD (this is C++) and set it beforehand so I can jump exactly to where I want, like:

if (myStruct.jumpThroughA || somethingA()) {
    if (myStruct.jumpThroughB || somethingB()) {
        if (myStruct.jumpThroughXY || (somethingX() && somethingY())) {
            // ...
        } else {
            // ...
        }
    } else if (myStruct.jumpThroughC || somethingC()) {
        // ...
    }
} else {
    // ...
}

However I'm worried I'll be making clusterfuck code by doing this (but it may be my only option). I really want to avoid such code unless my back is against the wall.

Plus such a thing would be great for unit testing something specific, especially since randomness will play a role in these things and I'd like to test this by forcing it down some branches (my idea of a predictable PRNG for testing may or may not make it still a pain in the ass).

UPDATE:

  • I need to avoid making the function calls since I need code to be fast, so evaluating them beforehand as per here isn't feasible

  • Assume the something...() functions have side effects which are designed to aid in increasing computation speed (ex: somethingC() should not be calculated if somethingB() returns true since it would invoke a very costly function)

  • The arrow head antipattern also does not solve my "branching into" mess of having to add more booleans at each level to quickly descend on a branch of choice

Water
  • 356
  • 1
  • 12
  • Possible duplicate of [How to tackle a 'branched' arrow head anti-pattern?](http://softwareengineering.stackexchange.com/questions/205803/how-to-tackle-a-branched-arrow-head-anti-pattern) – gnat Mar 03 '17 at 18:29
  • @gnat That does not handle my probability issue and requires me to evaluate everything beforehand, by which I cannot do since there are side effects to the functions. Doing the solutions provided there would require me to do the arrow head pattern to avoid the arrow head pattern. It will also waste a significant amount of computation time because I will not be able to skip the function calls. I will edit the post with this information. – Water Mar 03 '17 at 18:35
  • 2
    If you want to jump into a FSM, you should just be able to set the state to o what you want and go... – whatsisname Mar 03 '17 at 19:16
  • if `somethingA()` is an object of some sort, you might want to consider replacing it temporarily with a [Mock object](http://stackoverflow.com/q/2665812/427192) that always returns the value you want for testing purposes. – Dan Pichelman Mar 03 '17 at 20:09
  • @whatsisname You missed the point of everything, which is partially my fault for including the words FSM since they don't quite relate to the question in retrospect (possibly even less than I'm letting on to be). The FSM is already a huge state that I jump to and then I have a bunch of conditions at the state which is the crux of the problem. Regardless, reading the entire thing would have had you see that so I recommend checking again and seeing if you have any solution that deviates from the other answers. – Water Mar 03 '17 at 22:24
  • If your FSM is that complex then I would say that this was a serious flag that major refactoring was needed. One option is to move to a tables based state machine as this can drastically simplify, (and speed up), the code. Also worth adding is that most consider code with side effects ***bad***! – Steve Barnes Mar 04 '17 at 10:32
  • @SteveBarnes Are you referring to a state transition table? – Water Mar 05 '17 at 00:20
  • @Water For fairly simple FSMs a state transition table is good, for complex ones, with some transition requirements having heavy overheads a state function table can have big benefits or even a hybrid table. – Steve Barnes Mar 05 '17 at 04:06

3 Answers3

3

I don't have the reputation to make a comment on JacquesB post.

I suggest breaking down the problem into two smaller problems. 1. Compute the next state. 2. Process that state.

Computing the next state can be done in a function with multiple returns. In order to make this run fast check the most frequently expected conditions before the less frequent conditions.

Upon return from the compute_next_state function use a switch statement to process each state.

cwallach
  • 327
  • 1
  • 7
2

If your code is a FSM it should be possible to jump to any specific state directly by making the state explicit. Eg. if you have:

if (state="start" && somethingA()) state = "A";
if (state="A" && somethingB()) state = "B";
if (state="B" && (somethingX() && somethingY()) ...
if (state="B") ...

Then you can start from the beginning by setting the state variable to the start value, but you can also start at any specific state by initializing the state variable to any other state.

When you have a deeply nested conditional (perhaps spanning multiple nested functions) then at each point you have an implicit state which indicates which branches you have selected at each junction. By turning this path into an explicit state object, you can "flatten" the conditional (possibly into a giant single-level if-else or switch) so you can jump directly to each point.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
1

You might be able to use a switch statement. If you do not include a break at the end of each block, the program will fall through and executive statements after the subsequent cases.

There's a good article on switch statements here.

Doug Anger
  • 49
  • 5