こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

SGN関数を使った2つの動的配列の初期化判定

Excelのvbaにおいて2つの動的配列を同時に初期化(redimされているか)どうか判定するときに
sgn関数を使うとおかしな現象が起こります。
a,bの動的配列を2つ宣言して、redimを行う前あるいはeraseで開放した後に
sgn(a)=0とsgn(b)=0の結果を評価すると共にTrueが返ってきますが
(Sgn(a) = 0) And (Sgn(b) = 0) を評価すると
なぜかFALSEが返ってきます。
(True And True = FALSEとかいう矛盾した結果が返ってくる)

sgn(a)=0、sgn(b)=0の結果をそれぞれAbool,Bboolというbool型変数に格納してから
Abool And Bboolとすれば、正しい評価は可能なのですが、
(Sgn(a) = 0) And (Sgn(b) = 0)
がFALSEになる理由がわかりません。
この現象が起こる理由を押して下さい。

この現象が発生しているパソコンは2台あります。
OSはWindows 7の32bitと64bit
Excel 2013 32bit (2台とも)
以下はサンプルコードです。

Public Sub test()

Dim a() As Integer
Dim b() As Integer

If Sgn(a) = 0 Then MsgBox "配列aは初期化されていません。"
If Sgn(b) = 0 Then MsgBox "配列bは初期化されていません。"

If (Sgn(a) = 0) And (Sgn(b) = 0) Then
MsgBox "配列a,bは両方共初期化されていません。"
Else
MsgBox "配列a,bはいずれかが初期化されています。"
End If
End Sub

投稿日時 - 2017-12-04 19:29:58

QNo.9404403

困ってます

質問者が選んだベストアンサー

>こちらのやろうとしていることとは少し異なってしまっています(とはいえUboundの後err.Numberで判定すればいいのでこれからはsgn関数を使うのをやめることにします。
論理と結果の表示に矛盾があるのではないでしょうか?
下記のコードで試してください。
Sub test()
Dim a() As Integer
Dim b() As Integer
Dim myarray() As Variant
ReDim myarray(0)
ReDim a(100, 10, 50)
ReDim b(2, 10)
Erase a ' Eraseをコメントアウトして比較します。
Erase b '     〃
On Error Resume Next ' エラー発生時にエラーを解除して次のステップへ
err_a = -1: err_a = UBound(a, 1) ' エラー発生時はerr_aの値がそのまま残る
If err_a = -1 Then ' 配列aをEraseで開放するとUBound(a,1)でエラー発生
MsgBox "配列変数 a は要素数が定義されていません。"
End If
err_b = -1: err_b = UBound(b, 1)
If err_b = -1 Then
MsgBox "配列変数 b は要素数が定義されていません。"
End If
err_a = -1: err_a = UBound(a, 1)
err_b = -1: err_b = UBound(b, 1)
If err_a = -1 And err_b = -1 Then
MsgBox "配列変数 a と b は要素数が定義されていません。"
Else: If err_a = -1 Or err_b = -1 Then MsgBox "配列変数 a と b の何れかの要素数が定義されていません。"
End If
On Error GoTo 0
End Sub

ReDim a(0) で配列の再設定をすると UBound(a,1) でエラーにならず、0が帰ります。
また、ReDim Preserve myarray(10) のようにすると既存の配列値を残して配列をリサイズしますのでReDimでは初期化にならないと思います。
何かの役に立てば幸いです。

投稿日時 - 2017-12-10 20:41:47

お礼

何度もコードを提示してくださりありがとうございます。
確かにこれで目的の動作が可能になります。
これからは提示してくださったコード参考にプログラムを組んでみようと思います。

投稿日時 - 2017-12-11 18:52:33

ANo.4

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.3

>redimしていない場合はUboundを使うとエラーが出てしまいます。
質問に提示のコードについて動作を確認しましたところ下記の誤りがありました。
If Sgn(a) = 0 Then MsgBox "配列aは初期化されていません。"
配列aが未使用のときはSgn(a)で0が帰りますので"初期化されていません。”と判定されます。
ReDim a(0)で配列の初期化をすると0以外の値が帰りますので逆の判定になります。
Sgn(a) = 0 → Sgn(a(0)) のように1つの要素を対象に変更すると0が帰りますが、未使用の場合はエラーになります。(UBoundと似たようなエラーです)

>やはりSGN関数を判定に使わない方がいいのかもしれまんせんね。
下記のコードで試してください。
Dim a(), b() As Integer
ReDim a(10)
For i = 1 To 10
a(i) = i
Next i
'Erase a, b ' コメントアウトを解除したときと比較する
'ReDim a(0), b(0) ' コメントアウトを解除したときと比較する
an = 0: bn = 0
On Error Resume Next
an = UBound(a, 1): bn = UBound(b, 1)
On Error GoTo 0
MsgBox "配列の要素数は a " & an & ",b は" & bn & " です。"
If an <> 0 Then MsgBox "配列aは初期化されていません。"
If bn <> 0 Then MsgBox "配列bは初期化されていません。"
If (an * bn) <> 0 Then MsgBox "配列a,bは両方共初期化されていません。"
If (an + bn) <> 0 Then
MsgBox "配列a,bはいずれかが初期化されています。"
Else: MsgBox "配列a,bは両方共初期化されています。"
End If

投稿日時 - 2017-12-06 08:42:14

お礼

返信が遅くなりまして申し訳ありません。
初期化という言葉使ったのがまずかったのですが、
私が行いたいのは配列が空っぽ(宣言やeraseをしてからredimを一度も指定ない状態)を2つ同時に判定しようとするときにsgn関数を使うとおかしな結果がでるという話でした。そちらが提示されたプログラムを実行してみましたが、Erase a,bをコメントアウト解除した時とredim a(0),b(0)をコメントアウトを解除した時でどちらも「配列a,bは両方共初期化されています。」というメッセージがでてしまっており、こちらのやろうとしていることとは少し異なってしまっています(とはいえUboundの後err.Numberで判定すればいいのでこれからはsgn関数を使うのをやめることにします。ありがとうございました。

投稿日時 - 2017-12-10 03:52:02

ANo.2

>Excelのvbaにおいて2つの動的配列を同時に初期化(redimされているか)どうか判定するときにsgn関数を使うとおかしな現象が起こります。
Sgn関数の戻り値は-1、0、1の3種です。
従って配列の値が0の場合とSgn関数の戻り値が混同しますので初期化の判定には不向きと思います。
UBound関数でサイズを確認する方が確実でしょう。
ReDim a(0)
ReDim b(0)
(UBound(a) = 0) And (UBound(b) = 0) で試してみると良いでしょう。

投稿日時 - 2017-12-05 14:01:26

補足

redimしていない場合はUboundを使うとエラーが出てしまいます。
On error resume next でerr.numberで判定すればいいという意見があるかもしれませんが。
やはりSGN関数を判定に使わない方がいいのかもしれまんせんね。

投稿日時 - 2017-12-05 18:22:10

ANo.1

Sgn関数の引数に配列を渡しているけど、エラーにならないんですか?
VBAのヘルプでは、「任意の数式を指定できる」となっているが配列は対象外では?

投稿日時 - 2017-12-04 19:46:16

補足

動的配列を初期化(redim)されているかどうかを判定するのにsgn関数を使うというのはよく聞くテクニックなのですが、確かにSGN関数の本来の使い方とは異なるようです。だからこそ、どうしてこうなるのか仕組みを理解しておきたいのです。「not not」を使うなんて方法も書いてあるサイトもありますがこちらもどうしてこうなるかはよくわかっておりません。動的配列を初期化されているかどうか明確な方法が他にあるということであればもちろんそちらを使います。

投稿日時 - 2017-12-04 21:08:00

あなたにオススメの質問