VBAのFunctionプロシージャの使い方|SubとFunctionの違いと戻り値の返し方

スポンサーリンク

VBAで「Subは書けるようになったけれど、Functionプロシージャはいつ使うの?」と感じていませんか。

SubとFunctionは見た目が似ていますが、役割がはっきり違います。この違いを理解すると、コードがぐっと整理しやすくなります。

この記事では、Functionプロシージャの基本構文から戻り値の返し方、呼び出し方、SubとFunctionの使い分けまでを解説します。引数のByVal/ByRefの違いや、複数の値を返す設計パターン、エラー処理の組み込み方も実務コードで紹介します。最後にExcelのLAMBDA関数との使い分けも整理しました。

VBAのFunctionプロシージャとは?

VBAのFunctionプロシージャは、処理を実行して結果(戻り値)を呼び出し元に返すためのプロシージャです。

一方、Subプロシージャは処理を実行するだけで、結果を返しません。この「結果を返せるかどうか」が両者の最大の違いです。

たとえば「消費税を計算する」処理を考えてみましょう。計算した税込金額を呼び出し元で受け取って使いたいですよね。こういうときにFunctionが活躍します。

SubとFunctionの違い

まずは両者の違いを表で整理します。

項目SubプロシージャFunctionプロシージャ
役割処理を実行する処理を実行して結果を返す
戻り値なしあり
開始と終了Sub ~ End SubFunction ~ 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」をクリック

「開発」タブが表示されていない場合は、次の手順で追加できます。

  1. 「ファイル」→「オプション」を開く
  2. 「リボンのユーザー設定」を選択
  3. 右側の一覧で「開発」にチェックを入れて「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実行するだけで戻り値は不要
文字列が有効なメールアドレスか判定するFunctionTrue/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学習の順番とロードマップを参考にしてみてください。

タイトルとURLをコピーしました