コンピュータができることは少ない,
言語を駆使して思い通りに動かそう

講師 矢沢 久雄

プログラミング手法
四則演算,命令の順序,関数

 ここからは,コンピュータがプログラム・コードをどのように実行するかを解説し,正しい命令の与え方を説明する。プログラムは,プログラマがコンピュータに与える命令である。しかし,アラジンの魔法のランプから出てくる大魔人のように,コンピュータが何でも命令を聞いてくれるわけではない。プログラマは,コンピュータができることを理解して,コンピュータができる範囲のことだけを命令しなければならない。では,コンピュータは何ができるのだろうか。

3つのことしかできないコンピュータ

図7●コンピュータができる3つの処理
コンピュータは,入力,演算,出力しかできない
 パソコンで使えるアプリケーション*13の種類には,ワープロ,スプレッド・シート,およびゲームなど様々な種類のものがある。これらのアプリケーションを使っていると,コンピュータは色々なことができると思うかもしれない。しかしそうではない。コンピュータの機能はとてもシンプルだ。コンピュータは,「データの入力」,「データの演算」,および「データの出力」の3つしかできない(図7[拡大表示])。

 CPUは,メモリーに記憶されたプログラムの内容に従って,I/Oからデータを入力し,データの演算結果をI/Oに出力する*14。プログラム自体や,演算の途中結果のデータを記憶するためにもメモリーが使われる。I/Oの先には,キーボードやマウスなどの入力装置,ディスプレイやプリンタなどの出力装置,およびフロッピ・ディスクやハード・ディスクなどの補助記憶装置が接続されている。CPUが行える3つの機能が幾重にも組み合わされて,様々なプログラムの機能を実現している。

 機能が3つしかないと分かれば,プログラミングなど楽なものだと思えてくるだろう。ただし,これはハードウエアのレベルの話である。アセンブラでプログラミングするなら,入力,演算,出力が1行ずつのプログラム・コードとなるが,BASICやC言語などの高水準言語を使った場合は,1行のプログラム・コードの中に,3つの機能が盛り込まれることもあり,やや複雑になる。例えば,A=B+123というプログラム・コードの中では,「Bという変数が表すメモリーの内容をCPUの内部に入力する」,「CPUの内部で123という値を加算する」,「加算結果をAという変数が表すメモリーに出力する」という3つの機能が使われている。

プログラムの構造は,入力,演算,出力の3つ

 コンピュータは3つの機能を持っているため,コンピュータ上のプログラムも,入力,演算,出力の3つの基本的な構造に分けて考えることが重要だ。

 コンピュータは,自ら物事を考えて新しいデータを生み出すことはできない。従って,必ずデータの入力が必要となる。コンピュータは,データのゴミ箱ではない。入力されたデータがコンピュータの内部に捨てられることはなく,演算されてから必ず出力される。コンピュータに入力されたデータをそのまま出力するのでは,何の意味もない。入力されたデータには必ず何らかの演算が行われる。

 プログラムを入力,演算,出力という基本的な3つの機能に分けて考える習慣を身に付けることは大事なことだ。これは,大きなシステム全体を考える場合でも,プログラムの一部分を考える場合でも同様だ。何が入力され,それがどのように演算され,そしてどのように出力されるか,という考え方でプログラミングするのだ。

プログラムには流れがある

 もしもプログラミング経験がまったくない人に「プログラムという言葉から何を想像するか?」と尋ねたら,何と答えるだろう。競技種目や演奏曲目の順序が示された「運動会やコンサートのプログラム」と答えるだろう。プログラムという言葉の意味は,コンピュータの場合でも全く同じだ。コンピュータに実行させることの順序を示したものが,プログラムに他ならない。プログラム・コードの順番は,コンピュータに指示する命令の順番を示している。

 単純なプログラムなら,たった1行のプログラム・コードだけで目的が達成できるかもしれない。だがほとんどのプログラムは,複数行のプログラム・コードから成り立つ。基本的にどのようなプログラム言語であっても,プログラム・コードが記述された順に,上から下に向かってプログラムは実行される。これは,記述された順序でプログラム・コードがメモリーに格納され,CPUがメモリーに格納された順序でプログラム・コードを実行するからである。何はともあれ,プログラムには,川のような「流れ」があることを知ってほしい。

図8●プログラムの流れ
プログラムは何も指定しなければ,先頭行から順次流れていく(実行していく)。条件分岐や繰り返しを指定し,プログラムの流れを制御することも可能である
 川の流れが途中で分岐して複数の支流に分かれていくように,プログラムの流れが途中で分かれることもある。川の流れが途中で渦を巻くことがあるように,プログラムの流れが途中で渦を巻くこともある。プログラム・コードが,記述された順番に流れることを「順次進行」,途中で分かれることを「条件分岐」,途中で渦を巻くことを「繰り返し」または「ループ」と呼ぶ。これら3つの流れの組み合わせで,プログラムを構成する(図8[拡大表示])。

 皆さんは,「フロー・チャート」という言葉を聞いたことがあるだろう。フロー・チャートは,プログラムの設計図の一種である。フロー・チャートのことは,「流れ図」とも呼ぶ。その名が示すとおり,プログラムの流れを設計したものである。プログラマがプログラムを設計する時には,「ここでまっすぐ流れて,ここで分岐して,ここで繰り返して...」といった流れを常にイメージしている。

 順次進行だけのプログラムで目的の結果が得られることもあるが,条件分岐や繰り返しを使った方が,効率的なプログラミングが実現できることが多い。例を挙げよう。「1から10までの整数をディスプレイに出力する」というプログラムを作成するとしよう。単純に考えると,

Print 1
Print 2


Print 10

となる。このプログラム・コードは,図9[拡大表示]のように,繰り返しを用いて記述することもできる。図9の方が明らかにプログラム・コードの量が少ない。このように,繰り返しや条件分岐を用いた方が,効率よくプログラミングできるものなのだ。

コンピュータに無駄な処理をさせない

図9●繰り返しを用いて1~10までの整数を出力する
 コンピュータが,繰り返しと条件分岐の命令をどのように解釈するかを知っておくことは重要だ。コンピュータの動きを知れば,実行速度の高いプログラム・コードが記述できるからだ。

 まず,図9のプログラム・コードで使っているFor~Nextを例に,繰り返しの仕組みを説明しよう。For i=1 to 10 Step 1の部分は「変数iの値を1から10まで1ずつ増加させろ」という命令で,Next iの部分は「変数iを次の値にせよ」という命令だ。For~Nextで囲まれたPrint iの部分は,iの値の変化と共に10回実行し,1~10までの整数をディスプレイに出力する。

 重要なのは,10回で繰り返しを終える仕組みである。その仕組みは,プログラム・コードを見ただけではなかなか読み取れない。10回繰り返したかどうかを判断するには,変数iの値を常に監視する必要がある。コンピュータは,図9のプログラムを以下のように実行する。

・変数iに1を格納。10を超えていないからOK。Print iを実行。
・変数iに2を格納。10を超えていないからOK。Print iを実行。
   ・
   ・
・変数iに11を格納。10を超えたからNG。Next iの次の行に流れよう。

といった具合だ。繰り返し処理をコンピュータがどのように実行するか,ご理解いただけただろうか。

図10●実行速度の遅いプログラム
a*2の演算を毎回行うため実行速度が低くなる
 では次に,図10[拡大表示]のプログラム・コードの問題点を指摘して欲しい。これは「変数aを2倍した回数の繰り返しを行う」というものである。For~Nextの中で行う処理は省略する。図10のプログラムは,正しく動作する。しかし,コンピュータは繰り返しの終了判定を行うたびに,変数aを2倍する,という処理を行っている。変数aの2倍は,いつでも同じ値である。だから,毎回2倍して結果を求める処理は無駄である。こういう小さな無駄が積もり積もって,プログラムの実行速度は低下してしまう。コンピュータは,プログラムで命令された処理を真面目にこなす。無駄があろうが,間違っていようが,プログラム・コードの通りに実行する。プログラム・コードを短く効率的に記述することも大事だが,もっと大事なのはプログラムの実行速度を高くすることだ。プログラマは,常に,無駄な処理をさせていないか,を意識してプログラム・コードを記述しなければならない。

図11●実行速度の速いプログラム
a*2の演算を1回しか行わないため実行速度が高くなる
 図11[拡大表示]は,図10のプログラムを改良し,実行速度を向上したプログラム・コードである。繰り返しの中で変わらないa*2という計算の結果を変数bに格納し,変数bを使って繰り返しの終了を判定している。変数bに値を格納する処理の分だけプログラム・コードは長くなるが,実行速度は格段に高くなる。このように,プログラム・コードを短くすることと,実行速度を高くすることは両方同時に満たせない場合もあるのだ。

条件分岐の仕組み

 次は,条件分岐の仕組みを説明しよう。条件分岐とは,コンピュータに条件の判定を行わせ「もし条件が成立すればAという流れ,そうでないならBという流れ」というように流れを分岐させることだ。BASICではIf~Then...Elseという構文を使って条件分岐を表すのが一般的だ*15。例えば,変数aに格納した数値が100より小さいかどうかで処理を分岐し,異なるメッセージをディスプレイに表示するプログラムは,図12[拡大表示]のようになる。

図12●条件分岐の例
 If a < 100 Thenの部分で,aが100より小さいかどうかを判定している。コンピュータが判定を行う仕組みはとてもシンプルだ。この場合には,aから100を引いた結果がマイナスになったらaが100より小さい,と判断する。

 IfとThenの間にあるa < 100が調べる条件である。数値を処理するコンピュータは,条件が成立するかどうかも数値で表す。BASICでは,条件が一致しないことを0,条件が成立することを0以外の値としている*16。このような条件成立を表す数値を知っていれば,条件部分のプログラム・コードを省略して記述することも可能だ。

 例えば,ベテランのプログラマは,If a Thenのようなプログラム・コードを記述することがある。この場合,条件に相当する部分は「a」だけであり,初級のプログラマにはプログラム・コードの意味が理解できないかもしれない。条件成立を0か0でないかで表すのだから,If a Thenは,If a <> 0 Thenと同じ意味である*17。「もしもaなら」と言うのは,「もしもaが条件成立を表す数値なら」と言い換えることができる。それなら,意味的に分かりやすいIf a <> 0 Thenと記述した方がよいと思われるかもしれない。だが,プログラム・コードを短くしたいと願うベテラン・プログラマの中には,If a Thenというスタイルを好む人も多い。これは,純粋に好みの問題だ。

数学と共通点が多いプログラム

 プログラムは,数学と同じように「変数」や「関数」を使う。これは,数学とプログラムには,数値だけを取り扱う,という共通点があるからだ。コンピュータはコード化することで情報を何でも数値で表し,数値を演算することで様々な機能を実現する。数値を格納する場所は,物理的にはメモリー上のどこかである。数値を格納したメモリー上の位置を,変数で名前を付けて表す。関数は,数値を処理する方法に名前を付けたものである。数学とプログラムは,共通点が多い。

 中学生の数学の関数は,y=f(x)という構文になっている。これは,変数xにfという処理を行うと,結果がyに格納されることを表している。プログラムの関数も基本的に同じである。y=MyFunc(x)というプログラムの場合,変数xにMyFuncという処理を行うと,結果を変数yに格納する。この場合,変数xを「パラメータ」,MyFuncを「関数」,変数yに格納する値を「戻り値」と呼ぶ。y=MyFunc(X1,X2,X3)のように複数のパラメータをとる場合もある。

 コンピュータの「データの入力」,「データの演算」,および「データの出力」の3つの機能にも,関数はよく当てはまる。パラメータが入力,関数が演算,そして戻り値が出力を表しているからである*18

関数の組み合わせでプログラムを作る

図13●InStr関数の構文
 再利用される可能性の高い関数は,プログラム言語によって提供される。「標準関数」と呼ばれる関数がそれだ。標準関数の構文は,プログラム言語のマニュアルに明記されている。例として,BASICのInStr関数の構文を図13[拡大表示]に示した。InStr関数は,文字列の検索機能を提供する関数である。パラメータは文字列データであるが,これも実際には文字コードで表した数値である。BASICのAsというキーワードは,データ型を指定するものである。2つのパラメータのデータ型はString型(文字列型),関数の戻り値のデータ型はInteger型(整数型)となっている。変数や戻り値のデータ型を示さなければならないのは,数学の関数とプログラムの関数の唯一の違いである。これは,データ型を定めないと,データを格納するために,何バイトのメモリーを確保すればよいかが分からないからである。

 InStr関数は,i = InStr(a, "ABC")のように使うことができる。この場合,変数aの中に格納した文字列の中から,“ABC”という文字列を探し,見つかった位置を変数iに格納する。関数のパラメータには,変数を与える場合もあれば,直接数値を与える場合もある。

図14●4つの関数で構成されたプログラムの例
 目的の機能を提供する標準関数が無い場合,プログラマが新しい関数を作ることができる。いずれにしても,プログラムは,いくつかの関数の集合体として構成されることになる。図14[拡大表示]は,Main,InStr,MyFunc1,およびMyFunc2の4つの関数から構成されたプログラムである。InStrはBASICの標準関数。残りの3つの関数は,プログラマが作成した関数である。プログラムの起動時にはMainが呼び出され,Mainの中でInStr,MyFunc1,およびMyFunc2を使っている。MyFunc1の中でもMyFunc2を使っている。MyFunc1やMyFunc2を関数とせず,Mainの中に直接記述することも可能だ。しかしそれでは,MyFunc1やMyFunc2と同じ機能が必要な場合,再度同じプログラム・コードを記述しなければならない。関数にしておけば,プログラム・コードを再利用できるのだ。

 プログラムの生産効率を向上させるには,プログラム全体を再利用可能ないくつもの関数に分けることが重要だ。関数に分けなければ,プログラム・コードの切れ目が不明確になり,一まとまりのプログラム・コードが長く,読みにくくなるという問題も生じてしまう。小さな関数に分けたプログラムなら,全体の構造が見わたしやすい。1つの関数を記述したプログラム・コードの長さは,プリンタ用紙1枚に収まることを目標にしてほしい。

まとめと次回予告

 プログラム言語の種類やプログラミング手法の基本的な考え方を説明した。十分にご理解いただけただろうか。どんなに高度なプログラムであっても,内部は単純な命令の組み合わせである。プログラマは,寝ても醒めても頭を悩ませ続け,目的の動作をコンピュータに実行させようとする。プログラミングは,はっきり言ってつらい作業だ。しかし,プログラムが完成し,コンピュータが思い通りの動作をした時は,拍手をしたくなるような感激がある。それがあるからこそ,プログラミングが楽しいものになる。

 次回は,「OSとアプリケーション」というテーマで,OSの役割と,アプリケーションからOSの機能を利用する方法などを説明する予定である。