はじめてのプログラミング#005

  • SYSTEM
  • 2022年07月05日 22時53分

前回(はじめてのプログラミング#004)の投稿では、「プログラムの共通化」であるモジュールについて解説しました。
標準モジュールを使いプログラムを共通化することで、仕様変更にも柔軟に対応できるようになります。

モジュール化と切り離せない関係となるパラメータ(引数)について詳しく解説して行きたいことですが、パラメータは変数の受け渡しであるため、まずは変数について理解する必要があります。

と言うことで、今回は変数について解説していきます。

目次

  1. 変数と定数
    1. 変数
    2. 定数
    3. 意味のある名前付け
  2. ExcelVBAにおいて Option Explicit は必須
  3. 変数・定数の型
    1. 型の一覧
    2. 数値の型
    3. 文字の型
    4. 日付の型
    5. オブジェクト型
    6. バリアント型
  4. アクセス修飾子
    1. プロシージャ内だけで使う場合
    2. オブジェクト内で使う場合
    3. ブック全体で使う場合
  5. 変数・定数の競合
    1. DimとPrivateの競合
    2. DimとPublicの競合
    3. PrivateとPublicの競合
    4. PublicとPublicの競合
  6. 変数と定数の名前
    1. ハンガリアン記法とは
    2. コーディング規約の重要性
    3. ハンガリアン記法の弊害
  7. まとめ

1.変数と定数

どの言語のプログラムを学ぶにしても、変数と定数は必ず使用します。

1-1.変数

変数とは「変な数」ではなく「値が変わる数」です。
「数」と表現すると「数字だけ」と思われるかも知れませんが、文字や日付、時刻、シートやブックなどのオブジェクトなど基本的になんでも扱うことができます。

変数を一言で表すと「値を変えることができる入れ物」です。

ExcelVBAで変数を使用する際には、
「数字を入れる変数A」
「文字を入れる変数B」
「日付を入れる変数C」
のように、使用する変数の名前と変数が扱える型を宣言する必要があります。
Dim が 変数を宣言するときに使用する命令です。
Dim 変数名 As 型 のように書きます。

Dim A As Integer ' 数字を入れる変数A
Dim B As String  ' 文字を入れる変数B
Dim C As Date    ' 日付を入れる変数C

1-2.定数

変数が「値を変えることができる入れ物」なのに対して、定数は「値を変えることができない入れ物」です。

値を変えることができない・・・
そんな入れ物、どのような場面で使われるのでしょうか?

次にサンプルは、
「変数Aの値が12だったら、処理●を実行しなさい」
のと言う、プログラムになります。

Dim A As Integer

If A = 12 Then
    ' 処理●を実行
End If

さて、この「12」は何を意味するでしょうか?
「12時」「12月」「12歳」もしくは「iPhone12」かもしれません。

このように、プログラムの中に値を直接書くことを「ハードコーディング」と言います。
ハードコーディングはプログラミングの現場において、とても嫌がられます。
その理由は、「その値の意味が分からない」からです。

そこで登場するのが定数です。

先ほどのプログラミングを定数を使って書き換えると、次のようになります。

Const が定数を宣言するときの命令です。
Const 定数名 As 型 = 値 のように書きます。

Const LAST_MONTH As Integer = 12
Dim A As Integer

If A = LAST_MONTH Then
    ' 処理●を実行
End If

これならどうでしょう?
12の意味が分かった気がしませんか?
「変数Aの値がLAST_MONTH(最終月)だったら、処理●を実行しなさい」のプログラミングだと理解できます。
値に名前を付けることで、プログラムの可読性を向上させることができます。

定数には可読性を向上させる以外にも、メリットがあります。

次に示したサンプルは、円の円周と面積を求めるプログラムです。
ここで3.14と言うハードコーディングがあります。これは、説明するまでもなく円周率です。

精度を上げるために円周率を 3. 1415926と変更する場合、2カ所変更する必要があります。

' 円周
Enshu = Hankei * 2 * 3.14
' 面積
Menseki = Hankei * Hankei * 3.14
' 円周
Enshu = Hankei * 2 * 3.1415926
' 面積
Menseki = Hankei * Hankei * 3.1415926

円周率を定数にした場合はどうでしょうか?

円周率用の定数Piを用意し円周、面積の計算式に定数を使用していれば、精度を上げる場合、定数Piの値1カ所だけ変更するだけになります。

今回のサンプルでは大差ないように見えますが、大規模なプログラムでは数百、数千カ所の変更が必要になる場合があります。
数百、数千カ所あったとしても定数を使用していれば変更するのは1カ所だけです。

'円周率
Pi = 3.14
' 円周
Enshu = Hankei * 2 * Pi
' 面積
Menseki = Hankei * Hankei * Pi
'円周率
Pi = 3.1415926
' 円周
Enshu = Hankei * 2 * Pi
' 面積
Menseki = Hankei * Hankei * Pi

1-3.意味のある名前付け

定数を使用すれば値に名前を付けられる為、プログラムの可読性が上がると説明しました。
そうなると「変数A」も意味が分かりません。「何が最終月だったら・・・」なのでしょうか?

定数でも変数でも、意味のある名前を付けておかないと、他人がプログラムを見たとき、数年後に自分で作ったプログラムを見たとき、理解するのに時間がかかります。

先ほどの例だと「変数A」ではなく「変数currentMonth(今月)」などの名前にしておけば「今月が最終月だったら」の処理ということが一目でわかります。

Const LAST_MONTH As Integer = 12
Dim currentMonth As Integer

If currentMonth = LAST_MONTH Then
    ' 処理●を実行
End If

2.ExcelVBAにおいて Option Explicit は必須

次のプログラムをご覧ください。
一見正常に動作するように見えますが、このプログラムは生涯「処理●を実行」されることはありません。
そしてエラーが発生することもありません。

よく見ると「LAST_MONHT」となっており定数名が間違っています。

Const LAST_MONTH As Integer = 12
Dim currentMonth As Integer

If currentMonth = LAST_MONHT Then
    ' 処理●を実行
End If

この場合、LAST_MONHTが定数なのか変数なのかすら分かりません。
そしてLAST_MONHTは空です。

ExcelVBAの場合、初期設定では変数や定数を宣言しなくても使えるようになっています。
これを防ぐのがOption Explicit命令です。

プログラムの先頭にOption Explicit書くと「変数、定数は必ず宣言しなさい」となります。
そして、宣言されていない変数、定数を使った場合、即座にエラーになります。


Option Explicit の書き忘れを防ぐ為にもExcelの初期設定を変更しておきましょう。

メニューから[ツール]→[オプション]と進み、[編集]タブにある[変数の宣言を強制する]にチェックを入れてください。

3.変数・定数の型

前述のとおり、変数と定数を使用する際に名前を型を宣言する必要があります。
変数は Dim 変数名 As 型、定数は Const 定数名 As 型 = 値 と宣言します。
いずれも型が必要です。

それでは、型にはどんな種類があるのでしょうか?

3-1.型の一覧

型名型指定説明
ブール型BooleanTrueまたはFalse
バイト型Byte0~255までの整数
整数型Integer-32,768~32,767の整数
長整数型Long-2,147,483,648~2,147,483,647の整数
通貨型Currency-922,337,203,685,477.5808 ~ 922,337,203,685,477.5807の固定小数点数
単精度浮動小数点数型Single負の値:約-3.4×10(38乗)~-1.4×10(-45乗)正の値:約1.4×10(-45乗)~1.8×10(38乗)
倍精度浮動小数点数型Double負の値:約-1.8×10(308乗)~-4.0×10(-324乗)正の値:約4.9×10(-324乗)~1.8×10(308乗)
日付型Date日付:西暦100年1月1日~西暦9999年12月31日時刻:0:00:00 ~ 23:59:59
文字列型String任意の長さの文字列
オブジェクト型Objectオブジェクト
バリアント型Variantすべてのデータ

3-2.数値の型

数値を扱える型としてバイト型、整数型、長整数型、通貨型、単精度浮動小数点型、倍精度浮動小数点型があります。

この中でバイト型(Byte)整数型(Integer)長整数型(Long)が扱える数値は整数のみです。
また、バイト型はマイナスを扱うことができません。

次のサンプルでは、整数だけ扱える型に小数点を入れています。
10 ÷ 4 の結果は2.5になるはずですが結果は2となります。
小数点以下切り捨てではなく、四捨五入になることに注意が必要です。
2.5→2、2.51→3となります。

なお、バイト型にマイナスを値を入れるとエラーになります。

この3種類のうち、まずは長整数型(Long)だけ覚えておけば良いでしょう。

Dim A As Byte
Dim B As Integer
Dim C As Long

A = 10 / 4 ' 結果2
B = 105 / 10 ' 結果10
C = 106 / 10 ' 結果11

A = -10 ' 結果エラー

通貨型(Currency)、単精度浮動小数点型(Single)、倍精度浮動小数点型(Double)は小数点以下の数字も扱うことができます。

浮動小数点型は、非常に大きな数字や細かな小数点以下以下を扱うことができますがVBAの仕様上、誤差が発生します。

Dim A As Double
A = 0.1
If A * 3 = 0.3 Then
    ' 処理●を実行
End

一見何の変哲もない計算式ですが、誤差の都合で「処理●を実行」が動きません。

0.1を3倍しても0.3とイコールにならないのです。
0.00000000000000000277555756156289
と言う非常に細かな誤差が出ます。

以上のことから、単精度浮動小数点型(Single)と倍精度浮動小数点型(Double)は使われることがほぼありません。
小数点以下4桁までの制限はありますが、通貨型(Currency)の使用をお勧めします。

3-3.文字の型

文字を扱う型は、文字列型(String)のみです。
このStringには2通りの使い方があります。

1つは、任意の長さの文字列です。
約2億文字まで扱うことができます。

ちなみに文字列はダブルクォーテーション「”」で囲みます。

Dim A As String
A ="あいうえお"

もう1つが、固定の長さの文字列です。
String の後ろに文字数を指定します。

指定文字数を超えた場合は、切り捨てられます。

指定文字数に満たない場合は、半角スペースで埋められます。

Dim A As String * 3
Dim B As String * 10

A = "ABCDE" ' 格納された値は「ABC」
B = "ABCDE" ' 格納された値は「ABCDE     」

3-4.日付の型

日付を扱う型は、日付型(Date)になります。日付だけでなく時刻も扱うことができます。
日付のみ、時刻のみ、日付+時刻を扱うことが可能です。

日付は「年/月/日」(スラッシュ)または「年-月-日」(ハイフン)で区切って表します。
年を省略した場合は現在の年が付加されます。
日付を省略した場合は1日が付加されます。

時刻は「時:分:秒」(コロン)区切って表します。
秒を省略した場合は「:00」が付加されます。

Dim A As Date

A = "2021/1/2"       ' 「2021/01/02」
A = "12:34"          ' 「12:34:00」
A = "2021/1/2 12:34" ' 「2021/01/02 12:34:00」
A = "1/2 12:34"      ' 「2022/01/02 12:34:00」
A = "2022/12 12:34"  ' 「2022/12/01 12:34:00」

3-5.オブジェクト型

ExcelVBAにおけるオブジェクトには、第2回 で解説したようにWorkBookやWorksheet、Cells、Rangeなどがあります。
オブジェクト型変数の場合、他の型とは値の代入方法が異なります。

次に示すサンプルのようにSet命令を使って値を代入します。

変数の値をクリアする場合は、ゼロや空白ではなく Nothing を代入します。

Dim curSheet As Worksheet
Set curSheet = ActiveWorkbook.Worksheets("Sheet1")
Set curSheet = Nothing

3-6.バリアント型

ExcelVBA最強の型であるバリアント型は、言わば「何でも入る型」です。
これまで説明した型が意味のない事になりかねませんが、数値も文字も日付もオブジェクトも何でもこれ1つで対応できます。

ですが自由が故に取り扱いには注意が必要です。

次に示すサンプルをご覧ください。

数値の100と200を足すと300になりますが、ダブルクォーテーションで囲まれた文字を足すと文字が連結されて100200となります。

変数に何が入るか判断付かない状態でバリアント型を使うのは危険なことがわかります。

Dim A As Variant
Dim B As Variant
Dim C As Variant

A = 100
B = 200 
C = A + B  ' 「300」 

A = "100"
B = "200"
C = A + B  ' 「100200」 

また、バリアント型はプログラムの処理速度が若干遅くなります。
例えば1億回処理を繰り返す際、長整数型(Long)を使った場合とバリアント型(Variant)を使った場合で約0.1秒差が出ます。(パソコンの性能に依存します)
小さな積み重ねがプログラムの処理速度に影響を及ぼすの為、可能な限り正しい型を使うことをお勧めします。

4.アクセス修飾子

以前にプロシージャのアクセス修飾子について解説したのを覚えていますでしょうか?
PrivateとPublicです。
他から使う場合はPublic、自分だけが使う場合はPrivateになります。

変数と定数にもPrivateとPublicがあります。

4-1.プロシージャ内だけで使う場合

プロシージャ内だけで変数を使う場合、これまで説明してきた通り Dim を使います。

プロシージャとは、前回説明した通りSubやFunctionのことです。

プロシージャ内でDimで宣言した変数は、プロシージャの処理が終わると、変数の値が消えます。

Private Sub Sample1()
    Dim totalTax As Long
End Sub

定数の場合は、Constを使って宣言します。
プロシージャ内で宣言した変数は、他のプロシージャから使用することはできません。

Private Sub Sample1()
    Dim totalTax As Long
    Const taxRate As Long = 10
End Sub

4-2.オブジェクト内で使う場合

Sheet1だけで使う、標準モジュールだけで使うなどオブジェクトの中だけで使う変数や定数を宣言する場合は、アクセス修飾子にPrivateを指定します。

次に示すサンプルでは、標準モジュール内でPrivate宣言した変数・定数をSheet1で使った場合の例です。

この場合、標準モジュールで宣言した変数・定数はSheet1から見えないた=変数・定数が宣言されていない、と言う扱いになり、結果としてエラーになります。

Option Explicit

Private Const ZEIRITSU As Long = 10
Private TotalTax As Long
Private Sub Sample1()
    TotalTax = 0 ' 変数が宣言されていない為、エラー
End Sub

4-3.ブック全体で使う場合

ブック内のどこからでも使えるようにするアクセス修飾子がPublicです。

定数であれば Public Const 定数名 As 型 = 値
変数であれば Public 定数名 As 型
になります。

Option Explicit

Public Const ZEIRITSU As Long = 10
Public TotalTax As Long
Private Sub Sample1()
    TotalTax = 0 ' Public変数が使える
End Sub

Public変数を使う場合に1点注意が必要です。
それは、ブックを閉じるまで変数の値がクリアされないことです。
前回変数に代入した値が残り続けるため、適切にクリア(数値なら0、文字なら空白)を代入する必要があります。

ブックを開いている間でも、唯一Public変数がクリアされるタイミングがあります。
それはエラーが発生した場合です。
エラーが発生しないように対処することはもちろんですが、万が一エラーが発生した場合でも処理を継続(通常の処理ではなくエラー発生時の処理が継続)できるような対応が必要です。

5.変数・定数の競合

同じ名前の変数・定数が存在した場合、どのようになるでしょうか?

なお、

  • 同一プロシージャ内にDimで同じ名前の変数・定数を宣言したとき
  • 同一モジュール内にPrivateで同じ名前の変数・定数を宣言したとき
  • 同一モジュール内にPublicで同じ名前の変数・定数を宣言したとき

は、問答無用でエラーになります。

Private Sub Sample()
    Dim A As Long
    Dim A As Long
End Sub
Private A As Long
Private A As Long
Private A As Long
Public A As Long

5-1.DimとPrivateの競合

Private A As Long   ' ①

Private Sub Sample()
    Dim A As String ' ②
    A = "あいうえお"
End Sub

Private修飾子で標準モジュール内で使える長整数型の変数Aを宣言しました①。
さらにプロシージャSampleの中でDimで文字列型の変数Aを宣言しました②。
変数Aに文字列を代入した場合、①変数Aが採用されると長整数型の為、エラーになります。

結果としては、プロシージャSampleではDimの方②が使われる為、エラーになりません。

Dimが優先されるからです。

5-2.DimとPublicの競合

Public A As Long ' ①

Private Sub Sample()
    Dim A As String ' ②
    A = "あいうえお"
End Sub

Public修飾子でブック全体で使える長整数型の変数Aを宣言①した場合はどうでしょうか。

この場合もDimが優先される為、エラーにはなりません。

5-3.PrivateとPublicの競合

Public A As Long ' ①
Private A As String ' ②

Private Sub Sample()
    A = "あいうえお"
End Sub

では、この場合はどうでしょうか?
結論としてはエラーになりません。

この場合は、Private修飾子②が優先されます。

5-4.PublicとPublicの競合

Public A As Long ' ①
Public A As String ' ②

Private Sub Sample()
    A = "あいうえお"
End Sub

Public修飾子で同じ名前の変数・定数を宣言した場合はどうでしょうか?

結論としては、これもエラーになりません。

Sampleプロシージャの A = 20 を、
正式に表現すると Module2.A になるからです。

モジュール名が省略できることは前回の解説でお伝えましたが、省略した場合は所属しているモジュール名が自動で付加されます。


Public A As Long ' ①

Private Sub Sample()
    A = "あいうえお"
End Sub
Public A As String ' ②

したがって、①と同じモジュール内にプロシージャSampleを書いた場合はエラーになります。

これはSampleプロシージャで使用される変数がModule1.Aだからです。

6.変数と定数の名前

変数と定数に適切な名前を付けることによって可読性が向上することは、先述の通りです。
可読性が向上することによりプログラムのミスも少なくなります。
システム開発の現場は、複数人がチームとなって1つのシステムを作り上げます。
他の人が見ても何の処理をしているのか容易に判断できる美しいプログラムを書くべきです。

では、適切な名前とは何でしょうか?
「用途がわかる」は勿論のこと、「適用範囲(Dim or Private or Public)がわかる」「型がわかる」と尚、他のプログラマーに親切な変数・定数になります。
その考えから誕生したのが「ハンガリアン記法」です

6-1.ハンガリアン記法とは

ハンガリアン記法とは、変数や定数の先頭または末尾に意味を持った接頭語を付ける表記法です。
この「意味を持った接頭語」をプレフィックスと言います。

次に示すサンプルはハンガリアン記法に則り命名した例です。
Private修飾子による変数宣言の為、プレフィックスとして「pri_」を付けています。
変数の型が「長整数型(Long)」の為、プレフィックスとして「lng_」を付けています。

Private pri_lng_ZeiGaku As Long

このように変数名「pri_lng_ZeiGaku」を見ただけで「PrivateのLong型の税の額」であることがわかります。

「pri_」や「lng_」などのプレフィックスに決まりはありませんし、「Zei」ではなく「Tax」、「Gaku」ではなく「Mony」でも良いです。

またアクセス修飾子は、プレフィックスに頼らず大文字小文字で表現したり、「_(アンダーバー)」を使って表現することも可能です。

このように

  • 定数ならすべて大文字
  • privateなら小文字、アンダーバー区切り
  • Dimなら先頭にアンダーバー

など、大文字小文字アンダーバーの組み合わせによってアクセス修飾子を判別することも可能です。

1点注意が必要なのが、VBAの変数・定数は大文字小文字しないことです。
「taxrate」と「TaxRate」は同じ名前として扱われます

' Private→p/Long→l
Private plTaxRate As Long

' Public→型を大文字/Currency→c
public C_TaxAmount As Currency

' Publicの定数はすべて大文字
Public Const TAX_RATE As Long = 10

' Dim宣言はアンダーバーで始める
Dim _TaxMoney As Long

6-2.コーディング規約の重要性

プログラミングには言語ごとの文法以外のルールはありませんし、これまでに経験した現場によって個人個人名前付けのクセが出てしまいます。
Long型を「lng」とする人もいれば「l」だけの人もいます。先頭につける人、末尾につける人。
ローマ字読みで「Zei」とする人、英語表記で「Tax」とする人。様々です。

そのため、チームでプログラミングする場合には、特にルールが重要です。
このプログラミングをする上でのルールを「コーディング規約」と言います。

【参考】Excel VBAコーディング ガイドライン案
https://qiita.com/mima_ita/items/8b0eec3b5a81f168822d

コーディング規約にも決まりは在りません。チームで話し合い、またはチームリーダーがルールを定めます。
インターネット上にはサンプルが数多くありますので、サンプルを雛型として自チームようにカスタマイズすることも可能です。

6-3.ハンガリアン記法の弊害

システム開発の現場において仕様変更は日常茶飯事です。
もし仮に「消費税率を12.5%にします」となった場合、どうなるでしょうか?

ハンガリアン記法に則り変数名を「pri_lng_ZeiRitsu」としていました。
この変数名から読み取れるのは「Private」の「Long型」です。
Private pri_lng_ZeiRitsu As Long と宣言していることでしょう。
Long型は、整数しか扱うことができません。
消費税率はこれまで3%、5%、8%、10%とすべて整数だったので問題ありませんでした。

今回12.5%と小数点以下が発生しました。これに対応するため
Private pri_lng_ZeiRitsu As Long を Private pri_lng_ZeiRitsu As Single
に変更することになります。
プレフィックスが「lng_」なのに型は「Single」です。
もちろん pri_sin_ZeiRitsu とプレフィックスを変更すれば問題ないのですが、pri_lng_ZeiRitsu変数が数百カ所で使われていた場合、すべてを変更する必要があります。

変更には手間がかかりますし、ミスも起きます。変更した箇所は動作確認を行う必要もあります。
このようにハンガリアン記法では、可読性が向上する一方で、保守性が低下するデメリットがあります。

コーディング規約では、この辺りも含めたルール決めが求められます。

7.まとめ

今回は変数と定数について解説しました。

変数と定数、特に変数を扱えなければプログラミングが出来ない程、重要なものです。
型や宣言の文法が違えど、どのプログラミング言語でも変数・定数は使います。
ぜひ、名前の付け方と一緒に理解を深めてください。

次回は、変数を利用したパラメータ(引数)について解説する予定です。