implementing a state machine using the “yield” keyword

若如初见. 提交于 2019-11-27 03:50:52

It's feasible but it is a bad idea. Iterator blocks were created to help you write custom iterators for collections, not for solving the general-purpose problem of implementing state machines.

If you want to write a state machine, just write a state machine. It's not hard. If you want to write a lot of state machines, write a library of useful helper methods that let you cleanly represent state machines, and then use your library. But don't abuse a language construct intended for something completely different that just happens to use state machines as an implementation detail. That makes your state machine code hard to read, understand, debug, maintain and extend.

(And incidentally, I did a double-take when reading your name. One of the designers of C# is also named Matt Warren!)

Yes, it's absolutely possible and easy to do. You can enjoy using control flow constructs (for, foreach, while, ... goto (using goto particularly suits this scenario ;))) along with yields to build one.

IEnumerator<State> StateMachine
             (Func<int> currentInput /* gets current input from IO port */, 
              Func<int> currentOutput) {
    for (;;)  {
       if ((currentInput() & 1) == 0) 
           yield return new State("Ready"); 
       else {
           if (...) {
               yield return new State("Expecting more data");
               SendOutput(currentOutput());
               while ((currentInput() & 2) != 0) // while device busy
                    yield return new State("Busy");
           else if (...) { ... } 
       }
    }
}

// consumer:
int data;
var fsm = StateMachine(ReadFromIOPort, () => data);
// ...
while (fsm.Current != "Expecting more data")
    fsm.MoveNext();
data = 100;
fsm.MoveNext();

While this is not a state machine in the classical sense, the article about Iterator-based Micro Threading uses yield creatively for state-based actions.

IEnumerable Patrol ()
{
    while (alive){
        if (CanSeeTarget ()) {
            yield return Attack ();
        } else if (InReloadStation){
            Signal signal = AnimateReload ();
            yield return signal;
        } else {
            MoveTowardsNextWayPoint ();
            yield return TimeSpan.FromSeconds (1);
        };
    }
    yield break;
}

Iterator blocks do indeed implement state machines, but the tricky bit is getting the next input. How are you going to know where to move next? I guess you could have some sort of shared "current transition" variable, but that's somewhat icky.

If you don't need any input (e.g. your state machine is just cycling between states) then it's easy, but that's not the interesting kind :)

Can you describe the kind of state machine you're interested in?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!