[[20170821100541]] 『VBA()の省略について』(T17) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『VBA()の省略について』(T17)

よろしくお願いします。

配列変数?を使用するつもりで、うっかり下記のような記述を
したのですが、現在問題なく処理されてるように思います。
Dim yii
X = (別処理で決定)
ReDim yii(1 To X, 0)
(..処理)

型宣言部「As」は省略可能としても、せめて「Dim yii()」と
すべきかと思うのですが、「()」は省略できるのでしょうか?
(因みに「Dim yii()」としても処理結果は同じです)

< 使用 Excel:Excel2010、使用 OS:Windows7 >


はい、Dim yi だけだと、型も、配列かどうかも、なんでもアリの宣言なので、後の Redim で配列であるのが決まった、という事です。

なお、例えばこの変数が整数型だと判っている場合でも、Dim yii As Long とすると、ReDimでエラー発生します。 これは、Long型である、という指定と一緒に、配列ではない、という事を指定しているからです。

逆に、Dim yii() だと、配列である、という指定だけで型は決まっていないので、これはエラーになりません。

しかしながら、データ型が決まっているならば、文字列なのか整数なのか、宣言時に明示することで、予定外の型のデータならエラー停止してくれるし、後からコードを読むときもどういうデータなのか判りやすくなるので、決まっている範囲で宣言しておくべきですね。
(???) 2017/08/21(月) 10:38


???様、 ご丁寧な解説ありがとうございます。

詳しい知識がないままに先に「Dim A」などと使用予定の変数名を列挙し、
後から型宣言することを覚えてしまっているので、いい教訓になりました。

以後、気をつけるようにしたいと思います。
これからもよろしくお願いいたします。
(T17) 2017/08/21(月) 11:17


 >いい教訓になりました。 
 >以後、気をつけるようにしたいと思います。

 そんな大げさな問題だったんですか? エラーになった訳でもなし・・

 >型宣言部「As」は省略可能としても、せめて「Dim yii()」とすべきかと思うのですが

 ヘルプにはこう書かれています。
       ↓ 
 >ReDim ステートメントは、動的配列を宣言したり、
 >Private、Public、または Dim の各ステートメントにおいて、
 > 次元の添字を省略した空のかっこだけを指定して宣言されている動的配列の、要素数や次元数を変更するときに使います。

 前段は、あらかじめ宣言してある必要さえ無い、と読めますけど。

 先ごろ、他のスレッドで、変数宣言を「先頭にまとめる派」か「使用直前にする派」かの論議がありましたが、
「使用直前にする派」は空カッコだけなんて宣言は多分やらないんでしょうね。

(半平太) 2017/08/21(月) 20:43


 最初の ReDim ステートメントが Preserve キーワードと併用の場合は ()が必要になるので、その点だけ注意。
(seiya) 2017/08/21(月) 21:05

 >最初の ReDim ステートメントが Preserve キーワードと併用の場合

 なるほどです。

 そうなると、「使用直前にする派」も空カッコだけの宣言をチャンとやるのが正しい(?)在り方なんでしょうね。

(半平太) 2017/08/21(月) 21:33


 私は「先頭にまとめる派」です。
 動的配列、静的配列問わず変数宣言していますが、前述の場合だけ()を入れています。(つもり...)
(seiya) 2017/08/21(月) 21:53

 >前述の場合だけ()を入れています。(つもり...)

 前述とは「最初の ReDim ステートメントが Preserve キーワードと併用の場合」ですか?

 そうだとすると、普通はカッコ無し・・・こりゃまた、ぶったまげです。

 私は、今後は、Preserveが必要ないケースでも敢えて全部Preserveと併用するし、
 当然、全部()を付ける、と言う方針にしようかと思っていたところです。

 理由は、いちいち、場合分けするのが面倒であることと、
 Redim ステートメントでスペルミスした時、コンパイルが通ってしまうのを防ぐ為。

(半平太) 2017/08/21(月) 22:22


 Hummm....というより

 Dim a
 Redim Preserve a(1 to 1)

 は、Type Mis-matchの実行時エラーになるので慌てて()をつけている感じです。

(seiya ) 2017/08/21(月) 23:06


 > Dim a
 > Redim Preserve a(1 to 1)
 >
 > は、Type Mis-matchの実行時エラーになるので慌てて()をつけている感じです。

 なーるほどです。実践的ですね。

 私も、質問者のT17さんじゃないですけど、

 Dim aa,bb,cc,dd,ee

 とかやって、取りあえずコーディングを初めちゃってました。

 途中で整形を入れればいい話だし、それで、特に問題なかったですからねぇ。 

(半平太) 2017/08/21(月) 23:33


 う〜ん。関数なんかで配列で返す場合とそうでない時は、こんな感じにしてたけど。
 通常は、配列を使うと決まっている場合は、動的でも()つけます。

 Dim tb As Variant
 if xxxxxxxx then
    if 1回目 then
      ReDim tb(1 To 1)
      tb(1) = 1
    else
      ReDim Preserve tb(1 To 2)
      tb(2) = 2
    end if
 else
   tb = empty
 end if

 なんかちょっと違うなぁ、だめだ覚えてない。

 (BJ) 2017/08/22(火) 01:02

 半平太さん
 直前使用検討派です。
 >「使用直前にする派」は空カッコだけなんて宣言は多分やらないんでしょうね。
 この理屈ってなんでしょう?
 私も
 > 途中で整形を入れればいい話だし、それで、特に問題なかったですからねぇ。 
    ~~~~~~~~~~~~~~~~~~~~
 と思っているので整形時に改めて変数を直前に移動しているので、何でそう思うのかなー程度なのですが。

 Preserveについては、中身が最初からあることが前提である場合が多いので、気にしたこと無かったですね・・・。

    Dim a
    a = Array(1)
    ReDim Preserve a(0 To 10)

 ついでにお伺いしたかったことがあって、次の場合、左辺は()付けるべきなのでしょうか?
 ()付けないで宣言した場合、左辺に()付けるとインデックスエラーになりますが、ここも明確にすべき?

    Dim a()
    a() = Array(1, 2, 3)

 あとはSplit関数を使う前提で組んでいた場合、
 String型の配列か、ただのVariant型しか受け付けないと思うのですが、そういった場合の使い分けも含めて
 配列は()付けたほうがいいよって流れなんですかね?

    Dim a() As String
    Dim b As Variant
    Dim c()
    a() = Split("1 2 3")
    b = Split("1 2 3")
    c() = Split("1 2 3") 'エラー

(稲葉) 2017/08/22(火) 09:38


半平太様、seiya様、BJ様、稲葉様
フォローありがとうございます。

 >そんな大げさな問題だったんですか? エラーになった訳でもなし・・

そうですね、ここらの感覚の違い?はおそらく“立ち位置”の違いかと。
“富士山のてっぺん付近”にいる人たちと2,3合目で四苦八苦している者では
見えているものが違うので問題の有無や大小すらわからないんですね。
ただ、ヘルプに目を通しておれば違う質問になってたかと(笑)・・反省です。

回答いただいた皆さまのご見解から、トラブル未然防止上やはり「()」は
付ける方がいいと判断していたのですが、稲葉様の「c() = Split("1 2 3")」が
何でエラーになるのか分からず、振り出しに戻ったような気分です・・・^ ^;
(T17) 2017/08/22(火) 09:54


 単なるコーディングの癖になると思いますが、私にとっては()を付けない方が使い勝手が良いですね。

 Dim a
 a = Cells(1).CurrentRegion.Value

 この場合もし、該当範囲に値がない、またはA1にしか入力がない場合はa()で宣言するとエラーになります。
 (97までは セル範囲からのデータロードは a() としないとできないと思いましたが...)

 ()を付けなければ IsArray 関数で判定できるし 他の場合でも Variant型にしておくことでIsEmpty関数で
 判定できますので。
(seiya) 2017/08/22(火) 10:33

 seiyaさん

 ()付けるときは、型を宣言する場合だけ付けていたので、
 Variant型の場合は、なんとなくで()付けないことが多かったため、 大変勉強になりました。
 > ()を付けなければ IsArray 関数で判定できるし 他の場合でも Variant型にしておくことでIsEmpty関数で
 >判定できますので。

 あと気になったのが
[[20170724204331]] 『配列のソートをわざわざシート上でやる意義』(中途B)
 の最後の投稿で、syさんに指摘されていたVariant型配列の引数について、
 ()を付ける場合と付けない場合で処理速度にかなり影響があったようですが、
 配列をVariant型に渡すのと
 配列を配列に渡す場合とでそんなに違いがあるとは思えないのですが・・・。

 T17さん
 >何でエラーになるのか分からず、振り出しに戻ったような気分です・・・^ ^;
 これはエラーの通りだと思います。
 SplitはString型の配列を返す関数(String(0 to X))なので
 Variant型の配列(Variant(0 to X))には形があわないだけです。

 Variant型とVariant型の配列が紛らわしいですが・・・

(稲葉) 2017/08/22(火) 11:53


 稲葉さん

 > >「使用直前にする派」は空カッコだけなんて宣言は多分やらないんでしょうね。
 > この理屈ってなんでしょう?

 元々、Redimは単独で動的配列を宣言できるので、常々、
 何の為にあらかじめ宣言するのかなぁー、と思っていました。

 そこで、先頭一括派は、変数の種類は全て宣言部分で確認したい人達なのであろうから、
 そこに変数の痕跡が無いと落ち着かないのかなぁと推測しました。

 一方、使用直前派は、必要な時に必要な場所に書く主義の人達なのであろうから、
 上の様な変数の痕跡を見たいとも思わないので、あらかじめ宣言はしないであろう(空カッコがあろうが無かろうが)と推測しました。

 全て、推測に過ぎません。m(__)m

 私が知りたいことは「あらかじめ宣言しなくて済む時にも、何故、宣言をするのか?」ですね。

 >Preserveについては、中身が最初からあることが前提である場合が多いので、気にしたこと無かったですね・・・。

 BJさんがサンプルを提示されていますが、一回目かそれ以後かで分岐させるのが普通なんでしょうが、
 Redimをループの中に入れるとき、その分岐判定を省略したいと思う事が少なくないので、
 初っ端からPreserveを使うことは十分あり得ると思っています。

 >ついでにお伺いしたかったことがあって、次の場合、左辺は()付けるべきなのでしょうか?
 >()付けないで宣言した場合、左辺に()付けるとインデックスエラーになりますが、ここも明確にすべき?
 >    Dim a()
 >    a() = Array(1, 2, 3)

 分からないです。 
 左辺を()付きにするステートメントを私は書いたことがないです。

 >あとはSplit関数を使う前提で組んでいた場合、
 > String型の配列か、ただのVariant型しか受け付けないと思うのですが、そういった場合の使い分けも含めて
 > 配列は()付けたほうがいいよって流れなんですかね?
 >
 >    Dim a() As String
 >    Dim b As Variant
 >    Dim c()
 >    a() = Split("1 2 3")
 >    b = Split("1 2 3")
 >    c() = Split("1 2 3") 'エラー

 前述の通りなので、私は分からないです。
 多分、Dim b でやる事になると思います。すなわち、()無しのVariant型

 そうなると、初めから動的配列を使うつもりでも、()無しを宣言しなければならない時もある、となりますね。

 今まで、ArrayとかSplitを使って配列にした変数を、その後でReDimの対象にした記憶がないです。
 凄く、勉強になります。

(半平太) 2017/08/22(火) 16:00


古い話なので、ちょっとうる覚えですが。

元々のBASIC(といっても、Quick-Basicの頃からかな? 大昔はReDimできませんでした)では、ReDimは配列の個数を変化させる命令であり、宣言機能はありませんでした。なので、以下のように仮宣言しておいて、後から個数を変える必要があったのです。 ExcelのVBAでは便利になっていて、DimなしでいきなりReDimできてしまうので、配列の場合のDimって何?、とかに見えるのでしょう。

    Dim a(0) As Long
    ReDim a(10)

いきなり ReDimでも動く訳ですが、先頭に宣言を集めるのが主流であり、昔のように Dim a() As Long とか書いた方が、多くの人に受け入れられるのです。 更に、個数の省略ができるようになったので、無駄な1個宣言を無くし、括弧だけ書いて「配列として使うのだ」、という事だけ表現する訳ですね。

今回の稲葉さんの例では、私のお薦めは、以下の書き方ですかねぇ。

    Dim a() As String
    a = Split("1 2 3", " ")
    MsgBox UBound(a) + 1 & "個"

Splitに関すると、これは何個に分かれるか判らないが、文字列配列として使うのだ、という点と、分けた後はそのまま使うので、普通は後からReDimしない(できるけど、やると可読性が落ちるからしない)、という点から、こういう感じに落ち着いてます。
(???) 2017/08/22(火) 16:45


皆さま、 ありがとうございます。
ちょっと議論についていけてませんが、順番に・・ということで拙問お許しください。

 >Variant型とVariant型の配列が紛らわしい・・・
↑う〜ん、言わんとされることを知りたいのですが、何をどう尋ねたら・・すらわかりません... _ _;

Dim a
a() = Array(1, 2, 3)
↑がエラーになるのは「VBAでは配列に配列を代入できないから」という理解でよろしいのでしょうか?
(T17) 2017/08/22(火) 17:34


元の質問から大きく脱線して、突き詰めた会話になっているので、理解できなくとも気にすることはないですよ。

ちなみに、T17さんの例がエラーになるのは、Dim a だけでは a は Variant型単独であり、Variant()とは決まっていないので、a() = として配列である前提で使おうとした事により、エラーになっているかと思います。 ローカルウィンドウを表示させておいてから、ステップ実行すると、違いが判るかも。(Dim a に括弧を付けた場合と付けない場合で、型表示の違いを見てください)
(???) 2017/08/22(火) 18:01


???様、 ありがとうございます。

ステップ実行での型(の表示内容)の違いはわかったのですが、
「Dim a()、a = Array(1, 2, 3)」や「Dim a、a = Array(1, 2, 3)」が
なぜ成立するのかわかりません。

後者(Dim a...)は何となく“Variant型は何でもありだから”と納得させてますが、
前者は何故左辺()が省略できるのでしょうか?

また、ステップ実行では前者は「Variant()」→「Variant(0 to 2)」と、後者は「Variant/Empty」→「Variant/Variant(0 to 2)」と変わっていきますが、これが稲葉様の言われる“Variant型とVariant型の配列”なんですかね?
(T17) 2017/08/23(水) 10:10


 自己レスです。

 > 多分、Dim b でやる事になると思います。すなわち、()無しのVariant型
 > そうなると、初めから動的配列を使うつもりでも、()無しを宣言しなければならない時もある、となりますね。
 > 今まで、ArrayとかSplitを使って配列にした変数を、その後でReDimの対象にした記憶がないです。
 > 凄く、勉強になります。

 と書いたのですが、Variant型で定義しても、Splitから配列を貰うと、その後はString型の配列になってしまうので
 Variantに拘っても意味無かったです。

 すると、こっちでやった方が明快でした。
      ↓
  Dim a() As String

 また、動的配列で、{String、String、String、Date、配列} なんて混在する型を持ちたい場合(実際あるかは?)は
 Variant型でしか対応できないので、初めに a() = Split("1 2 3") とか、お手軽な代入法は使えず、
 地道にFor Nextループで処理せざるを得ないなぁと思っています。

 Sub try1()
     Dim a(), temp, NN As Long

     temp = Array("1", "2", "3")
     ReDim a(UBound(temp))

     For NN = 0 To UBound(temp)
         a(NN) = temp(NN)
     Next

   redim a(ubound(a)+2)

     a(UBound(temp) + 1) = Date
     a(UBound(temp) + 2) = temp
 End Sub

 T17さん

 ここ、結構詳しいです。(私の頭には、一気に入らなかったですが・・)
  ↓
 配列研究室
  STEP 3 VBA編
http://www.clayhouse.jp/array/array03.htm

 そこの解説によれば

 >Arrayで作成される配列はバリアント型なので、
 >それを受け取る変数も、当然「バリアント型の動的配列」、
 >または「バリアント型の変数」である必要があります。

 なので、「Dim a、a = Array(1, 2, 3)」が 可能・・多分

 >    Dim a(2) As Integer
 >    Dim b() As Integer
 >    a(0) = 1
 >    a(1) = 3
 >    a(2) = 5
 >    b() = a()

 >カッコを略して「b = a」のように記述することも可能です。
 理屈は書いてなかったですが、それに基づけは

 「Dim a()、a = Array(1, 2, 3)」が可能・・多分

 まぁ、理屈を突き詰めようとすると、
 VBAの設計者しか分からないことがでて来るんじゃないでしょうか?
 (単なる決めの問題に近くなって来ますから)

(半平太) 2017/08/23(水) 10:46


 >    Dim a(2) As Integer
 >    Dim b() As Integer
 >    a(0) = 1
 >    a(1) = 3
 >    a(2) = 5
 >    b() = a()

 型を指定してたか覚えてないんだけど、

 >b() = a()

 これ、2000以降ならOKですね。
 97の時は、これができなかったんで。
 こうしてました。

 Dim b As variant
 略
 b = a()
 msgbox b(1)

 あれ?
 b = a() もエラーになったかもしれないので、

 単純に
 b = a
 だったかも。(因みに、現在でもこれです。)

 97は、空の括弧が嫌いだったような。

 Range("A1:C1").value = 配列

 なら、速いけど。

 Range("A1:C1").value = 配列()

 とすると、極端に遅くなりました。(どこかで見たのでまねしてみた。)

 (BJ) 2017/08/23(水) 13:54

 半平太さん
 >一方、使用直前派は、必要な時に必要な場所に書く主義の人達なのであろうから、
 >上の様な変数の痕跡を見たいとも思わないので、あらかじめ宣言はしないであろう(空カッコがあろうが無かろうが)と推測しました。
 なるほどですね。
 私はいきなりReDimだと違和感あるので、必要な時に必要な場所、というより見やすい位置にあったほうが、分かりやすい・・・のかなぁ。

 > 私が知りたいことは「あらかじめ宣言しなくて済む時にも、何故、宣言をするのか?」ですね。
 それは先頭一括派、使用直前派どちらにも問われている、と解釈してしまっていいんですかね?
 推測とのことですので、この件はこれで終わります。

 >今まで、ArrayとかSplitを使って配列にした変数を、その後でReDimの対象にした記憶がないです。
 ・・・私もReDim対象にしたことないですね。

 > また、動的配列で、{String、String、String、Date、配列} なんて混在する型を持ちたい場合(実際あるかは?)は
 まず無いとは思いますが、配列数式を与えてから型変換・・・とか?
    Dim a()
    a = [{1,"2","2017/1/1"}]
    a(3) = CDate(a(3))

    Dim b() As String
    b = Split("1 2 2017/1/1")
    b(2) = CDate(b(2)) '通るけど、String型

 いずれにしろ、Splitを使う場合はString型配列を宣言しておくと明朗でよい程度に覚えたいと思います。
 ありがとうございました。

 ???さん
 >Splitに関すると、これは何個に分かれるか判らないが、文字列配列として使うのだ、という点と、分けた後は
 >そのまま使うので、普通は後からReDimしない(できるけど、やると可読性が落ちるからしない)、
 >という点から、こういう感じに落ち着いてます。 
 先にも書きましたが、Splitで使いますよの宣言として覚えておきたいと思います。

 まとめるとこんなカンジの理解です。
    Dim a() As String
    Dim b(2) As Long
    Dim c() As Long
    Dim d() As Double
    Dim e As Variant
    a = Split("1 2 3")                  'String型配列で宣言し、左辺括弧無し
    c = b                               '配列を配列に代入時、左右括弧なし
    ReDim Preserve d(3) As Double       'Preserveに関わらず、配列として宣言する Redim時は型を明言する?
    e = Range("A1").CurrentRegion.Value 'セル範囲を取り込むときはVariant型とする

 QuickBASICって・・・私生まれたころですねたぶん。
 大変勉強になりました。ありがとうございます。

 T17さん
 勉強の機会を与えて頂きありがとうございます。

(稲葉) 2017/08/23(水) 17:12


  >今まで、ArrayとかSplitを使って配列にした変数を、その後でReDimの対象にした記憶がないです。
 ・・・私もReDim対象にしたことないですね。

 これは英語サイトではよく使用します。
 ここでもどこかで使用した記憶がありますが...

    Dim x
    x = Split(StrConv("ABCD", vbUnicode), Chr(0))
    ReDim Preserve x(UBound(x) - 1)
    MsgBox Join(x, "-")
(seiya) 2017/08/23(水) 17:21

 稲葉さん

 >いずれにしろ、Splitを使う場合はString型配列を宣言しておくと明朗でよい程度に覚えたいと思います。

 これは、私と多分、行き違っています(違っていても構いませんが、クリアにして置きたいので)

 私の場合は、「後でReDimする積りなら」a() as String とカッコ付きにする、と言う趣旨でして、
 そんな意図がないなら(実際、過去にそうしたいと思ったこともないので)、今まで通り、慣れた単数Variantでやります。

 しかし、seiyaさんのサンプルは一度、機会があったら使ってみたいですね。
 (多分、忘れていて、別の方法でやっちゃうんでしょうが・・)

(半平太) 2017/08/23(水) 19:17


皆さま、 ありがとうございます。

ご紹介いただいたサイト、以前に訪問しているのですが何も身に付いて
いないことがわかり呆れてしまいました..._ _;

 >まぁ、理屈を突き詰めようとすると・・VBAの設計者しか分からないこと・・ (単なる決めの問題・・)

↑今の私には“心地いい響き”で、“そういうもんだ”と納得します。
Split関数は使用したことがなく今後もなさそうなので、今ムリヤリ頭に入れても
おそらく忘れてしまいそうです(情けない..._ _;)
ちょっと気になってしまったのが、

 >「Range("A1:C1").value = 配列」 なら速いけど「Range("A1:C1").value = 配列()」とすると極端に遅く・・

というところ。 今確認できる環境にないので、これも今のところは“そういうもんだ”としておきます。

正直、内容はほとんどオーバーフローしていますが(すみません)、いろいろと勉強させていただき嬉しい限りです。
これからもよろしくお願いいたします。

(それにしても、MSのヘルプサイトはわかりにくい・・原典とはいえ酷過ぎる..と思うのは私だけ?)

(T17) 2017/08/24(木) 09:32


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.