前回はメッセージボックスに文字列を表示する最初のプログラムを作成しました。
ここからは、少しだけ詳しく基本の勉強をしていきます。
基本は大事ですが、よく分からないところはあまり深く考え込まずに、先に進んでみた方が良いかもしれません。最初から一度で全てを理解しようと無理するとかえって効率が悪く、身につく前に嫌になってしまうものです。
変数
変数とは、プログラムの中で変化する値(データ)に名前(ラベル)をつけたものです。入れ物にたとえられることもあります。
計算結果を格納しておいて後で参照したり、集計したデータを保存したりするためには、変数を使って値を記憶しておく必要があります。
VBAにはデータの『型』という概念があり、プログラムで変数を扱う場合、適した型を使わなければなりません。
例えば"Hello world!"は文字列なのでstring型です。文字列は二重引用符(ダブルクォーテーション)で囲って記述します。
以下は主なVBAの型です。ざっと眺めてみてください。
データ型 | 型名 | 扱えるデータの種類 |
---|---|---|
Integer | 整数型 | 2バイトの整数 |
Long | 長整数型 | 4バイトの整数 |
Boolean | ブール型 | TrueまたはFalseの真偽値 |
Double | 倍精度浮動小数点数型 | 8バイトの符号付き浮動小数点数 |
String | 文字列型 | 文字列 |
Variant | バリアント型 | 全て |
定数
定数とは、プログラムの中で実行中に変化しない値です。
プログラムに直接書かれた 4 とか 121.97 とかの値、"エクセル"や"ブイビーエー"といった文字列などは定数です。
これらの定数にも変数と同様に名前をつけることができます。
変数はその中身を変化させることができますが、定数は一度定義した値を変更することができません。というか、実行中に変化するような値は定数としての役割を果たせません。
ラベルを付けた定数を使うことによって、後で定数の値を変更したくなったときでも、最初の定義を修正するだけでよく、全部の箇所の値を直接書き換えるのに比べて圧倒的に効率的なプログラムになります。
変数と定数の宣言
プログラムの中でどのような名前を何の用途に使うのかを記述することを、変数や定数の宣言といいます。
VBAでは変数の宣言にDimというキーワードを使います。
『dim 変数名 as 変数型』と記述することが変数の宣言で、指定した変数型の変数名を変数として使えるようになります。as 以下を省略することでvariant型の宣言になります。
『const 定数名 as 変数型 = 定数の値』と記述することで、定数の宣言と値の代入(定義)を行うことができます。
それでは、変数と定数を宣言して簡単な計算を行い、メッセージボックスに結果を表示してみましょう。
新しく標準モジュールを追加し、以下のコードをVBEに打ち込んで、実行してみてください。
'変数、定数の宣言と簡単な計算
Sub test_sengen()
Dim kazu1 As Long 'kazu1という名前のlong型変数を宣言
Dim kazu2 As Long 'kazu2という名前のlong型変数を宣言
Const teisuu As Long = 2013 '2013という値をteisuuという定数の値として定義
kazu1 = 500 'kazu1に500を代入(格納)
kazu1 = kazu1 * 10 'kazu1にkazu1と10の積を格納
kazu2 = teisuu + kazu1 'kazu2にteisuuとkazu1の和を格納
MsgBox "計算結果:" & kazu2 '"計算結果:"という文字列とkazu2を続けて表示
End Sub
コードの解説
コードのコメントを読んでいただければだいたい想像できると思いますが、簡単に説明します。
まず、dimで変数を、constで定数を宣言しています。
定数(teisuu)は宣言時に2013という値を割り当てています。
変数の宣言後、『kazu1 = 500』と記述することで kazu1 という変数に500という値を代入しているため、この行が実行された時点で、kazu1の示す値は500になります。
『kazu1 = kazu1 * 10』は kazu1 の値を10倍した結果を kazu1 に格納しており、この後の kazu1 の値は5000です。
そして、『kazu2 = teisuu + kazu1』では teisuu が示す2013という値と kazu1 が加算され、計算結果が kazu2 に格納されます。
最後はHello worldで使ったMsgBox関数を使っていますが、ここで文字列と変数を & 記号でつないでいます。
& はVBAで文字列の結合に使われる記号で、ここでは"計算結果:" という文字列と、kazu2の値を文字列に変換したものが一つの文字列として扱われ、メッセージボックスに表示されます。
文字列と文字列の結合には & ではなく + を使うこともできます(上の例だと、kazu2は文字列ではないので + で結合しようとするとエラーになります)。
変数名を変えたり、他のデータ型を使ってみたり、いろいろな計算をしたり、試してみてください。
なお、VBAでよく使う演算記号には以下のようなものがあります。
演算記号 | 用途 | 使用例 | 計算結果(aの値) |
---|---|---|---|
= | 代入 | a = 15 | 15 |
+ | 加算 | a = 1 + 2 | 3 |
- | 減算 | a = 5 - 1 | 4 |
* | 乗算 | a = 3 * 2 | 6 |
/ | 除算 | a = 10 / 3 | 3 (aが整数型の場合) 3.33…(aが小数型の場合) |
mod | 除算の余りを求める | a = 10 mod 3 | 1 |
& | 文字列を結合 | a = "富士山は" & "高い" | "富士山は高い" |
and | and演算 | a = true and false | false |
or | or演算 | a = true or false | true |
プログラム中の計算結果は、結果を格納する変数によって異なる場合があります。
例えば表中の除算は、変数 a が integer や long(整数型)であれば小数点以下が切り捨てられて商が a に代入されますが、変数 a が single や double(小数型)の場合は小数点以下まで計算した結果が代入されます。
変数の宣言を強制
変数は使用する前に宣言しなければならないのがルールですが、実はVBAでは宣言しなくても使用することができる場合があります。
モジュールの先頭に option explicit という記述をすることにより、宣言されていない変数を使おうとした場合にエラーと判断することができます。
以下の例を見てください。
'変数の宣言強制のテスト
Option Explicit
Sub hensuu()
a = 1
End Sub
変数を宣言しないで使っていると、プログラムのミス(バグ)を見つけることが困難になる場合があります。
今度は次の例を試してみてください。
'変数名を間違った例
Sub ss()
Dim siijkinliIrm1l45 As Integer
siijkinliIrm1l45 = 153
MsgBox (siiikihlilrm1l45)
End Sub
極端な例ですが、 siijkinliIrm1l45 という変数に153をセットし、メッセージボックスにこの変数の値を表示しようとした例です。 option explicit を宣言していない場合、実行すると特にエラーは発生しませんが、メッセージボックスには何も表示されません。
変数の宣言を強制していれば、このようなプログラムミスを実行前にエラーとして検出することができ、プログラムが複雑になればなるほどそのありがたみを実感できるはずです。
VBAでプログラムを作成する場合には、モジュールの先頭でまずoption explicitを宣言する癖をつけておくことをおすすめします。option explicit宣言は『初心者っぽさ』を表すものではなく、開発するプログラムの正確性に対する積極的な姿勢を表すものだと考えた方がよいでしょう。
ちなみに変数の宣言強制は、VBEのツールバーでツール→オプションからも設定することもできますが、そこまでしなくてもよい気はします(デフォルトはオフ設定)。
もちろん、変数の宣言を省略した方がコーディング量を減らすことができるというメリットはあります。
変数のスコープとパブリック宣言
少し難しいかもしれませんが、変数の適用範囲について少し説明します。
VBAのプログラムはモジュール単位で作成します。
そして、モジュールには複数のプロシージャを記述することができます。
変数のスコープ
ここで、変数と定数のスコープ(適用範囲)を考える必要があります。
VBAでは、前述の Dim によって宣言した変数には、宣言した場所以下のスコープレベルからしかアクセスできません。
具体的には、モジュール先頭で Dim を使って定義した(モジュールレベル)変数は、当該モジュール内に記述された全ての Sub プロシージャから参照することができますが、他のモジュールからは参照することができません。
そして、プロシージャ内で Dim 宣言された(プロシージャレベル・ローカル)変数は、同一モジュール内であっても他のプロシージャから使用することはできません。
以下の図(コード、コメント)を参考に考えてみてください。
scope3には引数が指定されています。『Call scope3(a3, c2)』と『scope3 a3, c2』はどちらもscope3関数に引数(a3とc2)を渡していることを示しています。
scope2では、未定義の変数(a3とc2)を使うことができずにエラーになりますが、scope3では引数としてscope1から定義済みの変数(a3とc2)を受け取っているので、実行することができます。
パブリック宣言
プログラムの規模が大きくなると、一つのモジュールに全てを記述することが困難になってきます。もちろん不可能ではありませんが、モジュールを分割することで保守、管理が容易になります。
このとき、前述したモジュールレベルの変数を複数のモジュール間で共通して使うことができず、不便な場合が出てきます。
引数を使って受け渡すことも可能ですが、全てのモジュールからアクセスしたい変数を定義する場合には、パブリック変数として定義することができます。
パブリック変数は、いずれかのモジュール先頭で『public a as long』のように dim の代わりに public を使って宣言します。
以上より、とりあえず全ての変数をパブリック変数として定義しておけばよいと思うかもしれませんが、必ずしもそうではありません。どこからでもアクセスできるということには、誤って変数の値を変更してしまうリスクがあります
また、プログラムの実行時、ローカル変数は定義されたプロシージャを抜ければ初期化されますが、モジュールレベルやパブリックレベルの変数は値が保持されます。モジュール変数等の値がプログラム実行後においても自動的には初期化されないことに気がつかないと、以下のようなケースに悩まされることになるでしょう。
さて、これを見ただけでそれぞれの実行結果が想像できるでしょうか。
図の test1 も test2 も、メッセージボックスを表示した後に、表示しようとしている文字列変数に値を代入しています。
test2は何回実行しようと画面に"値を入れる(test2)"と表示されることはありません、
一方、 test1 を1回目に実行したときには何も表示されませんが、そのまま続けて実行すると、2回目からは"値を入れる(test1)"がメッセージボックスに表示されるようになります。この現象の理由は先ほど説明したとおりです。ぜひ実際に確かめてみてください。
ユーザ定義型(構造体)
変数に様々な型があることは説明しましたが、それらを組み合わせて独自の型を定義することができます。
'ユーザ定義型の例
Type Kouka '硬貨の枚数を保持する構造体
gohyakuYen As Long
hyakuYen As Long
gojuuYen As Long
goYen As Long
ichiYen As Long
End Type
上の例のようにType ○○(型の名前)を書いた後にその型に属する変数(メンバ変数)を書き、最後にEnd Typeで閉じます。
Koukaというユーザ定義型(構造体)の中で複数の変数をまとめて定義しています。
このようにするとプログラム的に似たような使い方をしたい複数の変数を一か所で管理することができて便利です。
ユーザ定義型を定義しておけば、普通の変数と同じように宣言して使うことができ、メンバ変数の候補もインテリセンスで表示されるようになります。