サブルーチン

少し長めのプログラムを作っていると、同じような手続きの繰り返しがそこかしこに出てくることがよくあります。このような場合、「サブルーチン」を使うと便利です。サブルーチンは手続きに名前をつけたもので、あたかも新たなSmall Basicの命令が作られたようなイメージで使う事ができます。

例を見てみましょう。このサンプルプログラム自体はあまり意味のあることをやっているわけではありません。

debug=1

GraphicsWindow.Height=600
GraphicsWindow.Width=600
GraphicsWindow.BackgroundColor="darkgreen"
GraphicsWindow.Show()
GraphicsWindow.PenColor="white"

For x=0 to 600 step 40
  For y=0 To 600 Step 40
    DebugOutput()                                   <=== (1)
    GraphicsWindow.DrawRectangle(x,y,30,30)
    y=y+2
    DebugOutput()                                   <=== (2)
  EndFor
  x=x+2
EndFor

Sub DebugOutput                                     <=== (3)
  If debug = 1 Then
    TextWindow.Write("DBG: Time: "+Clock.Time+" ")
    TextWindow.WriteLine("x="+x+", y="+y)
  EndIf
EndSub                                              <=== (4)

(3) の行で新しいキーワード"Sub"を使い、「Sub DebugOutput」という名前が定義されています。この(3)から(4)の"EndSub"まででサブルーチン "DebugOutput"を定義しています。このサブルーチンでやっていることは単純なので見ればわかると思いますが、変数 debug というのが1だった場合に、その時の時刻と変数x,yの値をテキストウィンドーに表示しています。

この手続き(サブルーチン)を実際に使っているのが(1)と(2)の行です。(1)の行が実行されると、Goto 文と似たように処理がDebugOutput() が定義されている(3)に飛びます。そして、Sub とEndSub で囲まれた(3)〜(4)の手続きが実行され、次にこの手続きを呼び出した(1)の行に自動的に戻ってきて次の行の実行を行います。ここがGoto文とサブルーチンの違いで、またサブルーチンの便利なところです。Goto文で同じことをしようとしても、帰り先をラベルで明示的に指定してやらなければいけませんし、また帰り先を呼び出しの度に変更するという事もできません。

もちろんここに(3)と(4)に挟まれた手続きを(1)や(2)の位置にコピー&ペーストでタラタラと書き流してもよいのだけど、それだと更に同じ処理が追加で何か所にも出てくると面倒になってくると思います。例えば、この例では日付は表示せずに時刻だけを表示していましたが、何かの理由で日付も表示したくなったとします。100個所にコピー&ペーストしていた場合、100個所を修正しなくてはいけないし、たぶんそのうちに修正漏れやミスが発生すると思います。サブルーチンを使った場合、たった一か所だけを修正するだけですみます。

ところで試しに上の例の(1)と(2)の行を取り去って、プログラムの最後に一行追加してみましょう。

debug=1

GraphicsWindow.Height=600
GraphicsWindow.Width=600
GraphicsWindow.BackgroundColor="darkgreen"
GraphicsWindow.Show()
GraphicsWindow.PenColor="white"

For x=0 to 600 step 40
  For y=0 To 600 Step 40
    GraphicsWindow.DrawRectangle(x,y,30,30)
    y=y+2
  EndFor
  x=x+2
EndFor
                                                    <=== (A)
Sub DebugOutput                                     <=== (3)
  If debug = 1 Then
    TextWindow.Write("DBG: Time: "+Clock.Time+" ")
    TextWindow.WriteLine("x="+x+", y="+y)
  EndIf
EndSub                                              <=== (4)

GraphicsWindow.ShowMessage("hi", "hi")              <=== (B)

この時、サブルーチンDebugOutoutを呼び出している個所はどこにもありません。ですので、プログラムが実行されてもテキストウィンドーは開かれず、なにもテキストは表示されません。また、プログラムは(B)を実行しメッセージボックスを表示します。
なにを言いたいかというと、プログラムの処理が(A)の位置に達しても(3)から(4)の間のサブルーチンの行はあたかもなにもなかったように無視され、そのまま(B)の処理へ移るという事です。この動きもGoto文とは違いますね。

プログラムを見やすくするためにサブルーチンを使う

さて、前節では同じ手続きが何か所も実行される場合に手続きに名前を付けてプログラムを簡略化するためにサブルーチンを使う方法を紹介しました。しかし、実際には一回しか実行されないような処理にもサブルーチンが使われることが多くあります。これは、まとまった手続きに名前をつけることにより全体のプログラムの流れを把握しやすくするために役立ちます。もう一度先ほどのサンプルプログラムを見てみましょう。

debug=1

GraphicsWindow.Height=600                        <=== (1)
GraphicsWindow.Width=600                         <=== (2)
GraphicsWindow.BackgroundColor="darkgreen"       <=== (3)
GraphicsWindow.Show()                            <=== (4)
GraphicsWindow.PenColor="white"                  <=== (5)

For x=0 to 600 step 40
  For y=0 To 600 Step 40
    DebugOutput()
    GraphicsWindow.DrawRectangle(x,y,30,30)
    y=y+2
    DebugOutput()
  EndFor
  x=x+2
EndFor

Sub DebugOutput
  If debug = 1 Then
    TextWindow.Write("DBG: Time: "+Clock.Time+" ")
    TextWindow.WriteLine("x="+x+", y="+y)
  EndIf
EndSub

Small Basicでグラフィックスプログラミングをやっていくと、このサンプルの(1)〜(5)の処理、つまりグラフィックスウィンドーの体裁を定義するという処理は何度も出てくるパターンのようなものだという事に気づくと思います。

これを、次のように変えてみましょう。

debug=1

InitGraphicsScreen()                 <==== (1)
DrawRectangles()                     <==== (2)

'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Sub InitGraphicsScreen               <==== (3)
  GraphicsWindow.Height=600
  GraphicsWindow.Width=600
  GraphicsWindow.BackgroundColor="darkgreen"
  GraphicsWindow.Show()
  GraphicsWindow.PenColor="white"
EndSub

Sub DrawRectangles                    <==== (4)
  For x=0 to 600 step 40
    For y=0 To 600 Step 40
      DebugOutput()
      GraphicsWindow.DrawRectangle(x,y,30,30)
      y=y+2
      DebugOutput()
    EndFor
    x=x+2
  EndFor
EndSub

Sub DebugOutput
  If debug = 1 Then
    TextWindow.Write("DBG: Time: "+Clock.Time+" ")
    TextWindow.WriteLine("x="+x+", y="+y)
  EndIf
EndSub  

サブルーチンInitGraphicsScreenは画面の初期化を行うだけなので最初に一回だけ実行されます。何度も実行される処理ではないでしょう。また、サブルーチンDrawRectanglesも同様です。でも、敢えてサブルーチン化しているます。このプログラム、実質的には手続きは(1)と(2)の二つの行で定義されていることがわかるでしょうか。このプログラムでやりたかったことのアウトラインは、

  1. 画面を初期化し、
  2. 長方形(正方形)をたくさん描画する

という二つの事だけです。この二つをどう具体的に行うかは、別途考えてもよいことなのです。ある意味、ここではそれぞれの処理を「抽象化」したとも言えます。大きなプログラムを作る場合、処理の大まかな流れを最初に考えて、そのあとディテールを考えていくというトップダウンのやり方をを取るほうがプログラムの設計がしやすいことが多いのです。

例えば50階建のビルの設計をするとして、たぶん最初に行うのは大体の外観をデザインして、それから少しづつ詳細の設計に入っていくと思います。最初からトイレの電気の配線の設計の様なディテールを考える建築士はいないはずです。

また、この事にはもう一つの面があって、処理が抽象化されているので後からそのプログラムを見た人が全体の流れを把握しやすいという事が利点があります。プログラムを見る人は最初に大雑把な流れをつかんでからディテールを見たいでしょう。「ふむふむ、最初に画面を初期化して、その後に長方形を描画するのだな。では、画面の初期化は具体的には、ふむふむこうやっているのか…」と言った感じ。

ところでちょっと補足しておくと、ここで言っている「後からプログラムを見る人」とは別に赤の他人だけとは限りません。「未来の自分」かもしれません。例えばプログラムを作ってから1年もたってからそのプログラムを見直す必要ができたとき、プログラムの詳細まで覚えている人はどれだけいるでしょう。未来の自分は十分に赤の他人と言ってもいいのです。

まとめ

ここではサブルーチンを使う目的として次の二つを紹介しました

  1. 同じような手続きに名前をつけ、ひとまとまりの処理として扱う
  2. 手続きを抽象化しプログラムを見やすくする

実際にはその他の目的でもサブルーチンが使われることがあります。次回は「イベント発生時のコールバック」としての使い方を紹介します。今はなんのこっちゃ分からないと思うけど、心配しないください。

ところでSmall Basicの他のプログラミング言語でもサブルーチンやそれに似た言語機構を持っていて、それらは関数だとかメソッドだとか別の名前で呼ばれることがあります。それらについてはいずれ機会があれば説明したいと思います。