- 締切済み
二次元配列が、勝手に一次元配列になってしまう
Excelのマクロで、二次元配列を格納したバリアント型を戻り値とする関数を作ったのですが、… 二次元型配列のひとつの要素数が1の時、受け取ったバリアント型変数は、一次元配列になっています。 列ベクトルなら、それもありかな~と思うのですが、なんで、行ベクトルまで、一次元配列にするんだよ~って、困っています。 シートから関数を呼んだ場合は、列ベクトルは列ベクトル、行ベクトルは行ベクトルとして、表示されるので、マクロの中で関数を呼び出した場合も、行列の情報を保持できる方法があるんじゃないかと思ったのですが。 どなたか、ご教示頂けるとありがたいです。 よろしくお願いします。
- みんなの回答 (3)
- 専門家の回答
みんなの回答
- chie65536(@chie65535)
- ベストアンサー率44% (8740/19838)
>一番外側のmyMMultにわたってくる引数が1次元配列になってしまいます 何か勘違いしているようですが、ちゃんと2次元配列で返ってきていますよ。 もし、貴方の言う通り、1次元配列で返って来ているというのであれば lcm = UBound(lhs, 2) や rcm = UBound(rhs, 2) の行で「インデックスが有効範囲にありません。」のエラーが出ます。 返って来た値に対しUBound()を使用し、2番目の引数に「2」を指定してもエラーにならないので「返って来た配列の次元数は、少なくとも2以上」である事が証明されます。 貴方の考えが正しければ「UBound関数でエラーが起きる筈」ですが、そういうエラーが起きていないので、結論は「貴方が勘違いしているだけ」です。 以下の関数は「配列の次元数を返す関数」です。 Function GetArrayRank(ary As Variant) As Integer Dim rnk As Integer If IsObject(ary) Then ary = ary.Value rnk = 0 On Error Resume Next Do While Err.Number = 0 rnk = rnk + 1 tmp = UBound(ary, rnk) Loop On Error GoTo 0 GetArrayRank = rnk - 1 End Function この関数に「返って来た値」を引数に渡して「1」が返って来たら、貴方の言うとおり「1次元配列が返って来ている」でしょうけど、間違いなく「2しか返って来ない」ですよ。 もし「GetArrayRankで1が返って来るんですが」って場合は、実際に書いたコードを添えて補足質問を投稿して下さい。 因みに ' LHS count If (IsObject(lhs)) Then lrm = lhs.Rows.Count lcm = lhs.Columns.Count Else lrm = UBound(lhs, 1) lcm = UBound(lhs, 2) End If ' RHS count If (IsObject(rhs)) Then rrm = rhs.Rows.Count rcm = rhs.Columns.Count Else rrm = UBound(rhs, 1) rcm = UBound(rhs, 2) End If の部分は ' LHS count If (IsObject(lhs)) Then lhs = lhs.Value lrm = UBound(lhs, 1) lcm = UBound(lhs, 2) ' RHS count If (IsObject(rhs)) Then rhs = rhs.Value rrm = UBound(rhs, 1) rcm = UBound(rhs, 2) の方が良いです。 これは「オブジェクトが渡されたら、オブジェクトじゃ無くしてしまえ」と言う方法です。 但し「要素数が多いと代入で負荷が大きくなる」ので、要素数が少ない場合にのみ使用します。
- chie65536(@chie65535)
- ベストアンサー率44% (8740/19838)
追記。 Function SUB1() Dim ary1 As Variant Dim str As String ary1 = SUB2() str = "" For i = 0 To 0 For j = 0 To 9 str = str & ary1(i, j) & "," Next str = str & Chr(13) & Chr(10) Next MsgBox str SUB1 = ary1 End Function Function SUB2() Dim ary2(0 To 0, 0 To 9) As Variant For i = 0 To 0 For j = 0 To 9 ary2(i, j) = (i + 1) * (j + 1) Next Next SUB2 = ary2 End Function のように、行数を1にしても、何も問題は起きませんし Function SUB1() Dim ary1 As Variant Dim str As String ary1 = SUB2() str = "" For i = 0 To 9 For j = 0 To 0 str = str & ary1(i, j) & "," Next str = str & Chr(13) & Chr(10) Next MsgBox str SUB1 = ary1 End Function Function SUB2() Dim ary2(0 To 9, 0 To 0) As Variant For i = 0 To 9 For j = 0 To 0 ary2(i, j) = (i + 1) * (j + 1) Next Next SUB2 = ary2 End Function のように、列数を1にしても、何も問題は起きません。 (前の回答では、0行目、0列目の値が全部0になるので、掛け算の式を変更してあります) どのような状況で再現するのか、補足をお願いします。 因みに「関数から値を受け取る変数」は「配列じゃない、ただのVariant変数」にしないといけませんよ。 もしかして、受け取る変数を配列にしてたりしませんよね?(配列にすると「配列では受け取れない」って意味のエラーになる筈だけど)
- chie65536(@chie65535)
- ベストアンサー率44% (8740/19838)
EXCEL2003だと、以下のコードがちゃんと動くので「特に問題はない」と思います。 Function SUB1() Dim ary1 As Variant Dim str As String ary1 = SUB2() str = "" For i = 0 To 9 For j = 0 To 9 str = str & ary1(i, j) & "," Next str = str & Chr(13) & Chr(10) Next MsgBox str SUB1 = ary1 End Function Function SUB2() Dim ary2(0 To 9, 0 To 9) As Variant For i = 0 To 9 For j = 0 To 9 ary2(i, j) = i * j Next Next SUB2 = ary2 End Function どういう場合に問題が出るのでしょう?
お礼
分かった気がします。 一次元ベクトルは、行ベクトルと考えれば、全て、整合てきですね。 ありがとうございました。
補足
ご教示ありがとうございます。 会社から書き込めないもので遅くなりまして申し訳ありません。 質問の書き方がよくなかったですが、関数の複数回の呼び出し等のケースです。 たとえば、簡単な例では、行列の掛け算で、VBAコードは、 Function myMMult(lhs As Variant, rhs As Variant) Dim lr As Long, lc As Long, lcm As Long, lrm As Long Dim rc As Long, rcm As Long, rrm As Long Dim ret() As Double, vv As Double ' LHS count If (IsObject(lhs)) Then lrm = lhs.Rows.Count lcm = lhs.Columns.Count Else lrm = UBound(lhs, 1) lcm = UBound(lhs, 2) End If ' RHS count If (IsObject(rhs)) Then rrm = rhs.Rows.Count rcm = rhs.Columns.Count Else rrm = UBound(rhs, 1) rcm = UBound(rhs, 2) End If ' Check consistency If (rrm <> lcm) Then myMMult = "Inconsistent Matrixes" Exit Function End If ReDim ret(1 To lrm, 1 To rcm) For lr = 1 To lrm For rc = 1 To rcm vv = 0 For lc = 1 To lcm vv = vv + lhs(lr, lc) * rhs(lc, rc) Next lc ret(lr, rc) = vv Next rc Next lr myMMult = ret End Function です。 ここで、SheetのCellから、 =myMMult(myMMult(B3:C3,E3:F4),myMMult(H3:I4,K3:K4)) 様な形で呼び出すと、一番外側のmyMMultにわたってくる引数が1次元配列になってしまいます(まあ、このケースでは1次元なんですけど、両方とも列ベクトルになっています) 実際には、行列の掛け算だけではなく、すべてのデータを2次元配列として扱いたいのです。その時、行または列の要素数が1になった場合に、1次元配列になってしまうのを避ける方法はありますでしょうか?