ごく簡単なプログラムを書く場合を除いて、プログラムのコーディング前に、十分な設計を行っておく必要がある。状態遷移図と状態遷移表は、プログラムの状態変化を視覚的に記述することで、設計を簡単化したりバグの発生を抑えることに非常に効果的。
[図 状態遷移図の構成要素]
状態を定義するルールは、
待ってる「イベント」が異なる場合(=役割が異なる処理)、状態を分ける。 |
となる。
以下に、「ストップウオッチ」を例に、状態遷移図を説明する。ストップウオッチの構成として、以下の仕様を考える。
① ストップウォッチには「スタート」「ストップ」「リセット」の3つのボタンがある ② 「スタート」ボタンを押下すると計測を開始する ③ 「ストップ」ボタンを押下すると計測を一時停止し,計測結果時間を表示する ④ 結果表示中に「スタート」ボタンを押下すると計測を再開する ⑤ 結果表示中に「リセット」ボタンを押下すると計測結果時間をリセットする |
※状態をステートと呼ぶこともある。
これを、状態遷移図に置き換えると、以下のようになる。
[図 ストップウオッチを例にした状態遷移図]
スタートボタンを押した | ストップボタンを押した | リセットボタンを押した | |
待機中 | 計測中 | ― | ― |
計測中 | ― | 一時停止 | ― |
一時停止 | 計測中 | ― | 待機中 |
[図 ストップウオッチを例にした状態遷移表]
※ "―"は、状態遷移図では表現できていない遷移
状態遷移表は、上段にイベント、左の欄外に現在の状態、表中には、イベントが発生した時の遷移先を記入する。
仕様書には、遷移する状態しか書かれていない(遷移しない状態は省略されている)ので、状態遷移表を使って遷移しない状態を明らかにすることで、バグの発生を防ぐ。
この様に「状態遷移図」と「状態遷移表」は守備範囲が異なる。それぞれの特性を良く理解して、設計に活用する事。
上記の状態遷移図で表現されるように、一定の条件で状態を変化させながら動くシステム(機械)を状態遷移機械、またはステートマシンと呼ぶ。
※
ここで取り上げたのは、状態の変化が、現状態と入力に依存するミーリ型ステートマシン。このほかにも、状態の変化が現状態のみに依存する、ムーア型ステートマシンがある。
状態遷移図とコーディング
以下に、ストップウオッチの状態遷移図をベースにプログラムを作成する手順を示す。
(1) 状態をdefine文で、文字列に割り当てる。(プログラムを見易くするため)
状態遷移図で表現される状態は3つあるので、
として、それぞれdefine文で定義する。(別にdefineでなくてもenumでも良いが)
(2) 状態変数を宣言する。型は何でもよいが、int型にするのが適当と思われる。ここでは、
int state; |
として、宣言。
(3) 状態変数の初期値を設定。
(4) while文で無限ループ。(別にforでも構わない。お好みで)
(5) switch ~ case文を使って、状態を列挙する。ここで、default以下の文は、必ずしも必要ない。気休めの様なもの。
(ハードウエア設計の場合は、こういう記述があった方が良い。システムに高い信頼性を求める場合に必要となる)
(1)~(5)をCで記述すると、以下のようになる。
/* 状態を定義 */ while (1) // 状態を無限に回るので。
|
あとは、case文で示された状態が、入力条件によって、どの条件に変化するかを記述する。
また、その時の出力も同時に記述する。
例えば、待機中(S_WAIT)は、スタートボタンが押されることで、計測中(S_MASURE)に遷移するので、
case S_WAIT : |
のように記述する。
ここで、各状態でのアクション(出力)を以下の様に定義する。
init_time() ストップウオッチの初期化 (時刻を0にする) disp_time() 計測した時刻を表示 start_time() 計測を開始 stop_time() 計測を一時停止 |
以上を踏まえたうえで、他の状態も埋めてみる。
int main() case S_MASURE : case S_PAUSE : default : // 必ずしも必要ない。ただ、万が一、誤動作 return 0; |
状態遷移の考え方を使うと、複雑な順番に従ったシステムを容易に表現できる。例えば、
条件1が成立する場合:
処理Bは、処理Aと処理Cを順に実行した後に、実行する。
条件1が不成立の場合:
処理Bは、処理Cと処理Aを順に実行した後に、実行する。
とか。