制御構造の考え方をマクロの記述を通して学びます.まず前回に引き続き,マクロの作成を通じて条件分岐,繰り返しの考え方と記法を復習します.つぎに,処理の流れが「制御構造」と呼ばれる3つの基本構造に還元されることを確認します.同時に条件分岐や繰り返しが入れ子状に組み合わせ可能である理由を考察します.さらに次回への予習として,1つのプロシージャの中で別のプロシージャを呼び出して用いる「サブルーチン化」の考え方と記法を学習します.
1. 条件分岐と繰り返しの組み合わせ(続き)
2. 制御構造
3. サブルーチン化の考え方
先週に引き続き,条件分岐と繰り返しを組み合わせた処理を行うマクロを作成します.
以下のような「サークルの出欠表」があります(practice04.xls, Sheet1).この出欠表の「今週の出席回数」を集計するマクロを作成したいと思います.
集計を効率的に行うためには,「<繰り返しの仕事>を繰り返す」という形の処理手順を構築する必要が生じます.この「<繰り返しの仕事>を繰り返す」という処理手順の形式を「二重ループ」と呼びます.
さっそく処理手順を構想しましょう.
処理手順の構想の段階においては,相手にする仕事が大きく複雑な場合には,小さな仕事のまとまりに分割して考えるとうまくいくことがあります.今回の場合,最終的に得たい結果は,名簿全員分の「今週の出席回数」を正しく集計して表示することです.この仕事全体を,「1人分の出席回数を集計して出力する」という小さな仕事に分割します.そして「1人分の出席回数を集計して出力する」ことを「名簿全員分繰り返す」ことで,全員分の集計を実現すると考えてみましょう.
・名簿全員分の出席回数を集計して表示する(以下を全員分繰り返す) ・1人分の出席回数を集計する ・集計した出席回数を出力する
ここで問題を縮小して,名簿の先頭にある「相澤 隆」さん1人の出席回数の集計の仕方を考えてみましょう.
相澤さんの出席回数はどのように集計すればいいのでしょうか.この表では日付ごとのセルに入力された「○」が出席を表します.したがって,すべての日付について「○」の数を数える方法を考えます.
今回の場合,「(1つの)セルの値をチェックして,値が○であれば出席回数を1加算する」という仕事の繰り返しとして考えれば,仕事がスマートに片づきます.すなわち以下のようになります.
・相澤さんの出席回数を集計する(以下を日付の末尾まで繰り返す) ・日付の若い順にセルの値をチェックする ・出席の時のみ出席回数を1加算する ・集計した出席回数を出力する
以下は,上記の考え方に基づいて作成したソースコードです.
Sub 相澤さんの出席回数の集計() Dim 列番号 As Integer '日付の列番号(横方向) Dim 出席回数 As Integer '出席回数を初期化する 出席回数 = 0 '相澤さんの出席回数を集計する For 列番号 = 2 To 8 If Cells(3, 列番号).Value = "○" Then '日付の若い順にセルの値をチェックする 出席回数 = 出席回数 + 1 '出席の時のみ出席回数を1加算する End If Next 列番号 '集計した出席回数を出力する Cells(3, 9).Value = 出席回数 End Sub
出席回数を数えるために変数「出席回数」を用意します.「出席回数を初期化する」とは,変数「出席回数」の値を,数え始めの前提となる数(初期値),すなわち0にする処理です.もう一方の変数「列番号」は,繰り返し回数を数えるとともに,Cellsで指定したセルの列方向の値を示します.
出席回数 = 出席回数+1
上記の処理は「代入」であることを意識すれば理解できるでしょう.すなわち,「=」の右側の処理である「出席回数+1」(すなわち,現在の「出席回数」の値に1を加算した値を算出する)の計算結果を,「=」の左側にある変数「出席回数」に上書きする,という意味です.
相澤さん1人分の出席回数の集計方法が理解できれば,名簿全体の処理はもう一息で可能です.ここで処理の範囲を名簿全体に広げるための「下準備」をしておきます.
先ほどの相澤さんの例では,繰り返し処理によってセルのチェックは列方向(すなわち横方向ないしはy軸方向)に進みました.一方で行方向については,相澤さん一人を対象としたため固定されていました(ソースコード中の「Cells(3, y)」を参照).
今度は相澤さん一人ではなく,名簿全員分の処理を行います.そのため,行方向の値も可変値,すなわち変数が使われる必要があります.そこで,相澤さんの出席回数の集計マクロを以下のように少しだけ修正します.違いを見比べてください.
Sub 出席回数の集計1() Dim 行番号 As Integer '対象人物の行番号(縦方向) Dim 列番号 As Integer '日付の列番号(横方向) Dim 出席回数 As Integer '集計する対象人物を行番号で指定する 行番号 = 3 '出席回数を初期化する 出席回数 = 0 '対象人物の出席回数を集計する For 列番号 = 2 To 8 If Cells(行番号, 列番号).Value = "○" Then '日付の若い順にセルの値をチェックする 出席回数 = 出席回数 + 1 '出席の時のみ出席回数を1加算する End If Next 列番号 '集計した出席回数を出力する Cells(行番号, 9).Value = 出席回数 End Sub
対象人物の行番号(すなわ縦方向の変化)を表す変数「行番号」が追加されています.さらに,「集計する対象人物を行番号で指定する」という処理が加わっています.現在は変数「行番号」に3が代入されますが,代入する値を変更すれば,集計される人物も変わります.
先ほどの下準備によって作成されたマクロは,相変わらず1人分の集計しか出来ません.しかし対象人物の行番号を表す変数「行番号」と「集計する対象人物を行番号で指定する」という処理を加えたことで,手動ながら対象人物を任意に変更できるようになりました.
この,手動で行う対象人物の変更処理を,繰り返し文を使い自動化することによって,名簿全体の出席回数の自動集計マクロの最終形が完成します.すなわち,マクロの最終形は以下の処理手順によって実現されます.
・名簿全員分の出席回数を集計して表示する(以下を名簿全員分繰り返す) ・出席回数を初期化する ・1人分の出席回数を集計する(以下を日付の末尾まで繰り返す) ・日付の若い順にセルの値をチェックする ・出席の時のみ出席回数を1加算する ・'集計した出席回数を出力する
以下は最終形のマクロのソースコードの一部です.「・・・」の部分はソースコードが省略されています.これまでの解説をもとに自分で完成させてください.
Sub 出席回数の集計2() Dim 行番号 As Integer '対象人物の行番号(縦方向) Dim 列番号 As Integer '日付の列番号(横方向) Dim 出席回数 As Integer '名簿全員分の出席回数を集計して表示する For ・・・ '出席回数を初期化する 出席回数 = 0 '1人分の出席回数を集計する For ・・・ If Cells(行番号, 列番号).Value = "○" Then '日付の若い順にセルの値をチェックする 出席回数 = 出席回数 + 1 '出席の時のみ出席回数を1加算する End If ・・・ '集計した出席回数を出力する Cells(行番号, 9).Value = 出席回数 ・・・ End Sub
以下はAさんが作成した「箱根周辺の旅館・ホテルのレーティング表」です(practice04.xls, Sheet2).それぞれの旅館・ホテルについて,「料理」から始まる7つの評価項目の格付けを数値(1〜5)で示しています(旅館・ホテルはすべて架空のものです).
このままでは読みづらいので,数値別にセルを色分けしようと思います.1の場合はオレンジ(46番),2は黄色(44番),4は薄い水色(37番),5は水色(33番)で,3はそのままにします.この約束に従って表全体を自動的に色分けするマクロを作成しましょう.
マクロのあらゆる処理手順は「制御構造」という原則に従って記述されます.ここでは制御構造の考え方を理解します.
制御構造とはマクロの記述に見られる,処理手順の一般的な構造を指します.VBAによるマクロに限らず,あらゆるプログラムの処理手順は3つの基本的な制御構造に則って記述されます.すなわち,あらゆる記述の大前提となる「順次」,そして前回学習した「条件分岐」,さらに先程学習した「繰り返し」です.VBAのマクロを含むあらゆるプログラムの処理手順は,これら3つの制御構造を組み合わせて記述されます.
複雑な処理を記述する場合,3つの制御構造を互いに入れ子状に組み合わせて用いることになります.たとえば,上の図の繰り返しの命令Aの部分は,単独の命令だけでなく,順次構造を持った複数の命令に置き換えることが出来ます.また,同様に条件分岐の命令A, 命令Bの部分には,繰り返しの制御構造を組み込むことが出来ます(以下の図を参照).3つの制御構造を互いに組み合わせることによって,あらゆる複雑な処理が表現できます.
このように3つの制御構造を自由に組み合わせることが出来るのは,いずれの制御構造も処理の入り口が1つで,処理の出口が1つだからです.すべての制御構造が「入り口1つ・出口1つ」であるという点は,1度に1つの処理しか行うことが出来ないコンピュータの動作の特徴を反映しています.どんなに複雑な処理であっても,「入り口1つ・出口1つ」の原則に則る限り,3つの制御構造を組み合わせることによって記述できるのです.
最後に次回の作業の予習として「サブルーチン化」の考え方を学習します.
Excel VBAでは一つのプロシージャを記述する際に,別のプロシージャをあたかも一つの命令のように呼び出して実行することが出来ます.たとえば以下のコードウィンドウには3つのプロシージャが書かれています.1番上の「表項目欄の書式設定1」には,その下に書かれた2つのプロシージャの名前(「太字に設定」と「背景色を黄色に設定」)が書かれています.このように,他のプロシージャの名前を書くことによって,名前が書かれたプロシージャが呼び出され,その処理が実行されます.処理が終われば再び呼び出し元のプロシージャに戻り,次の行の命令の実行に移ります.
呼び出し元のプロシージャと呼び出される側のプロシージャの間に,記述の順序についての約束事はありません.上記の例ではたまたま呼び出し元の「表項目欄の書式設定1」が呼び出される側の「太字に設定」,「背景色を黄色に設定」よりも先に書かれていますが,これらの間の順序関係はどのようであっても構いません.とにかくプロシージャ名を正確に記述すれば,それがどこに書かれていようと,呼び出されて実行されます.
上述のように,複数のプロシージャの間で呼び出す/呼び出される関係が成立する場合,呼び出す側のプロシージャを「メインルーチン」,呼び出される側のプロシージャを「サブルーチン」と呼ぶことがあります.これはExcel VBAに限らず,あらゆるプログラミング全般について当てはまります.
メインルーチン/サブルーチンの関係を用いるメリットは,主に次の2点です.第1に,プロシージャの記述が長く複雑になった場合,その記述の一部をサブルーチンとして独立させることで,処理手順の全体像が整理され分かりやすくなります.この場合,記述の一部をサブルーチンとして独立させることを「サブルーチン化」と呼びます.第2に,しばしば行う処理をあらかじめプロシージャとして記述しておくことで,新しいプロシージャを作成する際に「部品」として利用することが出来ます.すなわち,新しいプロシージャ(メインルーチン)は,部品としてのプロシージャ(サブルーチン)の集まりとして組み立てられます.
テストの得点を手がかりに平均点,標準偏差,偏差値,偏差値に対する評価を自動的に出力する「テスト自動集計マクロ」を作成しましょう(practice04.xls, Sheet3).その際,平均点,標準偏差,偏差値の算出,および偏差値に対する評価の出力はそれぞれ独立したサブルーチンとして作成し,メインルーチンである「テスト自動集計マクロ」がすべてを統合するような形で作成しましょう.
今回の場合,メインルーチンはサブルーチンを呼び出すだけの役割を果たします.あらかじめサブルーチンの名前を決めた上で,以下のようにメインルーチンの記述を作成しておきます.
Sub テスト自動集計() ' テストの結果の集計を自動的に行う 平均点の算出 標準偏差の算出 偏差値の算出 偏差値評価の出力 End Sub
平均点及び標準偏差の算出には,ワークシート関数(Excelのワークシート上で,マクロではなく手作業で計算を行う際に利用する関数)を呼び出して利用します.平均点を算出する関数はAVERAGE関数,標準偏差を算出する関数はSTDEV関数としてそれぞれ用意されています.ワークシート関数を呼び出して利用する際には以下のように記述します.
Application.WorksheetFunction.関数名
AVERAGE関数およびSTDEV関数をワークシート上で利用する際には,計算の対象となる値の入っているセル範囲(複数のセルによって構成される範囲)を与える必要があります.たとえばB2からB27の値の平均値を算出するためには,平均値を表示したいセルに「=AVERAGE(B2:B27)」と記述します.このとき,関数が計算を行うために必要とする()内の値のことを「引数(ひきすう)」と呼びます.
VBAで上記のワークシート関数を呼び出して用いる際には,引数の記述の仕方が変わります.VBAにおいては,B2:B27のようなセル範囲はRangeオブジェクトとして扱われます.セル範囲を表すRangeオブジェクトの指定の仕方として最も一般的なのは,Rangeプロパティを用いて記述する方法です.たとえばB2からB27の範囲ならば「Range("B2:B27")」と記述します.このRangeプロパティの記述自体をまるごとワークシート関数の引数として記述します.
したがって,たとえばAVERAGE関数をVBA上で呼び出すには以下のように記述します.
Application.WorksheetFunction.Average(Range("B2:B27"))
上記の命令が実行されると,B2からB27の値の平均値が算出されます.関数やその他命令の実行によって算出される値のことを引数に対して「返値(かえりち)」と呼びます.Averageの返値を,たとえばB28に出力するためには,代入文によってRange("B28").Valueに代入します.
従って,平均点を算出するサブルーチンは以下のように記述することになります.
Sub 平均点の算出() ' 平均点を算出する Range("B28").Value = Application.WorksheetFunction.Average(Range("B2:B27")) End Sub
標準偏差を算出するサブルーチンも,上記と同様の手順で作成しましょう.
偏差値を算出する公式は「(得点ー平均点)/ 標準偏差×10+50」です.四則演算における()は,VBAでもそのまま()を記述することで有効となります.
1名分の偏差値を算出し,セルに出力するためには以下の処理手順が必要です.
名簿全員分の偏差値を算出するためには,上記の作業を人数分繰り返します.ただし1〜4の中で,1つだけ繰り返す必要のない処理があります(ヒント:あらかじめ1度だけやっておけばいいことです).考えてみましょう.
また,平均点,標準偏差,偏差値は小数値ですので,変数の宣言の際にはDouble型で宣言しましょう.
この部分は2.3の練習問題のマクロをそのまま応用すれば容易に作成できます.変更点は評価結果の表示の仕方です.2.3では背景色で示しましたが,今回はA, B, C, Dのいずれかを所定のセルに表示します.