Microsoft Access2003

Page 5 (2005/01)

モジュールとプロシージャ
ひさしぶりにAccessを触ってみて、相変わらず意味不明な用語に辟易しました。
さほど高度な使い方でもしないかぎり、目先のハウツーだけを理解していればいいのですけど、抽象的な概念の難しさ、わかりにくさはなんとかなら無いものだろうか?
オブジェクト指向とか構造化言語などという概念はハウツー本の分野ではないので、いずれも曖昧にしか解説されていません。

例えばプロシージャという用語にしても、なんとなく使ってしまうけど、具体的になにを指すものなのか?というと、これが曖昧だったりします。
・・・・で、まずインターネットでデジタル用語解説を探してみました。以下の二つです。
Procedure

辞書的な意味としては「手続き」とか「手順」といった意味になる。主にプログラミング言語の文法要素として目にすることが多い。一連の処理をひとまとめにし、あたかも新しい1つの命令であるかのようにプログラムの他の部分から呼び出して利用することができるもので、プログラマが任意に作成できる。利用頻度の高い処理をこのような形であらかじめ作成しておき、まとめたものが「ライブラリ」となる。

こうした機能は多くのプログラム言語がサポートしているものだが、名称がプロシージャとなるのか別の名前になるのかは、言語によって異なる。感覚的には、「構造化言語」「関数型言語」というコンセプトが出現する前に設計された言語ではプロシージャと呼ばれるように思う。それ以降は、同様の機構が「関数(Function)」と呼ばれ、またオブジェクト指向言語では「メソッド(Method)」と呼ばれる。いずれも、本質的には同様のものだと考えることも可能だ。なお、古いBASICでは、値を返すものが関数で、値を返さないものをプロシージャと呼んでいた記憶がある。

プロシージャ 【procedure】

 プログラム内で繰り返し出現する処理を行なうために、一連の命令を一つの手順としてまとめたもの。「手続き」とも呼ばれ、プログラム内部での処理単位の一つとして扱われる。プロシージャを実行することを「プロシージャを呼び出す」と言う。プログラムの中で何度も同じような処理を行なう場合、それをプロシージャとしてまとめておけば何度も同じ処理を記述する必要がなくなるので、手間が軽減され、後の修正も容易になる

要するにプログラムとかコードと言われるものとあまり違いのないもの・・とでも理解してしまえばいいのでしょうが、例えば前ページまでに紹介してきたもの、Page 4の一番下にリンクさせたページのプロシージャのリストの、一部というか各ブロックを指すのか、それとも全体を指すのか、分かりません。
ただ、このようなイベントから呼び出されるプロシージャのことをイベント・プロシージャと言うようです。
イベント・プロシージャはかならずフォーム・モジュール、またはレポート・モジュールとしてフォームやレポートに格納されています。

いのようなイベントによって呼び出されるプロシージャに対して、イベントやオブジェクトに関連付けられていないプロシージャのことを標準プロシージャと言い、標準モジュール、クラス・モジュールのどちらにも格納できます。
この標準プロシージャには、値を返さないSubプロシージャと 値を返すユーザー定義関数として使われるFunctionプロシージャがあります。

frmDVDのプログラム
 のなかでは、
Sub New_Actor(NewData As String, response As Integer) は引数で値を受け取って処理するSubプロシージャです。
Sub showErrorMessage() は引数のないSubプロシージャです。
Function IsRelative(fName As String) As Boolean はTrue/Falseのどちらかを返すFunctionプロシージャです。
モジュール(module)

 「module」は「規格化された構成単位」という意味。コンピュータ分野では、一般に論理的に分離可能なハードウェア/ソフトウェアの部品を指す。

Accessの公式解説書に記載されているのは以下の文です。

「Visual Basic コードは、Accessデータベースの"モジュール"に格納します。いくつかの関連するプロシージャをまとめたものです。」

これはデータベース・ウィンドウの「モジュール」のなかに格納される汎用サブルーチンのようなもので標準モジュールと呼ばれています。
一方、フォームに関連づけられたものをクラス・モジュールというのだそうです。
この二つのモジュールの中にそれぞれSubプロシージャと Functionプロシージャがある、ということです。
アプリケーション全体で使用する関数やプロシージャは標準モジュールとして登録し、フォームの中でしか使用しない関数やプロシージャはクラスロモジュールであるフォーム・モジュールのなかに書くということですね。
変数の適用範囲

プロシージャの Sub←→ End Sub の中で宣言された変数をローカル変数といい、Dim または Staticで宣言する。
Dimで宣言された変数はそのプロシージャのなかでのみ有効で、外では無効となるが、Staticで宣言された変数はアプリケーションの中で有効となる。

また、フォーム・モジュールの一番上の宣言セクションでPrivate宣言した変数は、そのモジュールのなかのすべてのプロシージャで使用できるプライベート変数という。
そして、同じく宣言セクションでPublic宣言した変数をパブリック変数といい、アプリケーションのどの場所でも使える(プロシージャの中ではパブリック宣言はできない。)。

面白いのは、プロシージャにもパブリック、プライベートがあって、フォーム・モジュールなどのイベント・プロシージャを作成すると自動的にPrivate Sub とプライベート宣言がされるが、これらのイベント・プロシージャはそのモジュールの中でのみ呼び出されるプライベート・プロシージャということです。。
プライベート宣言されないプロシージャは、他のモジュールから呼び出しが可能。
イベント・プロシージャ以外のプロシージャはデフォルトでパブリック・プロシージャになる。

実際に使用頻度が高いのはローカル変数Dimで、必要に応じてアプリケーション全体で必要とするパブリック変数、フォーム・プロシージャの中で共用されるプライベート変数といったところでしょうか。
問題はStatic変数。これが分かりにくいです。

キーワードDimによって宣言されたローカル変数は、プロシージャが終了すると破棄されます。一方、キーワードStaticによって宣言されたローカル変数は、アプリケーションの実行中、値を保持します
キーワードStaticを使って宣言したローカル変数は、アプリケーションが実行されている間を通して存在します

いずれも分かりにくく誤解されそうな表現で、具体的にどのように使われるのか理解しにくいですが、他の解説書で以下のような具体的で分かりやすい例を見つけました。

Sub Calculate_It()
   Static intValue As Integer
   intValue = intValue + 1
   MsgBox intValue
End Sub

Ststic変数はプロシージャが終了しても、次にプロシージャが実行されるときには前回の値を参照できるよう保持されています。

この例では、最初にこのプロシージャが呼ばれると変数intValueの値は1ですが、2度目に呼ばれるときは、intValueが1を保持しているため、加算されて値が2となります。Static intValue As Integer で変数が初期化されない、ということなのですね。
上記のコードでは、プロシージャ内でStatic宣言する代わりに、モジュールの宣言部でモジュールレベル変数として Dim で宣言しても同じ結果になります。
ただし、この場合は、モジュール内のほかのプロシージャから参照し値の変更が可能となるため、思いがけない結果になることもあります。
----------------------
Option Compare Database
Option Explicit

Dim intValue As Integer

Sub Calculate_It()
   'Static intValue As Integer この行をコメントにして、モジュールの宣言部で宣言しても同じ
   intValue = intValue + 1
   MsgBox intValue
End Sub

Private Sub コマンド0_Click()  'テスト・フォームにコマンドボタンをひとつだけ置いて、クリックするたびに上のプロシージャを呼ぶ。
   Calculate_It
End Sub
-----------------------
変数の有効期間はややこしいのですが、これをきちんと把握しておかないと、思い通りの処理がうまくできなかったりします。
Option Compare Database
Option Explicit

プロシージャの集まりであるモジュールの一番上の宣言セクションに必ず書かれているのがこの二行です。
もっとも2行目の Option Explicit はAccess2003の場合はデフォルトではオフになっているようですが、これは変数を明示的に宣言するよう強制するオプションです。
変数にはいきなり値を代入することもできるのですが、Option Explicitで、明示的に宣言しないとエラーになるようにできます。
1行目の Option Compare Database は文字列の比較の方法を指定するもので、データベースの並べ替え順序に従うようにします。
通常は意識するものではないのですが、この行を削除すると文字列の比較の仕方が変わってしまいます。
Functionプロシージャ(関数)と引数

ユーザーの作る関数Functionの例を解説書から引用してみます。
引数として文字列と数値を受け取り、数値分の文字列を反転させる関数です。
具体的には"abcdef" と 5 を渡すと "edcbaf"が返ります。

Function Reverse(strMoji As String, ByVal intChars As Integer) As String 関数が受け取る引数も、値を返す関数自体もデータ型があります。
  Dim strTemp As String, intCount As Integer
  If intChars > Len(strMoji) Then intChars = Len(strMoji) 渡された数値が文字列より大きい場合は、数値を文字列の長さに合わせます。
  For intCount = intChars To 1 Step -1            数値が1になるまで、カウンターintCountの値を-1しながら繰り返します。
    strTemp = strTemp + Mid(strMoji, intCount, 1)    文字列のintCount番目の文字を取り出し文字型変数strTempに代入します。
  Next
  Reverse = strTemp + Right(strMoji, Len(strMoji) - intChars) 反転された指定数の文字と残りの文字を連結して戻り値とします。
End Function

関数Len()は文字列の長さを数値で返します。Mid()は指定文字列からn番目の文字をn個取り出す関数です。right()は指定文字列の右側からn個を取り出します。
関数を呼び出す場合は、Reverse("abcdef",5) と言う具合にカッコの中に引数を入れます。

ByVal

引数にキーワードByBalを付けないと、「参照による引き渡し」、引数の変数の宣言にByValを付けると「値による引き渡し」になります。
参照によって渡されると、関数の中でその変数の値が変更されると、関数を呼び出した側の変数の値も変更されてしまいますが、値による引き渡しは変数のコピーが渡されるため変更されるのはこのコピーだけで、元の変数の値は変更されません。

上の関数のなかのByValを削除してしまうと・・・・、以下のようなケースで intXの値は20から10に変わってしまいます。
文字型変数 strMoji は関数内で加工されてないのでByValを付けなくても問題は無いです。

Dim strMoji As String
Dim intX As Integer
intX = 20
strMoji = "1234567890"
MsgBox Reverse(strMoji, intX)
MsgBox intX ←ここで10に変わっている
Subプロシージャと引数

ついでにこちらはSubプロシージャのサンプルコードですが、呼び出し方法は MultiBeep 3 という具合に、カッコを使わず引数を渡します。
引数で渡された数値分だけビープ音を鳴らすプロシージャです。

Sub MultiBeep(intBeeps As Integer)
  Dim intX As Integer, lngY As Long
  For intX = 1 To intBeeps
    Beep
    For lngY = 1 To 30000000 この二行はビープ音の間隔をあけるためのタイマーです
    Next lngY
  Next intX
End Sub

このプロシージャを標準モジュールのなかに入れておけば問題なく使えるわけですけど、例えば frmDVDのモジュールのなかに入れておいても、外部のプロシージャから呼び出せます。これはSub・・がパブリック・プロシージャだからです。
ただし、呼び出す場合、Form_frmDVD.Reverse 3 という具合にプロシージャのある場所を指定しないとならない。
でも、このような使い方はめったにしないような気がしますけど・・・。


Page Topへ戻る