今回は、プログラムの部品として重要な「変数」について説明していきたいと思います。
「変数」というのはもちろん日本語なのですが、なんとなく意味がよくわからない日本語だと思います。
プログラムの中で、いろんな値や文字などを格納する箱みたいなものと考えていただくのがいいと思います。
VBAにおいては、プログラムの最初に、そのプログラムの中で使う変数を宣言することが一般的です。
前回までのブログ記事を紹介しておきます。必要があれば参照ください。
・エクセルマクロ(その1)VBEの使い方を知ろう
・エクセルマクロ(その2)セルの選択方法
・エクセルマクロ(その3)ブックを切り替える
1.変数の宣言方法
まず、最初のプロシージャを見てください。
Sub テスト()
Dim x As Integer
x = 10
Debug.Print x '10
End Sub
最初の「Dim x as Integer」という部分が変数宣言になります。
「Dim」+「変数名」+「as」+「変数のデータ型」という形です。
「Integer」という部分が、変数のデータ型を示しています。
ちなみに、「Integer」は整数(-32768~32767の範囲の整数)を示します。
変数は何でも入る箱なのですが、入れる前にどんな種類のものを入れるかを宣言しておかなければならないのです。
「x = 10」という部分は、「x」という変数に、「10」を代入するという意味です。ただし、この「10」がいつまでも「x」の中に入っているわけではありません。
Sub テスト()
Dim x As Integer
x = 10
x = 20
Debug.Print x '20
End Sub
上記のプロシージャは、「x = 10」の下に「x = 20」が書かれています。
すでに「10」が入っているにも関わらず、後から「20」を代入すると、前に入っていた「10」は消えて、「20」になってしまうが変数なのです。
つまり、後から代入したものが強いというわけです。
次は、こんなプロシージャを見てください。
Sub テスト()
Dim x As Integer
x = x + 1
Debug.Print x '1,1,1,1,....
End Sub
こんどは、「x = x + 1」という代入式になっています。
この場合は、今ある「x」に1を足したものを「x」に代入するという意味になります。
このプログラムを何度実行しても、Debug.Printで表示されるのは、「1」となります。これは、一旦プロシージャが終了すると、宣言された変数は消滅してしまうからです。
ところが、宣言されている変数の値をプロシージャの終了後も保持する方法があります。
Sub テスト()
Static x As Integer
x = x + 1
Debug.Print x '1,2,3,4,5,....
End Sub
「Dim」ではなく、「Static」と書くことで、変数「x」の値は、プロシージャ終了後も保持されるので、Debug.Printで表示される値は、「1、2,3,4、5・・・」と増えていきます。
その他にも、変数の値を保持する方法としては以下のようなものがあります。
(宣言セクション)
Private x As Integer
--------------------------
Sub テスト()
x = x + 1
Debug.Print x '1,2,3,4,5,....
End Sub
標準モジュールの先頭に、「Private x As Integer」と書いて、Subプロシージャの中には、変数宣言を書いていない場合です。
標準モジュールの先頭の場所を「宣言セクション」と言って、モジュール全体にかかるような内容を書きます。
宣言セクションに書いた変数宣言は、標準モジュール全体で使用される変数ということで、値が保持されています。
上記の場合、「Private」と書いているので、書かれている標準モジュール内のみで有効になっています。
一方、「Private」を「Public」に変えると、他の標準モジュールまで有効範囲が広がって変数が宣言されます。
(宣言セクション)
Public x As Integer
---------------------------
Sub テスト()
x = x + 1
Debug.Print x '1,2,3,4,5,....
End Sub
このように、変数を宣言する方法によって、有効期間や有効範囲が変わるということを覚えておいてください。
ただし、変数を保持する形で宣言したとしても、エラーでストップした時や、エクセルブックを閉じてしまうとリセットされますので、注意してください。
2.変数の型の種類
ここで、変数のデータ型について説明したいと思います。
(1)数値型
整数だったら、IntegerかLong、小数だったらSingleかDouble、通貨だったらCurrencyと覚えておけば大丈夫です。ただし、代入した数値の精度は、データ型によって変わりますので、注意が必要です。
Sub 数値()
Dim s0 As Byte
Dim s1 As Integer
Dim s2 As Long
Dim s3 As Single
Dim s4 As Double
Dim s5 As Currency
s0 = 100.123456789
s1 = 100.123456789
s2 = 1000000.12345679
s3 = 12.3456789012345
s4 = 12.3456789012345
s5 = 12.3456789012345
Debug.Print s0 '100 ...整数になる
Debug.Print s1 '100 ...整数になる
Debug.Print s2 '1000000 ...整数になる
Debug.Print s3 '12.34568 ...小数点以下5桁になる
Debug.Print s4 '12.3456789012345 ...すべて正しく表示される
Debug.Print s5 '12.3457 ...小数点以下4桁になる
End Sub
データ型 | 初期値 | データ範囲 |
Byte | 0 | 0~255 (バイト型) |
Integer | 0 | -32,768~32,767 (整数型) |
Long | 0 | -2,147,483,648~2,147,483,647 (長整数値) |
Single | 0 | 4Byteで表せる浮動小数点値 (単精度浮動小数点数型) |
Double | 0 | 8Byteで表せる浮動小数点値 (倍精度浮動小数点数型) |
Currency | 0 | 8Byteで表せる固定小数点値 (通貨型) |
(2)ブール型
ブール型は、真偽を判定するような場合に使われる型です。
Sub ブール型()
Dim flag As Boolean
Dim kazu As Integer
kazu = 15
If kazu > 10 Then
flag = True
End If
Debug.Print flag 'True
End Sub
If~End IFの構文の中で、flagにTrueを代入しています。Boolean型の変数には、TrueもしくはFalseしか代入できません。
データ型 | 初期値 | データ範囲 |
Boolean | false | TrueまたはFalse |
(3)日付型
日付は、エクセルの日付範囲と異なって、西暦100年から計算できます。
また、エクセルと同様に日付や時刻はシリアル値にも変換可能です。
日付型の変数に代入を見てみましょう
Sub 日付()
Dim hi As Date
Dim jikoku As Date
hi = "2022/7/20"
jikoku = "8:20:30"
Debug.Print hi '2022/07/20
Debug.Print jikoku '8:20:30
Debug.Print Now '2022/07/20 19:44:49
Debug.Print Date '2022/07/20
Debug.Print Time '19:44:49
End Sub
上記の例では、「hi = “2022/7/20″」と書いていますが、「hi = #2022/7/20#」と書くことができます。そうすると、自動的に「hi = #7/20/2022#」と整形されます。
また、「jikoku = #8:20:30#」と書くと、「jikoku = #8:20:30 AM#」と整形されます。
必ず、日付や時刻を代入するときは、ダブルクォーテーションか、#で囲むようにしてください。
Now、Date、Timeは、自動的に現在の日付や時刻を導く関数です。
データ型 | 初期値 | データ範囲 |
Date | 1899/12/30 00:00:00 | 西暦100年1月1日~西暦9999年12月31日 時刻は0:00:00~23:59:59まで |
(4)文字列型
文字列を変数に代入する場合は、必ずダブルクォーテーションで囲む必要があります。
Sub 文字列()
Dim kotoba As String
kotoba = "おはよう"
Debug.Print kotoba 'おはよう
End Sub
データ型 | 初期値 | データ範囲 |
String | vbNullString | 文字列 |
(5)オブジェクト型
オブジェクト型は、数字や値を代入するような変数ではなく、オブジェクトを参照するタイプの変数です。
Sub オブジェクト型()
Dim place As Object
Range("A1:C3").Select
Set place = Selection
place.Interior.Color = RGB(255, 0, 0) '赤、xlnoneで透明
Set place = Nothing
End Sub
オブジェクト型の場合は、「Set 変数名 = オブジェクト」と書きます。
「Set 変数名 = Nothing」と書くことで、オブジェクトの参照を解除します。
データ型 | 初期値 | データ範囲 |
Object | Nothig | 任意のオブジェクト参照 |
Range | Nothig | セルの範囲 |
Worksheet | Nothig | ワークシート |
Workbook | Nothig | ワークブック |
(6)バリアント型
バリアント型は、配列変数の宣言によく使われます。
すべての型のタイプに対応する万能型の変数です。
Sub バリアント型()
Dim x(5) As Variant
x(1) = 10 '数値型(整数)
x(2) = 10000000 '数値型(長整数)
x(3) = 10.1234567 '数値型(小数)
x(4) = "みかん" '文字列型
x(5) = True 'boolean型
Debug.Print x(1) '10
Debug.Print x(2) '10000000
Debug.Print x(3) '10.1234567
Debug.Print x(4) 'みかん
Debug.Print x(5) 'True
End Sub
データ型 | 初期値 | データ範囲 |
Variant | Empty | あらゆる種類のデータ型を扱える |
(7)ユーザー定義型
ユーザー定義型は、複数の種類のデータ型を1つの変数に収めるような場合に使用します。
(宣言セクション)
Type myClassMember
namae As String
birthday As Date
age As Integer
bloodtype As String
End Type
---------------------
Sub クラス()
Dim m(30) As myClassMember
m(1).namae = "鈴木"
m(1).birthday = "2004/12/1"
m(1).age = 18
m(1).bloodtype = "B"
Debug.Print m(1).namae
Debug.Print m(1).birthday
Debug.Print m(1).age
Debug.Print m(1).bloodtype
End Sub
Type [ユーザー定義変数]~End Typeの中で、個別の変数を定義して、Subプロシージャの中で、ユーザ定義変数を宣言した使い方の一例です。
3.定数の宣言方法
定数とは、変化しない値のことを言います。
もともとVBAが持っている定数のことを「組み込み定数」と言い、ユーザーが勝手に作成する定数のことを「ユーザー定義定数」と言います。
組み込み定数は、とても沢山あるのですが、とくに宣言する必要がなくて使うことができます。
一方、ユーザー定義定数は、きちんと宣言して使用する必要があります。
定数を定義する場合、2つの定義方法があります。それは、標準モジュールの宣言セクションで定義する場合と、プロシージャ内で宣言する場合の2つです。
(1)宣言セクションで定義する
(宣言セクション)
Const tax As Single = 0.1
------------------
Sub ユーザー定義定数1()
Dim kakaku As Long
Dim kosu As Integer
Dim goukei As Long
Dim goukei_zeikomi As Long
kakaku = 1000
kosu = 3
goukei = kakaku * kosu
goukei_zeikomi = goukei * (1 + tax)
Debug.Print goukei '3000
Debug.Print goukei_zeikomi '3300
End Sub
宣言セクションで宣言する場合、以下の2つの書き方があります。
Public Const tax As Single = 0.1
Private Const tax As Single = 0.1
[Public / Private] Const 変数名 As 型 = 値、という書き方になります。
Publicを付けて定数を宣言すると、他の標準モジュールでも有効な定数となります。一方、Privateを付けて定数を宣言すると、現在の標準モジュールのみで有効な定数となります。なお、単に「Const tax As Single = 0.1」として宣言すると「Public」が省略されたものとみなされます。
(2)プロシージャ内で宣言する
Sub ユーザー定義定数2()
Dim kakaku As Long
Dim kosu As Integer
Dim goukei As Long
Dim goukei_zeikomi As Long
Const tax As Single = 0.1
kakaku = 1000
kosu = 3
goukei = kakaku * kosu
goukei_zeikomi = goukei * (1 + tax)
Debug.Print goukei
Debug.Print goukei_zeikomi
End Sub
プロシージャ内で宣言する場合は、PublicやPrivateは付加せず、Constから記述するようにしてください。
Const 変数名 As 型 = 値
プロシージャ内で、定数を宣言した場合、定数の有効範囲は、そのプロシージャ内に限定されます。このような意味で、プロシージャ内での定数宣言の意味はそれほどないのですが、定数であることがすぐにわかるということで、用いられることが多いと思います。
4.列挙型の宣言方法
列挙型というのは、定数をまとめて宣言する方法です。ただし、列挙型で宣言できるのは、整数値のみです。
また、列挙型の宣言は標準モジュールの宣言セクションで行います。
(宣言セクション)
Private Enum youbi
日 = 1
月
火
水
木
金
土
End Enum
---------------------------
Sub 列挙型()
Debug.Print youbi.日 '1
Debug.Print youbi.月 '2
Debug.Print youbi.火 '3
Debug.Print youbi.水 '4
Debug.Print youbi.木 '5
Debug.Print youbi.金 '6
Debug.Print youbi.土 '7
End Sub
宣言セクションで列挙型を宣言する場合、PublicとPrivateのどちらかを先頭につけて宣言します。なお、省略した場合はPublicとなります。
[Private またはPublic] Enum 列挙型名
メンバー名 = [整数]
メンバー名
メンバー名
メンバー名
メンバー名
End Enum
Privateの有効範囲はそのモジュール内のみ、Publicの有効範囲は他のモジュールでも有効になります。
最初のメンバーの右に「= 1」などの整数値を書きます。
そうすると、次のメンバーは自動的に「2」、その次のメンバーは「3」というように連番が割り振られます。
ただし、すべてのメンバーに別々の数字を割り当てることも可能です。
(宣言セクション)
Private Enum Yakult
塩見 = 9
山崎 = 31
山田 = 1
村上 = 55
サンタナ = 25
中村 = 27
オスナ = 13
End Enum
----------------------------
Sub 列挙型2()
Debug.Print Yakult.塩見 '9
Debug.Print Yakult.山崎 '31
Debug.Print Yakult.山田 '1
Debug.Print Yakult.村上 '55
Debug.Print Yakult.サンタナ '25
Debug.Print Yakult.中村 '27
Debug.Print Yakult.オスナ '13
End Sub
一般的に、列挙型がよく使われるのは、列項目名をメンバーとして定義して、その項目が何列目にあたるかということを導くために用いることが多いと思います。
表のデータ全体を読み込んでマクロを組むような場合、どの項目が何番目の列に相当するかという点は、いちいち確認が必要になります。
また、列項目の並びが変更されたり、新しい列が追加された時でも、列挙型の宣言を更新するだけで、列が何番目にあるかを導けるので、プロシージャの中身を変更する必要がなくなります。
さらに、先頭の番号も変更できますので、読み込む表がA列から始まっていなくても定義することが可能となります。
5.配列変数の宣言方法
配列変数というのは、1つの変数の中に複数の値を持てるしくみを持っている変数のことです。
上の図の卵のパックを考えてみてください。
1つの卵パックに10個の卵が入っています。
この卵パックに相当するものが配列変数となります。
この場合、5個の並びが2列あるということで、2次の配列変数と言えます。
これをマクロ風に書くとすれば、以下のようになります。
Dim 卵パック(5,2) As 卵
もしくは
Dim 卵パック(2,5) As 卵
次に、以下のようなエクセルの表を考えてみたいと思います。
A | B | C | D | |
1 | 販売日 | 商品名 | 単価 | 販売個数 |
2 | 2022/7/25 | りんご | 250 | 20000 |
3 | 2022/7/26 | みかん | 100 | 50000 |
毎日の商品の売上の明細表みたいなものです。
3つの行(1~3)と4つの列(A~D)で構成されている表になります。
この表全体を1つの配列変数に入れるとしたら、どのような配列変数になるでしょうか?
項目名は日本語ですが、データには日付や文字列や数字があり、1つの型には収まりそうにありません。しかし、配列変数を用いると上記の表の値を1つの配列変数に含めることができます。
Sub 配列変数()
Dim uriage(3, 4) As Variant
Dim r As Integer
Dim c As Integer
For r = 1 To 3 '3行ある
For c = 1 To 4 '4列ある
uriage(r, c) = Cells(r, c).Value
Debug.Print uriage(r, c) '1つ1つのデータを表示
Next c
Next r
End Sub
まず最初に配列変数の宣言ですが、
「uriage(3,4)」の意味を考えてみましょう。
配列変数を定義する場合は、変数名の後ろに括弧で、入れ物の数を書くようになっています。
例えば「x(3)」という配列は、3個の入れ物があるように思いますが、実際には4個の入れ物を持っています。ここが配列変数のややこしいところなのですが、以下のように考えてください。
「x(3)」・・・x(0), x(1), x(2), x(3)という4つの入れ物を持つことになります。
つまり、x(0)が先頭にあることで、全部で4つの入れ物になるということです。
この「x(3)」は1次の配列変数と呼びます。
しかし、実際のエクセルマクロで表のデータを配列変数に読み込む場合、0行や0列は存在しないので、uriage(3,4)のように3行分×4列分というように書いた方がイメージしやすいと思います。
つまり、uriage(3,4)は以下のようなマトリックスになります。
(0,0) | (0,1) | (0,2) | (0,3) | (0,4) |
(1,0) | (1,1) | (1,2) | (1,3) | (1,4) |
(2,0) | (2,1) | (2,2) | (2,3 | (2,4) |
(3,0) | (3,1) | (3,2) | (3,3) | (3,4) |
行や列に0を含んでいる部分は使わないで、黄色い部分を使ってエクセルの表のデータを読み込んでいると思ってください。
for ~ nextという構文は繰り返しを行う構文です。
エクセルの表を配列変数に読み込む場合は、行を1つ1つ変えながら、列のデータを1つ1つ読み込んでいくと手法をとります。
6.固定配列と動的配列の宣言方法
(1)固定配列
固定配列というのは、宣言する時点で配列に含まれる入れ物の個数がわかっている場合に使われます。
Dim uriage(3, 4) As Variant
固定配列の宣言方法は、もう1つあって、上記の表で0行や0列の部分の入れ物は使用しないというような場合は、以下のように書くこともできます。
Dim uriage(1 To 3, 1 To 4) As Variant
この場合、「1 To 3」とは、「1から始まって3で終わる」と考えてください。
(2)動的配列
動的配列というのは、最初に宣言する時点では、配列の入れ物の個数がわからないような場合に使われるものです。
プログラムの途中で入れ物の個数がわかった段階で定義したり、途中で入れ物の個数を変化させたり、一旦すべてクリアして、再定義したりできる配列変数です。
具体的には以下のような宣言になります。
Sub 動的配列()
Dim uriage As Variant
Dim rmax As Integer
rmax = Range("A100").End(xlUp).Row '行の個数を求めている
ReDim uriage(rmax, 4)
Debug.Print rmax
End Sub
最初は、「Dim uriage As Variant」と宣言した時には、配列の個数は宣言していません。その後、行の個数を求めてから、「ReDim uriage(rmax, 4)」として再定義しています。
なお、配列宣言をVariant型で行う場合は、「Dim uriage As Variant」でも大丈夫ですが、配列宣言を固定の型で行う場合は「Dim uriage() As String」というように「()」を変数の後につけるようにしてください。
次に、動的配列の入れ物の個数を途中で変更する例になります。
Sub 動的配列2()
Dim namae() As String
ReDim namae(1 To 2)
namae(1) = "佐藤"
namae(2) = "鈴木"
ReDim Preserve namae(1 To 3)
namae(3) = "加藤"
Debug.Print namae(1) '佐藤
Debug.Print namae(2) '鈴木
Debug.Print namae(3) '加藤
End Sub
ここでは、配列の再宣言(ReDim)を2回行っています。
2回目の宣言では、「ReDim Preserve」としていますが、この「Preserve」を入れることで、それまでの格納したデータを保持して再宣言するという意味になります。
ちなみに、「Preserve」を入れないで2回目を宣言した場合は、その前に格納したデータは消去されてなくなります。
次に、動的配列に直接データを入れることが出来る「Array関数」を見ておきましょう。
Sub 動的配列3()
Dim uriage As Variant
uriage = Array(#7/25/2022#, "りんご", 250, 20000)
Debug.Print uriage(0) '2022/07/25
Debug.Print uriage(1) 'りんご
Debug.Print uriage(2) '250
Debug.Print uriage(3) '20000
End Sub
Array関数を使うことで、配列の入れ物の個数を定義することなしに、データを入れることができます。
7.配列変数のデータ削除方法
配列変数に格納されたデータを削除する場合は、「Erase」を使います。
Sub 配列の削除1()
Dim x(3) As String
x(0) = "りんご"
x(1) = "なし"
x(2) = "柿"
x(3) = "ぶどう"
Debug.Print x(0), x(1), x(2), x(3) 'りんご なし 柿 ぶどう
Erase x
Debug.Print x(0), x(1), x(2), x(3) '空白を出力
End Sub
固定配列の場合は、「Erase」を行うと、配列の中のデータはクリアされますが、配列の入れ物はそのまま残ります。
一方、動的配列の場合は、「Erase」を行うと、配列の入れ物ごと消去されます。
Sub 配列の削除2()
Dim namae() As String
ReDim namae(1 To 2)
namae(1) = "佐藤"
namae(2) = "鈴木"
Erase namae
Debug.Print namae(1) 'エラーになってストップ
End Sub
動的配列を「Erase」した場合は、再度「ReDim」して配列の個数を宣言するようにしてください。
8.まとめ
今回は、変数の宣言方法について書いてみましたが、かなり長くなったような気がします。
エクセルマクロの場合、変数を定義しなくてもマクロが動く場合もありますが、途中でエラーが起きやすくなりますので、必ず変数は使用する前に宣言をするようにしてください。
なお、変数宣言を忘れないために、VBEの設定を変更しておく方法があります。
VBEの画面のメニューから「ツール」⇒「オプション」として、出てくる画面の「編集タブ」の中に「変数の宣言を強制する」というチェックボックスがありますので、ここにチェックを入れてOKボタンを押します。
この設定にしておいてから、標準モジュールを追加すると、その宣言セクションに「Option Explicit」という1文が表示されてきます。
この宣言をしておくことで、変数宣言をしないで変数を使用した時に、警告が出てくるようになります。
エクセルマクロでは、配列変数を使うと、マクロの速度も速くなって、効率的なプログラムを作ることができるようになります。
また、変数の型についても、しっかりと意識して宣言するようにしてください。特に数字の型については、正しく設定しないと異なる結果になるので注意してください。
次回のエクセルマクロは、繰り返しや条件分岐などを行う構文について説明する予定です。