PIC AVR 工作室->TopPage->工作のツボ->簡略化PAD図

簡略化PAD図のお話

オブジェクト指向言語花盛りの昨今ですが、マイコンでプログラムを組むような用途なら何かと便利だろうと思うのでPAD図について纏めておきます。 PAD図といっても、私が使っているのは正式な書式ではなく簡易化してもっと見やすく解り易くしたものです。

gccを使うにしても、c++ではなくマイコンでC言語を使う範疇なら UMLは要りません。 かといってフローチャートじゃぁそもそも構造化プログラミングには不向き、不十分…

そこで、オブジェクト指向全盛の現代でもあえてPADを使う余地があるかな、ということで纏めておきます。

構造化プログラミング

構造化プログラミングとは

いまさら構造化プログラミングについて言及するまでもないと思いますが、一応簡単に整理します。

その昔、コンピュータの言語といえばアセンブリ言語にしても初期のBASICにしても、 goto文やjump命令が付き物でした。これは構造化プログラミング以前のお話。

このgoto文やjump命令は、プログラムの可読性を悪化させたり、メンテナビリティを下げたりということで「悪」とされました。 スパゲティープログラムを量産するとか言われましたね。そしてそれを解消するアイデアとして出てきたのが 構造化プログラミングというもの。

構造化プログラミングに則ると、プログラムは大きくは3つの要素、すなわち「順次処理」「反復処理」「分岐処理」 の3要素で構成することが出来、そしてプログラムを意味のある一塊ごとの「部品」として抽象化し…「サブルーチン」…とすることが特徴です。

この3つの要素と、サブルーチンによる抽象化で、可読性やメンテナビリティーを高めるというのが構造化プログラミングのネライです。

例えばC言語。構造化言語の筆頭としてもよく挙げられると思います。まぁ、C言語にもgoto文が無いわけではないんだけど、 それはループ(反復処理)から特殊な条件で強制的に抜け出すとか例外処理を行うとかそういう特殊な時にだけ用いられるわけで、 基本は構造化プログラミングの3つの要素+「関数(≒サブルーチン)」という要素で構成されています。

マイコンと構造化プログラミング

昨今は、マイコンの開発でもgcc(c++)を使ってAVRやarduinoなどでもオブジェクト指向によるプログラミングがメジャーになってきていますが、 マイコンの場合どちらかといえばc++で提供されたクラスライブラリを「C言語ベースで利用する」ということが多いのが実体でしょう。 とすると、今でもマイコンの「アプリケーション開発」なら十分実用レベルにあるのが「構造化プログラミング」とも言えると思います。 C言語で小規模のプログラムを書くだけならUMLは必要ないでしょうし。

アセンブラなどを使う場合でも構造化プログラミングの考え方自体は重要です。実現すべき機能(全体機能)を機能分割していき、 その各機能単位毎に構造化を適用していくことも可能です。以前私がアセンブラで作ったプログラム( スプライト表示器とか)も、ある程度構造化を盛り込んでおいたので可読性やメンテナビリティーは確保されており、 今でもメンテが可能な状態になっています。

構造化言語を使えば構造化プログラミングが可能か?

最近のプログラム言語なら、まぁどれをとっても構造化プログラミングの考え方自体は取り込まれているでしょう。c/c++然り、VB然り、java然り。

よく、goto文を使わなければ構造化だという言い方もされますが、それでは不十分です。

言語仕様上のシバリは重要ですが、もっと重要なのは「構造化する意義」と「実践」でしょう。 構造化プログラミングに対応した言語であっても汚く解り辛いプログラムを書くことは簡単だし、 一方「構造化」を心がければアセンブラだって可読性は保てるというわけです。 私はアセンブラでプログラムを組む際も、まず最初はPAD図を書いてからコーディングしていくスタイルをよく使います。 こうすると大して手間をかけなくても スパゲティーにならずに済みます。

もう少し言うと、構造化の観点には変数のスコープ管理の話も関係します。

変数などをすべてグローバル定義してしまうと”何のための構造化なの???”ということになってしまいます。 いくら処理ブロックをサブルーチン化して抽象化しても、変数があちこちで更新されるようだと可読性は極めて悪化します。 本当はそのような観点も踏まえて、変数のスコープは最低限の範囲にするなどのシバリを掛けて置く事も構造化プログラミングでは重要です。 余談ですが。

処理を「意味のある塊=部品」に分けて、その部品を組み合わせた全体がプログラム。 そして部品一つ一つの可読性を高めるためにはgoto文を使わないのはもちろん、部品一つ一つの大きさを程よく小さくして見通しを良くし、 さらには変数の局所化なども含めて、サブルーチン1個1個の強度を高める工夫が必要です。

構造化プログラミングを図示する方法

フローチャートとその欠点…自由度が大きいという欠点

プログラムの処理を図示する方法といえば言うまでもなく「フローチャート」が有名ですが、 フローチャートは元々構造化言語が登場するより前に作られたものです。

フローチャート自体を否定する訳ではありませんが、構造化言語の登場によってフローチャートは「過去の遺物」 になってしまったというのが私の感触です。現代のフローチャートは「むりやり構造化言語に対応させた感」はあるんですが、 やはり仕上がりがビジュアル的に美しくないし、なにより書く人によって仕上がりがマチマチになってしまいがちという大きな欠点が指摘されています。

これは、フローチャートで書く際に線や矢印で図形を繋いでいくだけというお作法…「自由度が大きい」ことに由来しているんですが、 これはある意味goto分で好き勝手に処理を飛ばすとプログラムの可読性が低くなることと似ています。

書く人によってマチマチになるということは、品質管理がままならないということにも繋がります。 自由度は適度に低いほうが管理が楽と言うことです。

自由度を狭める…標準化

よく言われる言葉に「プログラムは、書いてから数ヶ月も経つと、書いた本人ですら他人の書いたものにしか見えない」というのがあります。 自分が書いたプログラムですらそれを理解できなくなるのであれば、他人の書いたプログラムなどはなおさらです。

増して大規模開発/大規模メンテともなれば、たくさんの人がプロジェクトに出入りしながら新たに開発を行ったり機能追加をしたり… ということが日常的に行われていきます。その中で品質を確保しつつ安定した効率的なメンテナンスを続けていく場合には、 誰が書いても同じようになるという「シバリ」を掛けておく必要があります。

でなければ、キーマンが抜けた途端にメンテ不能なプログラムが大量生産される…なんてことになりかねません。 物凄い優秀なプログラマ一人のスタンドプレイでは大規模システムは開発/維持できません。大規模システムに限った話ではありませんが。

また、完成してから長い期間稼動させるのであればバグが見つかれば対応しなければいけないし、 仕様変更の要件が挙げられたら、オンタイムで効率的にメンテする必要があります。 人が抜けた途端に維持もメンテもできないプログラムではユーザに言い訳できません。

コーディングの観点についてはこの辺りが「構造化プログラミング」の重要性を示唆している面でもあり、 その図示方法の観点ではフローチャートの陳腐化を示唆しているかと思います。だれが書いても同じフロー図となり、 それを元に書いたプログラムは誰が書いても同じように仕上がる…と。それはある意味、「標準化」の一環とも言えるでしょう。

もちろん、「シバリ」が強すぎればグルグル巻きになって身動きが取れません。ある種のバランス感覚が必要です。 自由度は狭めつつ、利便性は低めない配慮が必要です。

オブジェクト指向に則ることでコードの可読性・再利用性を高めるというのも一つの考え方でしょうが、 今話題に挙げているのは所詮マイコンの中に入りきる規模の話ですからね…。

そこでPAD図

フローチャートは、処理ブロックを線や矢印で結ぶだけで好きなようにつなぎ合わせることが出来ます。 とっつきやすいので、覚えればだれでも簡単に書くことが可能でしょう。しかし「その線や矢印で結ぶ」というのが曲者です。

その点が解消されているのがPAD図というわけ。自由自在に書けない代わりに誰が書いても同じように仕上がる…。 しかもフローチャート並にとっつきやすく、誰が見ても解り易い…。そんな条件を備えているのがPAD図というわけ。 なにしろ、書いたり読んだりするのに苦労してしまうようだと、その時点でフローチャートの代替にはなりえませんからね。

あまりメジャーにはなれませんでしたが、書きやすさなどの実用性はなかなかです。「順次処理」「反復処理」「分岐処理」、そしてサブルーチン化。 これらの要素を誰が書いても同じように仕上がり、しかもルールも扱いも簡単、可読性はフローチャートよりも高い…と。

普通のPAD図の書き方と特徴

まずは普通のPAD図の書き方を例示してみます。PADで使う主な要素は先述のとおり「順次処理」「反復処理」「分岐処理」。 この3つを組み合わせて処理を組み立てていくわけですが、意味のある処理単位ごとに束ねてサブルーチン化(C言語なら関数化)することで 「抽象化」を行い、メイン処理は短くシンプルに「大筋がわかる」ように仕上げることになっています。 構造化プログラミングの旨味を上手く盛り込んだのがPAD図です。

そのことを踏まえると、3要素に加えて「サブルーチン」の要素を盛り込む必要がありますね。 さらに補足をすると、goto文を使わないことがキホンですから各処理は入り口が1個出口が1個ということになります。 つまり、メイン処理だろうとサブルーチンだろうと、各処理の入口・出口は各1個ずつです。それらを踏まえて例示します。

順次処理

順次処理はこの様になります。ポイントは、上に入り口、下に出口があって、その間を直線で結んでいます。 流れは必ず上から下だけ。(フローチャートのように矢印で横や上に流れることは出来ません)

そして、順々に処理していく内容を縦に並べていきます。つまり「上から下に」という1個のルールだけ。シンプルです。

この図の場合、何かデータを入力してそれをプリントアウトする、という処理を行っています。

一般には、既にライブラリとして提供されているようなもの(C言語でいうprintf関数など)はサブルーチン(後述)とは扱わず、 順次処理の1要素としてこの図のように表現することが多いと思います。 ユーザーアプリの中で設計・作成したサブルーチンだけを「サブルーチン」として扱います。

反復処理

この例では、入力したデータを10回繰り返し表示するという処理になっています。

反復処理…C言語でいうfor文やwhile文…は、四角の左側に縦線を1本入れて表現します。 繰り返しの条件はその四角の中に記述し、繰り返される処理の実体はその右に書かれます。 その実体とは、いわゆる順次処理のことです。順次処理の要素には単なる命令群だけでなく、反復処理や分岐処理も含まれます。

この例ではループの中身が1個の処理だけですが、もちろんループの中に複数の処理を組み入れたい場合が一般的でしょう。 その場合は、縦に棒を引っ張って順次処理の要素を縦に書き連ねていけばokです。 (先ほどの順次処理の図から楕円形を除いた部分がループの右側にそのままくっ付くイメージです)

繰り返し処理にしても、分岐処理(後述)にしても、「入れ子」となる処理の場合、必ず「右側」に書きます。 (フローチャートのように、下や左や上など好き勝手な方向に伸ばしてはいけません)

分岐処理

この例では、入力したデータが正数なら”A”を、ゼロなら”B”を、負数なら”C”を表示するという処理になっています。

分岐処理…C言語でいうif文やselect文…は、四角の左側をギザギザの並線にして表現します。 そして箱の中に分岐条件を書きます。ループと同じように、分岐先は右に延ばして記述していきます。「右だけ」です。

当然ながら、それぞれの分岐先には順次処理を組み込むことが可能です。

サブルーチン

実は、先ほどの分岐処理のところの図でサブルーチンの「呼び出し処理」が使われています。「input sub」と言うところです。

書き方としては、四角の左右両側に縦線を入れる…これがサブルーチンコールです。

そして、「呼ばれる側の処理…サブルーチン本体」はというと、

この様に入り口と出口で挟んだ処理一式を記すことで実現します。必要に応じて順次処理、反復処理、分岐処理、 さらにはサブルーチンの呼び出しをここに組み合わせることで構成されます。

PAD図のルールと弱点

PAD図ではこの様に、順次処理は下方向だけに、反復や分岐での入れ子構造は右方向に、という「2つのシンプルなルール」によって、 誰が書いても必ず同じ様に仕上げることが可能になります。 しかも、仕上がりが見づらくなるわけでもなく、書くのが面倒になるというほどのことでもありません。

誰が書いても同じようになるのだから、誰が見ても同じ様に解釈できるというわけ。それがPAD図最大のメリットです。

でもちょっとだけ弱点があります。それはなにか?

1点は、順次処理で書き並べられる処理が四角で囲まれているため、反復処理や分岐処理と似ており見分けが付きにくいということ。

もう1点は、どこからどこまでが反復処理の「反復対象」なのか? そしてどこからどこまでが分岐処理の「条件や被分岐処理」 なのかがパッと見で判別しづらいことです。

PAD図で検索してみたところ、こちらのサイト で解り易い図が公開されているんですが、ページの下のほうで図に青い線で補記されているように、 一つ一つの処理のカタマリが区別しにくくなっていることが示唆されています。

その点を何らかの方法で改善すれば、もっと解り易く、見易くすることが出来るんですが…それが簡略化PADというモノになります。

補足

エラー時に異常終了させるような場合など強制的にループを抜ける時はどうするんじゃい???って質問も出てくるわけですが、 とりあえずgoto文論争の話は他のサイトなどに譲るとしてここでは触れないことにします。

簡略化PADについて

普通のPAD図は、2つの弱点があるわけです。一つは「四角だらけ」で見づらいこと。もう一つは反復や分岐処理の頭とお尻がわかりにくいこと。 これらをなんとかしようというのが簡略化PAD図です。

まずは、先ほどの標準的なPAD図で書かれたものと同じ処理を「簡略化PAD図」で表すとどうなるかを示し、普通のPADとの違いを比べてみます。

順次処理

順次処理はこのように、四角の箱を取っ払って黒丸で表します。黒丸の右にその処理内容を記載します。

黒丸で表すということ以外には大きな違いはありません。処理は常に上から下にのみ。横や上に処理が流れていくことはありません。

反復処理

反復処理は、やはり左側に縦線を延ばすことで表現しています。その線の右側にループ回数など条件を記載しています。

異なるのは、横長の四角を用いないということ。つまり、ループの長さに従って縦に長ーく伸ばして使います。 そして右側の縦線は、これ自体が順次処理を取り込むことが出来るようになっています。例えば二重ループの場合は、 この右側の線の更に右側にループをくっつけて使うわけです。

この二重ループの図の意味について。外側のループは”A”を表示してから内側のループを行い、内側のループはで入力データを5回表示します。 このようにネストは右に右に書き延ばしていけば良いんですが、その際に外側のループ(左側)は内側のループ(右側)より縦長に延ばして描くことにより、 どこからどこまでがループに含まれるのかが一見してわかるようになっているのが特徴です。(分岐処理も同様です)

実は、C言語等などできちんとお作法に従ったインデントをつけてコーディングすると、 プログラムコードのビジュアルがこの簡略化PAD図とかなり近くなることが想像できると思います。

なお反復処理の下側には線を描いていませんが、描いても描かなくてもどっちでもいいと思います。大した問題ではありません。 (メンドウなのと、後述の分岐処理の様式と合わせて省いているだけです)

分岐処理

分岐処理も大差はありません。キザキザの波線で条件分岐を表しています。ただ、波線が右側ではなく左側に寄せて描いてあります。 この様にしておいたほうが何かと便利なのでこうしています。例えば分岐の条件1つ1つを横線で区切れるので明確に分離できるとか、 右側には反復処理のように縦線を描くことによって順次処理を描いていくことが可能になります。

順次処理にしても、反復処理にしても、分岐処理にしても、「上から下に処理する」「入れ子は右方向に」という2つのルールは一緒ですが、 こういう描き方のほうがビジュアル的に解り易いかと思います。いかがでしょう?

1つ1つの処理内容(サブルーチンコール含む)は縦線上に並んだ丸印を上から順々に、 反復や分岐などの”制御構文”は箱型の図形で表す、という描き分けが通常のPAD図との大きな違いです。

なお、「input sub」と描かれている所の二重丸の印はサブルーチンコールを表しています。 C言語でいえば関数の呼び出しがそれに相当します。が、ご存知の通りC言語などでは「関数」として定義した処理ルーチンを、 計算式中でも当然「関数」として組み入れることが多いです。そのような場合はサブルーチンコールというより計算処理(代入処理)の色が濃いため、 あえて二重丸を使わずに計算式全体を黒丸で表すほうが自然でしょう。もともと「関数」という性格上、明確な境目はないともいえます。

サブルーチン

サブルーチン本体側については特に言及不要でしょう。ご覧の通りです。

一般的な構造化の考え方に従い、メイン処理は数個程度のサブルーチンの呼び出しだけで構成して「大枠で何を行うのか」を表現→ 処理の詳細は各サブルーチン側に実装するというスタイルを用いると、構造化ならではの「抽象化」が計れて解り易いコードになります。

以前AVRで作ったドットイーター のC言語ソースは、メインループが6個の関数呼び出しだけで構成されていますが、まぁそんなイメージです。

(このプログラムは変数スコープの局所化のためにmain関数内で色々と変数定義などしていたり、 関数1個1個が長めで見通し悪かったりしてますが…)

というわけで、普通のPAD図でも簡略化PAD図でもどっちでもいいんですが、 フローチャートの代わりにどっちかを使うことをお勧めしたいと思います。 個人的には、規模が大きくなればなるほどその見易さの効果がわかる簡略化PAD図の方がお勧めです。

記述例

サンプル的に簡略化PAD図を書いてみます。

このPAD図は例の赤道儀用のロジックを書き表したモノです。 (縮小表示してます→クリックで別窓に開きます)

アイデアノートに手書きしていたPAD図を清書したものです。ただ、手書きしたPADは初期バージョン用のものだったので、 最新版のソースに合わせて手直しを入れてます。(本当はプログラムに合わせてPADを書くのはダメなんだけどね)

簡単な補足

実はこのPADの書き方は今ひとつなところがあるんだけど、幾つかの点で有意義でもあるのでこれを使うことにしました。

まずダメな点

先にダメダメな部分から。何と言っても、1つのモジュールが長いこと。というか、メイン処理に殆どすべての機能が集約していること。お作法違反。

メイン処理では「大筋」だけを記載しておいて、細かい処理はサブルーチン(C言語なら関数)側で行うというのが「構造化のお作法」なんだけど、 モロにお作法を破ってます。それがダメダメな点。まぁ、PIC用のCCS-Cコンパイラは色々と制約があってそれどころじゃなかったので、 これはこれで一先ずいいことにしました。

注目点その1:処理の全体像と流れ

通常のPAD図と違ってループや条件分岐用の箱が縦長に引き伸ばされて書かれているので、 どこからどこまでがループなのか、どこからどこまでが条件分岐なのか、 多重ループになってもその制御構造が見やすくなっているのが一見してお判りでしょう。

注目点その2:構造化からはみ出る部分

いわゆる非goto派とgoto派の論争になりがちな部分のこと。

ループの途中で特殊な条件の時だけ強制的に外に抜けたりする時、100%構造化プログラミングの作法にならうと無茶苦茶なロジックになり、 返って解り難くなるという例のお話。

今回のPAD図では、「巻き戻しボタン押下?」の判定により強制的にループを抜けさせています。(C言語ではbreak文を使用)

こういうケースや、関数の途中でreturn文を使って関数の呼び出し元に強制的に戻る場合も同様ですね。

で、その手の場合もPAD図(簡略化PAD図)で表現することは一応可能ですよ…ということが書きたかった訳。

厳格な構造化のお作法には反しますが、構造化プログラミングの目的は「お作法に従うこと」ではなく「ロジックの見通しを良くすること」 なので、そのために強制的にループを抜けたりするのは「合目的的」でokだと、個人的には思っています。

注目点その3:割込み処理

このPAD図では、タイマーモジュールを使ったハードウェア割り込みを使用しています。

ハードウェア割り込みは強制的にサブルーチンに処理が移されるので、サブルーチンの呼び出し元をPAD図上に明記することが出来ません。

まぁ、それはそれで致し方ないですね。

呼ばれる先のサブルーチンだけがポツンと書かれているので、なんとなく見通しが悪いのがネックです。 デメリットとして挙げられる点になると思いますが、そういうこともあるってことで頭の片隅に…

なお、ソフトウェア割込みについてはその心配はないでしょう。CPUの「実行権限関係の問題」でしょうから、呼び出し元は明確です。

その他

あくまで処理フローを記述するためのものなので、変数の扱いがここには表現されません。

構造化という観点では、変数のスコープなども重要になってくるんですが、その辺りはPAD図に表現できません。 別途解決手段が必要です。一般には「変数表」などを使うことが多いと思います。

あらためてサンプルを眺めてみると

さて、サンプルのPAD図を1歩引いて眺めてみます。順次処理、反復処理、分岐処理の空間的配置を眺めてみるとどうでしょう…

そう。C言語などのプログラム時にインデントを加味してコーディングすると、丁度似たような感じになることがお分かりでしょう。

普通のPADと比べて、簡略化PADのいいところはこのようにコーディングした時とソックリの見た目になることです。 コーディング時にPADからコード化していく作業がラクになります。

ここまでするなら最初からコーディングしちゃえばいいジャン!と思うかもしれませんが、 「コンピュータ用のコード」で書くのと「自然言語の文章」で書くのではやっぱり見易さが大違い。 一度書いてみると、その便利さは「ははん、なるほど」と思っていただけるかと。

まとめ

普通のPADでも簡易化PADでも良いんですが、構造化に向いていないフローチャートの代わりに使うと便利なのでお勧めです。

ちなみに、サンプルで挙げたPAD図は赤道儀用のものなので、 C言語コードと見比べてみるとまたアレかと。