システムトレード用の開発ツールです。エクセルのVBAを駆使して最高のパフォーマンスを目指します。基本は無料
1つの大きなプロシージャよりも幾つかに部品化することの利点を前にお話しました。今回はVBAを理解する最大の障害といわれている引数付きプロシージャと変数の宣言場所の説明です。
ここで再度、第2回で用いた料理人の師弟関係を再度使って部品化された「プロシージャ」のイメージのおさらいです。下図は前回の時に使ったイメージ図です。天ぷら御前という注文をうけた店主が天ぷらをあげながら、店員Aに小鉢とご飯のセットを指示し、店員Bのはサシミ盛を作るように指示している関係図です。思い出しました?

下図が今回の「引数付きプロシージャ」がある場合のイメージ図です。違いが分かるでしょうか?

目立つように赤字表示された、「イカそうめん抜き」が唯一違うところです。つまり、親方が弟子Bに今度の天ぷら御膳の刺身盛りは「イカそうめん抜き」にしろとオプションをつけて命令しています。普段は刺身盛りの中にイカそうめんは入っているのですが、注文したお客さんが要望があり、イカそうめん抜きにする感じです。命令するときにオプション(引数)というものをつけて、子(弟子)プロシージャを呼びだしています。これをVBAでは、「引数付きプロシージャを呼びだす」といいます。コードで書くとこんな感じです。
Sub 親方()
海老3本揚げる
ナス1本揚げる
かき揚げ1つあげる
白身魚を揚げる
Call 弟子A ’弟子Aを呼ぶ
Call 弟子B(イカそうめん抜き) ’弟子Bを呼ぶ
End Sub
Sub 弟子A()
ご飯を盛りつける
小鉢を盛りつける
End Sub
Sub 弟子B( イカそうめん抜き)
刺身盛りを作る
刺身盛りを御膳にセットする
End Sub
今回は「プログレスバー」を作りながら、引数付きプロシージャの作り方と使い方を説明しようかなと思います。まず、「プログレスバー」についての説明です。 時間の掛かる処理を実行している時には、「今動いてるのか?止まっているのか?」という思いでユーザーは不安になるものです。そこで、その不安を解消するとともに、「どの位進んだのか」を知らせる為のインターフェースとして使われるのが「プログレスバー」になります。「プログレスバー」を作る方法は色々な方法が考えられますが一番効果的で再配布できる利点を考えると、ユーザーフォームを使った自作プログレスバーになります。以下のプロシージャ2つが親プロシージャと子プロシージャの主要プログラム部分です。


図1のプロシージャはモジュール「テスト」の中に入っています。「テスト1」は実行すると図2の矢印分の数字が1秒ごとに1、2、3と増えていくプログラムです。
1.プロシージャの内容を詳しく見ていきます。まず、「テスト1」が実行されると、セルA1に1が記入されます。それが終わると子プロシージャーの「progress_bar」を引数「5」と「1」を付けて呼び出します。意味は定数「5」と「1」を渡すから、「progress_bar」は「5」と「1」を使って仕事しなさい。」という命令文です。先ほどの「イカそうめん抜き」の部分が「5」と「1」に変わっただけです。
2.数字「5」と「1」を受け取った「progress_bar」は自分の仕事であるプログレスバーの進捗状況を数字「5」と「1」で表す作業を始めます。全体が「5」で今「1」が終わったのですがら、プログレスバーは全体の5分の1進ませて、親プロシージャに命令を返します。
3.子プロシージャから作業の終わりをつげられたので、「テスト1」は次の「Sleep(1000)」という命令を実行します。「これは今から1秒間休め」という命令です。
4.次に、また初めに戻り、セルA2に数字の2を記入 → 「progress_bar」に引数「5」と、今度は「2」を付けて呼び出し → progress_bar」は数字の「5」と「2」を使い進捗状況を表示 → 親プロシージャに戻りスリープ1秒間。これをあと3回繰り返すとプログラムは終了します。
5.親プロシージャが引数付きで子プロシージャを呼び出す(命令する)時は、
Call 子プロシージャ名 (引数1、引数2、引数3・・・・)
になります。括弧内に引数1、2、3・・・という感じで、子プロシージャに渡す引数を指定します。尚、
子プロシージャ名 引数1、引数2、引数3・・・・
でも同じ結果になります。よく見かけるのはこの「Call」がないバージョンですが、親子関係がはっきりするのでなれるまでは「Call」付きをお薦めします。
6.一方、子プロシージャは、
Sub 名前〇〇〇〇(変数 as データ型、変数 as データ型 ・・・・)
とSubの括弧内に親とやり取りしたい数字を入れるための変数を宣言します。括弧内で変数のデータ型を指定するのですが、ここで注意点があります。親と子で渡す引数のデータ型は必ず同じにします。揃えないとエラーになります。図1の赤く囲った部分を比較してみてください。親プロシージャの呼び出し方と子プロシージャの宣言部分の文法がほぼ同じで分かりやすいと思います。呼び出すときは呼び出したいプロシージャに引数を付けて呼び出し、呼び出される方は、渡される引数を受け取るための変数をタイトル部分をあらかじめ用意しておきます。
次のプロシージャを見てください。

先ほどの「テスト1」とこの「テスト2」の実行結果は同じです。違うのは、今回は親プロシージャに変数とループを使っている点です(ここではループの使い方は省略します)。特に注目してほしいのは、親が子プロシージャを呼び出すときに、引数が変数になっている点です。
Call progress_bar(total, i)
の「total」と「i」のことです。引数が今度は「定数」ではなく、「変数」に変わりました。図3のように引数「total」と変数「total」、引数「i」と変数「i」を矢印で指すと、まるで引数「total」が変数「total」に渡ったような錯覚に陥りますが、渡ったのは引数「total」に代入された「定数」だということを確認しておいてください。結構ここで誤解が生じ、引数について混乱する原因になっています。引数「total」と変数「total」が同じである必要は全くありません。例えば親プロシージャの引数「total」を「総数」、引数「i」を「カウント数」に変えても数字は子に渡ります。
Sub テスト2()
Dim カウント数 As Long
Dim 総数 As LongSheets(1).Select
総数 = Cells(2, 1).Value '実行回数を決める変数
プログレスバー.Show (vbModeless)
For カウント数 = 1 To 総数
Cells(1, 1).Value = カウント数 'セルA1にiを代入
Call progress_bar(総数, カウント数) 'progrees_bar に変数「total」の値と
'変数「i」の値を付けて呼び出し
Sleep (1000) '1秒寝るZZZZNext カウント数 'ループします。
Unload プログレスバー
End Sub
さて、使い方の基本は終わった訳ですが、最後に、なぜ引数を付けてまでも分ける必要があるのかい再度考えてみます。まず、もし、プログレスバーを部品化しなかった場合は図1の子プロシージャを呼び出す5箇所をすべてを以下に置き換える必要があります。
j = i / total * 100 'iを100あたり幾らかを計算した変数がj
With プログレスバー
.Label2.Caption = Int(j) & "%"
.Label1.Width = .Label2.Width * j / 100
End With

投稿者 システムトレーダー壱式 : 2008年06月14日 04:48
このエントリーのトラックバックURL:
http://systemtrader.info/cgi/mt/mt-tb.cgi/87