和風スパゲティのレシピ

日本語でコーディングするExcelVBA

ParamArrayを別関数のParamArrayに渡す方法

引数の数を可変にするParamArrayキーワードで受け取った引数を、
別の関数のParamArrayに渡す方法を解説します。


と、言いたいところですが、私の調べた限りでは、ストレートな方法は無い様です。

どうしても呼び出す元関数を少しいじる必要が出るようですが、
ひとつのヒントとなれば幸いです。

やりたいこと

汎用関数など、いろいろなところで使いまわせる関数を使うとき、
その関数を元に、少し違う動きをする関数」を作りたくなることがあります。


例えば↓こんなもの

Debug.Print 足し算(1,2,3,-4) ' ← 2を返す
Debug.Print 絶対値の足し算(1,2,3,-4) ' ← 10を返す

 
この「絶対値の足し算」を1から自作せず、中で「足し算」を使うようにすれば、
足し算するコードをわざわざ書かなくても、-4を4にするコードだけ書けばよさそうですよね?

元の関数もいじらなくていいから、元の関数を使っている他の箇所に影響も出ないわけです。


これをプログラミング用語で「ラッパー(ラッパー関数、ラップ関数)」とよびます。

ラッパー関数を作るのはとても便利なのですが、
これがParamArrayだと上手くいかないため、何とかしようということです。

元の関数

Function 足し算(ParamArray pArr数値()) As Long
    
    足し算 = 0
    Dim i As Long
    For i = LBound(pArr数値) To UBound(pArr数値)

        足し算 = 足し算 + pArr数値(i)

    Next

End Function

目的の関数(このままではエラー)

Function 絶対値の足し算(ParamArray pArr数値()) As Long

    ' 渡された配列の中身を正の値にする
    Dim i As Long
    For i = LBound(pArr数値) To UBound(pArr数値)
        pArr数値(i) = Abs(pArr数値(i))
    Next

    ' 元の関数を呼ぶ
    絶対値の足し算 = 足し算(pArr数値)

End Function

こんな感じで、ラッパー関数では、
「パラメータだけいじってあとは元の関数におまかせ!」
をやります。

※「絶対値にするついでに足しちゃえばいいのに」と言わないように。あくまでサンプルです。


この手法はとても便利なのですが、
ParamArrayをバトンすると、これではエラーが出るんですね。


何故エラーになるかというと、ラッパー関数に渡した時点では、
ParamArrayは「1」,「2」,「3」,「-4」という4つ数値からなる配列です。

しかし、これをそのまま元の関数に渡したときは、
「1,2,3,-4」という1つの配列からなる配列になってしまうからです。


「配列をParamArrayに渡した」という仕様そのままに、
配列の配列になってしまう訳ですね。

解決策(ただし、元関数をいじることになる)

これを何とかするには、「元の関数を」こう書き替えます。

Function 足し算(ParamArray pArr数値()) As Long
    
    ' 受け取ったParamArrayを直接処理せず、実際に処理に使う配列を作る
    Dim Arr数値 As Variant
    
    ' ParamArrayの第1要素が配列なら、その第1要素を処理配列に入れる
    If IsArray(pArr数値(LBound(pArr数値))) Then
        Arr数値 = pArr数値(LBound(pArr数値))
    
    ' そうでないなら、ParamArrayをそのまま処理配列に入れる
    Else
        Arr数値 = pArr数値
    End If
    
    足し算 = 0
    Dim i As Long
    For i = LBound(Arr数値) To UBound(Arr数値)

        足し算 = 足し算 + Arr数値(i)

    Next

End Function

 
ParamArrayを直接処理せずに、処理用の配列をワンクッション置き、

第1要素が 処理配列に格納するのは
配列である ParamArrayの第1要素
配列でない ParamArrayをそのまま

と場合分けして処理する配列を決めることで、
配列が直接渡された場合に備えています。


やってることは簡単ですし、コメント無くしてOption Base 1 も禁止しているなら、

Dim Arr数値: Arr数値 = IIf(IsArray(pArr数値(0)), pArr数値(0), pArr数値)

と、1行で済むので、対策としては悪くない気はします。


ただ、ラッパーの役割を果たせず、元関数を書き換えてしまっているのは、気持ちよくはないですね。


だいたいは同じ動きをしてくれますが、
完全に同じ挙動というわけにはいかないため注意が必要です。


一番わかりやすい注意点は、「元のParamArrayが、元々配列をうけとる想定の関数」では、当たり前ですがうまくいきません。

また、このコードのままでは、「元のParamArrayに要素を追加して渡す」ことはできません。第1要素が配列だった時点で、第2要素は無視していますからね。


一発解答な方法ではありませんので、関数ごとに微調整は必要になると思います。
ご注意ください。


なお、元の関数に「配列も受け取ることができる」という副産物ができるので、

Debug.Print 足し算(Array(1, 2, 3, 4))
Debug.Print 足し算(Split("1,2,3,4", ","))

このように、配列を渡して動かすことができるようになっています。
これはこれで便利な場面がありますね。


ということで、完全回答でなく申し訳ありませんが、
何かのヒントになれば幸いです。