VBAで「Subは書けるようになったけれど、Functionプロシージャはいつ使うの?」と感じていませんか。
SubとFunctionは見た目が似ていますが、役割がはっきり違います。この違いを理解すると、コードがぐっと整理しやすくなります。
この記事では、Functionプロシージャの基本構文から戻り値の返し方、呼び出し方、SubとFunctionの使い分けまでを解説します。引数のByVal/ByRefの違いや、複数の値を返す設計パターン、エラー処理の組み込み方も実務コードで紹介します。最後にExcelのLAMBDA関数との使い分けも整理しました。
VBAのFunctionプロシージャとは?
VBAのFunctionプロシージャは、処理を実行して結果(戻り値)を呼び出し元に返すためのプロシージャです。
一方、Subプロシージャは処理を実行するだけで、結果を返しません。この「結果を返せるかどうか」が両者の最大の違いです。
たとえば「消費税を計算する」処理を考えてみましょう。計算した税込金額を呼び出し元で受け取って使いたいですよね。こういうときにFunctionが活躍します。
SubとFunctionの違い
まずは両者の違いを表で整理します。
| 項目 | Subプロシージャ | Functionプロシージャ |
|---|---|---|
| 役割 | 処理を実行する | 処理を実行して結果を返す |
| 戻り値 | なし | あり |
| 開始と終了 | Sub ~ End Sub | Function ~ End Function |
| 呼び出し方 | 名前 引数 または Call 名前(引数) | 変数 = 名前(引数) |
| 主な用途 | セルの書き込み・書式設定など | 計算結果・判定結果を返す |
ざっくり言うと、「何かを実行したいだけならSub」「結果を受け取りたいならFunction」という使い分けになります。
プロシージャの種類全体については「VBAのプロジェクト・モジュール・プロシージャの違いと役割」でも解説しています。VBAの構成要素を整理したい方はあわせてご覧ください。
Functionプロシージャの基本構文と戻り値の返し方
Functionプロシージャの基本構文は次のとおりです。
Function 関数名(引数) As 戻り値の型
'--- 処理を書く ---
関数名 = 戻り値
End Function
ポイントは2つあります。
As 戻り値の型で、返す値の型を指定する関数名 = 戻り値の形で、関数名に結果を代入する
この「関数名に値を代入する」のが、Functionで戻り値を返す方法です。Subにはない、Function特有の書き方です。
実際に書いてみる
消費税の税込金額を計算するFunctionを作ってみましょう。
Function 税込計算(金額 As Long) As Double
税込計算 = 金額 * 1.1
End Function
As Double で戻り値が小数も扱える数値型であることを指定しています。そして 税込計算 = 金額 * 1.1 で、計算結果を関数名に代入しています。これで税込金額が呼び出し元に返ります。
税率1.1を掛けると小数が出ることがあるため、戻り値の型は整数型(Long)ではなく、小数を扱えるDouble型にしておくのが安全です。整数型で受け取ると小数部分が丸められ、金額がずれてしまう場合があります。
ここからは実際にコピペして動かせるコードを紹介していきます。VBE(Visual Basic Editor)を開いて、標準モジュールに貼り付けてみてください。
VBEの起動とコードの準備
VBEはExcelからすぐに開けます。起動方法は次のとおりです。
- ショートカットキー: Alt + F11 を押す(最も手軽)
- リボンから: 「開発」タブ →「Visual Basic」をクリック
「開発」タブが表示されていない場合は、次の手順で追加できます。
- 「ファイル」→「オプション」を開く
- 「リボンのユーザー設定」を選択
- 右側の一覧で「開発」にチェックを入れて「OK」
VBEが開いたら、「挿入」メニュー →「標準モジュール」を選択してください。コードの貼り付け先ができます。
戻り値の代入を忘れるとどうなるか
注意点として、関数名 = 戻り値 の代入を忘れると、戻り値はその型の既定値になります。数値型なら0、文字列型なら空文字が返ってしまいます。
Function 税込計算(金額 As Long) As Double
'--- 代入を忘れた例。戻り値は常に0になる ---
Dim 結果 As Double '計算結果を入れる変数
結果 = 金額 * 1.1
End Function
この例では 結果 という変数に計算しただけで、関数名 税込計算 に代入していません。そのため戻り値は0のままです。エラーは出ないので、原因に気づきにくいミスです。戻り値は必ず関数名に代入しましょう。
Functionプロシージャの呼び出し方
作ったFunctionは、別のプロシージャから呼び出して使います。呼び出し方には2通りあります。
戻り値を受け取る呼び出し方
戻り値を変数で受け取るときは、カッコを付けて 変数 = 関数名(引数) と書きます。
Sub テスト()
Dim 結果 As Double '税込金額を受け取る変数
結果 = 税込計算(1000) '戻り値は1100になる
MsgBox "税込金額は " & 結果 & " 円です"
End Sub
税込計算(1000) の戻り値1100が、変数 結果 に代入されます。これがFunctionの最も基本的な使い方です。
戻り値を使わずに呼び出す方法
戻り値を使わずに、Subのように実行だけしたい場合は、カッコなしで 関数名 引数 と書くか、Call 関数名(引数) と書きます。
Sub テスト2()
税込計算 1000 '戻り値は捨てられる
End Sub
このとき戻り値1100は受け取られず、捨てられます。Functionは戻り値を受け取らない使い方もできますが、それならSubで書くほうが自然です。
カッコの使い方には注意が必要です。戻り値を受け取るときはカッコを付ける、実行だけならカッコを付けないと覚えておきましょう。
SubとFunctionの使い分け基準
「どちらで書けばいいか迷う」という方のために、使い分けの基準を整理します。
判断の軸はシンプルです。「呼び出し元で結果を受け取って使うか」で決めます。
- 結果を受け取って使う → Function(計算結果・判定結果・変換結果など)
- 実行するだけで結果は要らない → Sub(セルへの書き込み・書式設定・並べ替えなど)
具体例で見てみましょう。
| やりたいこと | 適したプロシージャ | 理由 |
|---|---|---|
| 税込金額を計算して受け取る | Function | 計算結果を呼び出し元で使う |
| セルA1に値を書き込む | Sub | 実行するだけで戻り値は不要 |
| 文字列が有効なメールアドレスか判定する | Function | True/Falseの判定結果を返す |
| シート全体を並べ替える | Sub | 並べ替えを実行するだけ |
| 2つの日付の営業日数を計算する | Function | 日数を計算して返す |
迷ったときは「この処理の結果を、別の場所で使うか」を自問してみてください。使うならFunctionです。
引数の渡し方|ByValとByRefの違い
Functionに値を渡す「引数」には、2つの渡し方があります。ByVal(値渡し)とByRef(参照渡し)です。
この違いを知らないと、思わぬバグに悩まされることがあります。しっかり押さえておきましょう。
ByValとByRefの違い
両者の違いは「呼び出し元の変数そのものを渡すか、コピーを渡すか」です。
| 渡し方 | 渡すもの | Function内で変更すると | 既定値 |
|---|---|---|---|
| ByVal(値渡し) | 変数のコピー | 呼び出し元は変わらない | いいえ |
| ByRef(参照渡し) | 変数そのもの | 呼び出し元も変わる | はい |
VBAでは引数の指定を省略すると、自動的にByRef(参照渡し)になります。この点が事故のもとになりやすいので注意が必要です。
実際の動作を比べる
次のコードで、ByValとByRefの違いを確認してみましょう。
Function ByVal版(ByVal n As Long) As Long
n = n + 100 '引数を変更する
ByVal版 = n
End Function
Function ByRef版(ByRef n As Long) As Long
n = n + 100 '引数を変更する
ByRef版 = n
End Function
Sub 比較テスト()
Dim x As Long '元の値を入れる変数
x = 10
MsgBox ByVal版(x) & " / 元の値: " & x '結果は「110 / 元の値: 10」
x = 10
MsgBox ByRef版(x) & " / 元の値: " & x '結果は「110 / 元の値: 110」
End Sub
ByVal版では、呼び出し元の x は10のまま変わりません。コピーを渡しているからです。一方、ByRef版では x が110に書き換わってしまいます。変数そのものを渡しているためです。
安全のためのおすすめ
意図せず呼び出し元の変数を書き換える事故を防ぐには、変更する必要のない引数にはByValを明示するのがおすすめです。
Function 税込計算(ByVal 金額 As Long) As Double
税込計算 = 金額 * 1.1
End Function
こうしておけば、Function内で 金額 をうっかり変更しても、呼び出し元には影響しません。コードの安全性が高まります。なお戻り値の型は、先ほどと同じく小数を扱えるDouble型にしている点もあわせて確認してください。
複数の戻り値が必要なときの設計パターン
Functionの戻り値は1つだけです。しかし実務では「計算結果と一緒に、成否や付随情報も返したい」という場面があります。そんなときの設計パターンを3つ紹介します。
パターン1: ByRef引数で複数の値を返す
ByRefの「呼び出し元の変数を書き換える」性質を逆手に取る方法です。返したい値の数だけByRef引数を用意します。
Function 割り算(ByVal a As Long, ByVal b As Long, ByRef 余り As Long) As Long
割り算 = a b '商を戻り値で返す
余り = a Mod b '余りはByRef引数で返す
End Function
Sub パターン1テスト()
Dim あまり As Long '余りを受け取る変数
Dim 商 As Long '商を受け取る変数
商 = 割り算(17, 5, あまり)
MsgBox "商は " & 商 & "、余りは " & あまり '結果は「商は3、余りは2」
End Sub
主たる結果(商)は戻り値で、付随する結果(余り)はByRef引数で返しています。読みやすく、実務でよく使うパターンです。
パターン2: 配列を返す
複数の値をまとめて返したいときは、配列を戻り値にする方法があります。戻り値の型をVariantにします。
Function 最小最大(ByVal 範囲 As Range) As Variant
Dim 結果(1) As Long '0番目に最小、1番目に最大を入れる
結果(0) = WorksheetFunction.Min(範囲)
結果(1) = WorksheetFunction.Max(範囲)
最小最大 = 結果
End Function
Sub パターン2テスト()
Dim v As Variant '配列を受け取る変数
v = 最小最大(Range("A1:A10"))
MsgBox "最小 " & v(0) & " / 最大 " & v(1)
End Sub
関連する複数の値(最小と最大など)をセットで返したいときに向いています。
パターン3: ユーザー定義型を返す
「名前・点数・判定」のような構造化したデータをまとめて返したいときは、ユーザー定義型(Type)を使います。
Type 成績データ
名前 As String
点数 As Long
判定 As String
End Type
Function 成績作成(ByVal nm As String, ByVal pt As Long) As 成績データ
Dim d As 成績データ '組み立て用の変数
d.名前 = nm
d.点数 = pt
If pt >= 60 Then d.判定 = "合格" Else d.判定 = "不合格"
成績作成 = d
End Function
少し高度ですが、関連する複数の項目を意味のあるまとまりとして返せます。データの中身が増えても引数を増やさずに済むのが利点です。
Functionプロシージャにエラー処理を組み込む(On Error)
Functionの中で予期しないエラーが起きると、処理が止まってしまいます。これを防ぐのが On Error によるエラーハンドリングです。
基本のエラー処理パターン
On Error GoTo ラベル を使うと、エラー発生時に指定したラベルへジャンプできます。
Function 安全な割り算(ByVal a As Double, ByVal b As Double) As Double
On Error GoTo エラー処理
安全な割り算 = a / b '0で割るとエラーになる
Exit Function '正常時はここで終了する
エラー処理:
安全な割り算 = 0 'エラー時は0を返す
End Function
このコードのポイントは2つです。
Exit Functionで正常処理を終え、エラー処理部分に入らないようにする- エラーが起きたら
エラー処理ラベルへジャンプし、戻り値に0を設定する
Exit Function を書き忘れると、正常時でもエラー処理のコードが実行されてしまいます。必ず正常系の最後に入れましょう。
成否を呼び出し元に伝える
戻り値で「成功か失敗か」を伝える設計も実務で役立ちます。Boolean型を返し、ByRef引数で計算結果を渡すパターンです。
Function 数値変換(ByVal 文字 As String, ByRef 結果 As Double) As Boolean
On Error GoTo エラー処理
結果 = CDbl(文字) '数値に変換する
数値変換 = True '成功
Exit Function
エラー処理:
数値変換 = False '失敗
End Function
呼び出し元では、戻り値のTrue/Falseで成否を判定し、成功時だけ結果を使えます。エラー処理の考え方はVBAのIf文(条件分岐)とあわせて学ぶと理解が深まります。
ユーザー定義関数(UDF)としてワークシートで使う
標準モジュールに作ったFunctionは、ワークシートのセルからも呼び出せます。これをユーザー定義関数(UDF: User Defined Function)といいます。
SUM関数やIF関数のように、自分だけのオリジナル関数をセルで使えるようになります。
UDFの作り方と使い方
先ほどの税込計算をUDFとして使ってみましょう。標準モジュールに次のFunctionを書きます。
Function 税込(ByVal 金額 As Double) As Double
税込 = 金額 * 1.1
End Function
あとはワークシートのセルに =税込(A1) と入力するだけです。A1の金額に対する税込金額が表示されます。SUM関数と同じ感覚で使えます。
UDFの制約に注意
UDFには重要な制約があります。セルの値の取得や計算はできますが、セルへの書き込みや書式設定はできません。
| UDFでできること | UDFでできないこと |
|---|---|
| セルの値を読み取る | 他のセルに値を書き込む |
| 計算して結果を返す | セルの色や書式を変える |
| 文字列を加工して返す | シートの並べ替えを実行する |
セルを操作したい場合はSubプロシージャを使います。UDFはあくまで「値を計算して返すだけ」と覚えておきましょう。
なお、Privateで宣言したFunctionはワークシートから呼び出せません。UDFにしたいFunctionは Function または Public Function で宣言してください。UDFを使うブックは、必ずマクロ有効ブック(.xlsm)で保存しましょう。
VBA FunctionとExcelのLAMBDA関数の使い分け
Excel 2021やMicrosoft 365では、VBAを使わずにワークシートの数式だけでカスタム関数を作れるLAMBDA関数が登場しました。
「カスタム関数を作るなら、VBAとLAMBDAのどちらを選ぶべきか」は多くの方が悩む点です。実務観点で比較します。
| 観点 | VBA Functionプロシージャ | LAMBDA関数 |
|---|---|---|
| 作成手段 | VBAコード(VBE) | 数式のみ |
| 保存形式 | xlsm(マクロ有効ブック) | xlsx |
| セキュリティ警告 | 出る | 出ない |
| 対応バージョン | VBA対応のExcel全般 | Microsoft 365のみ |
| 可能な処理 | ファイル操作・API呼出・複雑な制御も可 | 数式で書ける範囲 |
| 学習コスト | VBA文法を学ぶ必要あり | Excelの数式知識のみ |
判断基準を実務シーン別に整理すると次のようになります。
- 数式で書ける計算(税込・営業日・評価判定など) → LAMBDA
- xlsxのまま配布したい・マクロ警告を避けたい → LAMBDA
- Excel 2019以前の旧バージョンユーザーがいる → VBA Function
- ファイル操作・正規表現・外部API呼び出しが必要 → VBA Function
おすすめのアプローチは「まずLAMBDAで実装してみて、数式ではできない処理だけVBA Functionに移す」という考え方です。LAMBDA関数の詳しい使い方は「ExcelのLET・LAMBDA関数の使い方」で解説しています。
よくあるエラーと対処法
Functionプロシージャでつまずきやすいエラーと対処法をまとめました。
| エラー・症状 | 原因 | 対処法 |
|---|---|---|
| 戻り値が常に0や空文字になる | 関数名への代入忘れ | 関数名 = 値 の代入を必ず書く |
| 戻り値が反映されない | 関数名と別の変数に代入している | 代入先を関数名に修正する |
| 型が一致しませんエラー | 戻り値の型と受け取る変数の型が違う | 型を揃えるか型変換関数を使う |
| 呼び出し時にコンパイルエラー | 戻り値を受け取るのにカッコがない | 変数 = 関数名(引数) と書く |
| 呼び出し元の変数が勝手に変わる | ByRefで引数を渡している | 変更不要な引数はByValを明示する |
| UDFがセルで動かない(無反応) | UDF内でセル操作をしている | UDFは値を返すだけにする |
マクロを保存するときはファイル形式に注意してください。「Excelマクロ有効ブック(.xlsm)」を選びましょう。通常の .xlsx で保存すると、書いたマクロが消えてしまいます。
まとめ
この記事では、VBAのFunctionプロシージャについて基本から実務活用まで解説しました。
ポイントをおさらいしておきましょう。
- Functionは 処理を実行して戻り値を返す プロシージャ。Subは戻り値を返さない
- 戻り値は
関数名 = 値の形で代入する。代入忘れに注意 - 呼び出しは 戻り値を受け取るときカッコあり、実行だけならカッコなし
- 結果を受け取って使うならFunction、実行だけならSub で使い分ける
- 引数は 変更不要ならByValを明示 して事故を防ぐ
- 複数の値を返すには ByRef引数・配列・ユーザー定義型 の3パターン
- エラー処理は On Error GoTo + Exit Function で正常系と分離する
- 標準モジュールのFunctionは UDF としてセルでも使える(値を返すだけ)
まずはこの記事のサンプルコードをコピーして動かしてみてください。自分で書いて動かすと、SubとFunctionの違いがすっと腑に落ちますよ。
Functionの基礎を理解したら、次はVBAのプロジェクト・モジュール・プロシージャの違いでコードの整理術を学んでみましょう。条件分岐のVBAのIf文やSelect Case文とあわせて使えば、より実践的なマクロが書けるようになります。数式でカスタム関数を作りたい方はExcelのLET・LAMBDA関数の使い方もあわせてご覧ください。VBA学習全体の進め方はVBA学習の順番とロードマップを参考にしてみてください。
