ここに示すように、yield キーワードを使用して単純なステート マシンを実装することは可能ですか。私には、C# コンパイラが、yield ステートメントを機能させるためにステート マシンを内部的に実装しているため、大変な作業を行ったように見えます。
コンパイラーが既に行っている作業に便乗して、コンパイラーにステートマシンのほとんどを実装してもらうことはできますか?
誰かがこれを行ったことがありますか、技術的に可能ですか?
ここに示すように、yield キーワードを使用して単純なステート マシンを実装することは可能ですか。私には、C# コンパイラが、yield ステートメントを機能させるためにステート マシンを内部的に実装しているため、大変な作業を行ったように見えます。
コンパイラーが既に行っている作業に便乗して、コンパイラーにステートマシンのほとんどを実装してもらうことはできますか?
誰かがこれを行ったことがありますか、技術的に可能ですか?
実現可能ですが、それは悪い考えです。イテレータ ブロックは、ステート マシンの実装に関する一般的な問題を解決するためではなく、コレクション用のカスタム イテレータを記述できるようにするために作成されました。
ステート マシンを作成する場合は、ステート マシンを作成するだけです。それは難しいことではありません。多くのステート マシンを記述したい場合は、ステート マシンをきれいに表現できる便利なヘルパー メソッドのライブラリを記述してから、そのライブラリを使用します。しかし、たまたまステートマシンを実装の詳細として使用する、まったく別のものを意図した言語構造を悪用しないでください。これにより、ステート マシン コードの読み取り、理解、デバッグ、保守、および拡張が難しくなります。
(ちなみに、私はあなたの名前を読んで二重に考えました。C# の設計者の 1 人は、Matt Warren という名前でもあります!)
はい、それは絶対に可能であり、簡単に行うことができます。制御フロー コンストラクト ( for
、foreach
、while
、... goto
(goto
特にこのシナリオに適しています ;))) をyield
s と共に使用して構築することができます。
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();
イテレータブロックは確かにステートマシンを実装しますが、トリッキーなビットは次の入力を取得しています。次にどこに移動するかをどうやって知るのですか?ある種の共有された「現在の遷移」変数を持つことができると思いますが、それはやや厄介です。
入力が必要ない場合(たとえば、ステートマシンが状態間を循環している場合)は簡単ですが、それは興味深い種類ではありません:)
興味のあるステートマシンの種類を教えてください。
これは古典的な意味でのステート マシンではありませんが、イテレータ ベースのマイクロ スレッディングに関する記事では、状態ベースのアクションに yield を独創的に使用しています。
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;
}