Headline About TechLog Download Java VBA Link


April 20, 2008. Sunday.

プロシージャ

プロシージャを英語で表記すると「procedure」。直訳すると、手順や手段という意味になります。VBAにおけるプロシージャとは「ある機能を実現する処理の手順をまとめ、再利用しやすくした物」の事です。

例を挙げましょう。プラスチックパイプを5本使って、楽器を作ります。楽器を作るのに必要なプラスチックの量を計算してみましょう。パイプの厚さは全て1mm。直径は順に20mm, 18mm,16mm, 14mm, 12mm。長さはそれぞれ、200mm, 180mm, 160mm, 140mm, 120mmとします。プロシージャを使わずに計算するとプログラムは以下のようになります。(なお、長さ・太さは適当ですので実際にこの長さで楽器を作らないように(笑)。音階にするには太さが同じの場合、1オクターブで長さが半分でしたっけ??)


図1. 製作する楽器のイメージ.

Function calculateTotalVolume() As Double
  Dim total As Double '体積の合計
  total = 0 '初期化
  total = total + (((20 / 2)^2 * 3.14) - (((20 - (1 * 2)) / 2)^2 * 3.14)) * 200
  total = total + (((18 / 2)^2 * 3.14) - (((18 - (1 * 2)) / 2)^2 * 3.14)) * 180
  total = total + (((16 / 2)^2 * 3.14) - (((16 - (1 * 2)) / 2)^2 * 3.14)) * 160
  total = total + (((14 / 2)^2 * 3.14) - (((14 - (1 * 2)) / 2)^2 * 3.14)) * 140
  total = total + (((12 / 2)^2 * 3.14) - (((12 - (1 * 2)) / 2)^2 * 3.14)) * 120

  calculateTotalVolume = total
End Function

酷く見づらいですね。これをみて「凄くわかりやすい!最高!ばっちりじゃん」という人は、頭の回転は良いのかも知れませんが、プログラマとしては問題です。そう言えば、前回の「演算子の評価順について」で「長い式は分割するべきです」と書きました。ためしにやってみましょう。


Function calculateTotalVolume() As Double
  Dim largeCircle As Double 'パイプが詰まっていると仮定した円面積
  Dim smallCircle As Double 'パイプの穴の円面積
  Dim areaOfBase As Double 'パイプの底面積
  Dim aVolume As Double 'パイプ一本の体積
  Dim total As Double '体積の合計
  total = 0 '初期化

  largeCircle = (20 / 2)^2 * 3.14
  smallCircle = ((20 - (1 * 2)) / 2)^2 * 3.14
  areaOfBase = largeCircle - smallCircle
  aVolume = areaOfBase * 200
  total = total + aVolume

  largeCircle = (18 / 2)^2 * 3.14
  smallCircle = ((18 - (1 * 2)) / 2)^2 * 3.14
  areaOfBase = largeCircle - smallCircle
  aVolume = areaOfBase * 180
  total = total + aVolume

  largeCircle = (16 / 2)^2 * 3.14
  smallCircle = ((16 - (1 * 2)) / 2)^2 * 3.14
  areaOfBase = largeCircle - smallCircle
  aVolume = areaOfBase * 160
  total = total + aVolume
   :
   :
   :
  calculateTotalVolume = total
End Function

わぁ、式の意味が良く分かるようになって...って、その分、めちゃくちゃ長くなって読む気がうせるわ!!baristaの嘘つきめが!という声が聞こえてきそうですね。一つ一つの計算の意味は判りますが、ものすごくコードが長くなってしまいました。余りに長いので途中で書くのをやめたぐらいです(オイ)。じゃぁ、前の方がいいのかというとそういうわけではなくて、ここでプロシージャの登場です。一本のパイプの体積を計算する機能をプロシージャとして定義してみましょう。


Const PAI As Double = 3.14

Function calculateTotalVolume() As Double
  Dim total As Double '体積の合計
  total = 0 '初期化

  total = total + getVolumeOfPipe(20, 200, 1)
  total = total + getVolumeOfPipe(18, 180, 1)
  total = total + getVolumeOfPipe(16, 160, 1)
  total = total + getVolumeOfPipe(14, 140, 1)
  total = total + getVolumeOfPipe(12, 120, 1)

  calculateTotalVolume = total
End Function

Function getVolumeOfPipe(diameter As Double, height As Double, thickness As Double) As Double
  Dim aVolume As Double 'パイプ一本の体積
  Dim largeCircle As Double 'パイプが詰まっていると仮定した円面積
  Dim smallCircle As Double 'パイプの穴の円面積
  Dim areaOfBase As Double 'パイプの底面積

  largeCircle = (diameter / 2)^2 * PAI
  smallCircle = ((diameter - (thickness * 2)) / 2)^2 * PAI
  areaOfBase = largeCircle - smallCircle
  aVolume = areaOfBase * height

  getVolumeOfPipe = aVolume
End Function

どうでしょう。随分分かりやすくなったのではないでしょうか。もっと綺麗にするためには、円の面積を計算する部分を、別のプロシージャに切り出してもいいかもしれませんね。まぁ、実際にはcalculateTotalVolume()の中の記述は固定ではなく、Excelシートなどから値を取得して、getVolumeOfPipe()を呼び出すよう実装するのが普通ですけど。このようにある繰り返して実行する機能を切り出したものをVBAではプロシージャと呼びます。また、このようにプロシージャに切り出すことで、プログラムをメンテナンス性・可読性の高いものにする事を「構造化を図る」などと呼称します。

なお、実際にExcelで入力した値を計算できるように、上記のソースを編集したサンプルソースをこちらに準備しました。そのままクリックすると実行されてしまうので、右クリックで保存してみてください。




SubプロシージャとFunctionプロシージャ

VBAのプロシージャには2種類あります。SubプロシージャとFunctionプロシージャです。両者の違いは大きく分けて2つ。(1)Subは戻り値を返すことがでませんが、Functionは戻り値を返します。(2)Subは通常Callをつけて呼び出しますが、FunctionはCallをつけずに呼び出します。以下に簡単な利用例を示します。


Sub test()
  Dim answer As Integer
  answer = sumTwoInteger(1, 2)
  Call sayAnswer(answer)
End Sub

Function sumTwoInteger(n As Integer, m As Integer) As Integer
  Dim returnValue As Integer
  returnValue = n + m
  sumTwoInteger = returnValue
End Function

Sub sayAnswer(answer As Integer)
  MsgBox "答えは " & answer & "です"
End Sub

ではどんな時に使い分けるのかというと、何かの操作をまとめて実現したい時にはSubプロシージャを、計算結果などを取得したい場合にはFunctionプロシージャを用いるのです。「えぇ、よくわからないよ...」という方は、取り合えず全部、Functionプロシージャを使ってみるのも手です。「えぇ〜」という声が聞こえそうですね。余所の真面目なサイトなら許されないような発言ですが、普通に使う分にはそれで特に問題ありません。いろいろなプログラムが組めるようになってから、両者の違いを勉強すれば十分事足ります。




Sub プロシージャの構造

Subプロシージャの構造はとても簡単です。必要なのはプロシージャ名と引数のリストだけ。あとはやりたい処理をプロシージャ内に書きます。

下記の例を見てください。Sub とEnd SubがSubプロシージャの開始と終了を表します。sayHelloはプロシージャ名です。他のプロシージャや変数の名前と見分けのつく名前なら、好きな名前を付けてかまいません。ただし、機能が一目でわかる、分かりやすい名前を付けましょう。()の中のmessageは引数です。プロシージャを呼び出す際に渡したい値に名前を付けた物です。As Stringという表記は引数の型の宣言です。messageという引数の型が文字列型であることを宣言しています。


Sub sayHello(message As String)
  MsgBox message
End Sub



Function プロシージャの構造

次はFunctionプロシージャです。と言ってもSubプロシージャとの違いはそうありません。一つの違いは、さきほど「Sub」と書いていたところに、代わりに「Function」と書いている箇所です。次の違いは戻り値の型の宣言です。以下の例を参照下さい。Integer2つを足した結果をIntegerで返すプロシージャなので、1行目の最後に「As Integer」と書いてあります。

一番の大きな違いは戻り値の指定です。下記の例ではプロシージャ内の最後にgetSum = totalという記述がありますね。Functionプロシージャでは、プロシージャの最後で、プロシージャ名の変数に値を代入することで戻り値を指定します。


Function getSum(num1 As Integer, num2 As Integer) As Integer
  Dim total As Integer
  total = num1 + num2
  getSum = total
End Function



今回の記事はちょっと分かり難かったでしょうか。プロシージャに関する理解は何よりも慣れなので、サンプルソースをいろいろと改造して遊んでみてください。コツはサンプルそのままではなく、少しでも良いから改造して作ってみる事です。