フリーソフトの小道   

コンピュータ・プログラマー養成講座(入門編)
   第2回 プログラムの構造化とはどんなもの?


プログラム作成前に仕事の構造を分析を

 プログラムの初心者が突き当たる壁はどこにあるのか、 自己流プログラマの多くは限界を感じて真のプログラマにはなれないのだ。 入門書などを読んで、練習用の短いプログラムを作ることは誰にでも出来る。 木片を削って小さなおもちゃを作るのに小学生が難渋することはない。 しかし、同じ木材を使って家を作るとなると道具は揃っていても家は完成しないのだ。 プログラム作成においても同様のことに突き当たる。なぜそうなるのか、 ここに素人とプロの建築家の間で仕事に対する考えの違い出てくる。 大工さんは家を作ろうとするのでなく、柱1本、棚板1枚、床板1枚など、 家を構成している部品一つずつを作る作業をするのだ。それらを組み立ててゆくことで大きな家が完成する。 小さな部品を一つずつ作る仕事に分解する作業、これが家の建築の場合では「設計図」というものだ。 では、設計図を作成することは素人にできるか?ここにプロへの壁が存在する。 コンピュータプログラム作成でも同様のことが言える。

 プログラム練習用の小さなプログラムが作れるようになったのに、実用的な大きなプログラムを作れない。 スケールが大きくなるときに、どのように対処すればよいのか、ここに「構造化」という視点が重要になる。 「大きなものも小さなものの組み合わせ」で出来ている。 だから、対処の仕方さえ心得れば、大きなものを作り上げることに、それほど困難は生じないはずなのだ。 大きな仕事を小さな仕事に分解し、小さな仕事の集まりとして組み合わせる。 この作業が「プログラムの構造化」なのだ。全体を見通して、作業内容を分解する。 作業内容をバラバラにするのではなく、共通作業で処理可能な部分の切り出し、 作業の時系列による作業群の分離など、構造化への目の付け所は多い。 この構造化が適切であれば、プログラム作成作業において飛躍的な効率アップ、 プログラムの不良(バグ)の発見、修正など多くの効用が浮かんでくるのだ。

プログラム構造化を体験する  〜 素数判定プログラムの作成 〜

プログラムの処理を分解

 数学に「素数」という用語がある。素数とは1とその数以外では割り切れない整数のことだ。 5は素数、8は素数でないのはすぐ分かるが、1231は素数だろうか?素数でないのだろうか? では、任意の整数を入力し、それが素数かどうかを確かめるプログラムを作ってみることにしよう。 プログラムの構造を考えると、次のようになるだろう。

  1. 確かめるべき整数の入力を受ける部分
  2. 入力された整数が素数かどうか確かめる部分
  3. 判定結果を出力する部分

 以上の3つの大きな部分に分かれるが、それぞれの部分はもっと小さな部分から構成されているのだ。。

整数の入力を処理する

 最初の項目1の「入力を受ける部分」を更に細部まで分解してみよう。次のようになるだろう。

  1. 素数を判定してほしい整数の入力を促すように画面に表示する部分
  2. 入力を受ける部分
  3. 入力されたものが整数かどうか判定する部分
  4. 入力されたものが不正である場合、不正であったことの表示と、再入力を要求する部分
  5. 正しく入力された文字列を数値に変換し、次の処理に渡す部分

 項目3の「整数かどうかを判定する部分」がなぜ必要なのだろうか? 整数の変わりにABCと入れてみればすぐに分かります。エラーが出てプログラム処理が止まるからです。 それを許すなら無くてもいいのですが、手抜きと言われても仕方ありません。
 ここまで分解すると、項目3の整数かどうかを判定する部分以外は素人でもプログラムが作れるようになりました。
 構造化をはかる上では、整数かどうかを判定する部分パックした形にしましょう。

 BASIC言語で記述してみると、項目1、2の部分は次のようになります。

1000PRINT "素数判定してほしい整数を入れてください→ ";   ' 項目1
1010INPUT S$ ' 項目2(文字変数 S$ に文字列として入力)

 C言語で記述してみると、項目1、2の部分は次のようになります。

char s[10];/* 変数宣言 */
  printf"素数判定してほしい整数を入れてください→ ";   /* 項目1 */
  gets(s);/* 項目2(文字変数 S$ に文字列として入力)*/

 PASCALで記述してみると、項目1、2の部分は次のようになります。

var s: string;{ 変数宣言 }
  write('素数判定してほしい整数を入れてください→ ');   { 項目1 }
  readln(s); { 項目2(文字変数 s に文字列として入力)}

 入力処理部分の項目1、2はプログラム・コード1行のレベルに分解できたのでプログラム記述コードは容易だ。 しかし、項目3の「整数かどうかを判定する」部分はまだ、分解できていない。

整数かどうかを判定する

 入力された文字列が整数を表すかどうかの判定はどのようにすればよいのだろうか。 入力された文字列が数字だけを含んでいることだ(小学生でも分かることだから説明は要らないだろう)。 受け取った文字列が数字だけであることを判定処理を記述する部分を更に分解してみよう。

  1. 受け取った文字列の文字列の先頭から「文字を取り出す」部分
  2. 取り出した文字が「0」から「9」であるかを判定する部分
  3. 「0」から「9」以外であれば判定は「否」として終了
  4. 「0」から「9」であれば、次の「文字を取り出し」判定作業を繰り返す

 文字列から文字を取り出す操作を行う関数はどのプログラミング言語にも備わっている。 これを使えば任意の文字列から1文字を切り出すことができる。 繰り返しの作業なので、プログラムの実行繰り返しの記述のテクニックが必要となる。
BASICでは、[FOR、NEXT]、[WHILE、WEND]などの制御文が使える。
PACALでは、[for next]、[repeat until]、[while do]などがある。
 言語の仕様書を読めばどのようなプログラム制御関係の記述が使えるか、 どの言語も必ず備わっている機能なのだが、各言語間に微妙な差異が見られるので注意が必要だ。

文字列を数値に変換する

 文字列を数値に変換するのは10進数の仕組みを知っておれば簡単だ。 その手順を示してみよう。

  1. 先頭の数字文字を1つ取り出す。これを数値に直し、結果をいれる変数(Nとしておこう)に代入する。
  2. 次の文字があれば、次の数字文字を取り出し、これを数値に直し、Nを10倍したものに加える。
  3. 上の処理を次の文字がなくなるまで繰り返す。

 本当にそのような処理で出来るのでしょうか?試してみよう。入力された文字が「1234」だとしましょう。

  1. 始めに、先頭の文字は「1」だから、N には 1 が入ります。
  2. 次に、まだ文字が残っているので、次の文字「2」の処理に入ります。
  3. 「2」だから数値は 2 、N の値 1 を10倍して 10、それに数値 2 を代入すると、N は 12 になります。
  4. 次に、まだ文字が残っているので、次の文字「3」の処理に入ります。
  5. 「3」だから数値は 3 、N の値 12 を10倍して 120、それに数値 3 を代入すると、N は 123 になります。
  6. 次に、まだ文字が残っているので、次の文字「4」の処理に入ります。
  7. 「4」だから数値は 4 、N の値 123 を10倍して 1230、それに数値 4 を代入すると、N は 1234 になります。
  8. 次の文字がなくなったので処理は終了です。したがって、N の値は 1234 になっています。

 入力された「1234」が数値 1234 に見事に変換されたでしょう。 実際のコンピュータでは直接数値しているわけではありません。キーボードのキーを押されただけですから、 文字を受け取っているだけなのです。これを数値に直し計算処理に使っているのです。

数値が素数かどうかの判定

 素数の判定は「1と自分自身のみで割り切れる自然数」というルールを確かめればよい。 1と自分自身で割り切れるのは当たり前だから、2 から 自分自身の数値-1 で割り切れるかどうかを判定すればよい。 その手順としては次のような手順が考えられる。

  1. 最初、割る数を 2 とする
  2. 判定しようとする数を割る数で割り、余りが出るかどうかを調べる。
  3. 余りがでなければ(割り切れれば)「素数ではない」の判定が出せるから、この処理を終えてよい。
  4. 余りがあれば(割り切れなければ)、割る数に 1 を加える。
  5. 割る数が判定しようとする数になれば「素数である」ので処理を終える
  6. 割る数が判定しようとする数未満であれば 処理2 に戻り繰り返す。

 上の処理には無駄な部分がたくさん入っているので改良が必要だが、簡単にするためにはこれで済ますことにする。

実際に作ってみました  〜 PASCAL(Delphi)の場合 〜

 PASCALで実際に記述して見ました。使用したものコンパイラははDelphi6というフリーソフトです。 必要な人は、この Borland サイト からダウンロードできます。 Windowsで動くソフトですので、GUIでのアップリケーションになるので、そのための少し複雑な手続きが必要です。 まだまだプログラムとして改善すべき点がたくさん残っています。

procedure TForm1.Button1Click(Sender: TObject);
const
  numberstr='0123456789';
var
  s,c,sol: string;
  f: boolean;
  i,n,p: integer;
begin
  {{文字列入力処理}
  s:=Edit1.text;   {入力欄から入力された文字列を取り出す}
  {{整数判定処理}
  n:=0; f:=true;   {初期値設定}
  for i:=1 to length(s) do begin
    c:=copy(s,i,1);   {1文字取り出す}
    p:=pos(c,numberstr);   {'0123456789'の何番目にあるか、無ければ 0 を返す}
    if ( p > 0 ) then begin f:=false; break; end;   {無ければ処理をやめる}
    n:=n*10+p-1;   {数値に加算}
  end;
  if f then label3.Caption:='入力OK' else label3.Caption:='入力に誤りあり';   {入力チェック結果表示}
  str(n,s); Edit1.Text:=''; Edit1.Text:=s;   {変換した数値を表示}

  {素数判定処理}
  if f then begin   {入力チェックでエラーが無ければ次の処理をする}
    for i:=2 to n-1 do begin   {自分より小さな数値全てで割り算してみる}
      if ( n mod i )=0 then begin f:=False; str(i,sol); break; end;   {余りが無ければ割り切れた}
    end;
    if f then label3.Caption:='素数だ!' else label3.Caption:=sol+'で割り切れる';   {結果を表示}
  end;
end;

 実際に動かして見たい人は ダウンロード して使ってみてください。まだまだ改善する部分が残っているが、 このコーナーの趣旨にはないのでこれ以上の手を加えないものとします。  管理人(志)



このページは2003/12/28に作成されました。