Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 10 Pro : Excel 2016)
For Each 〜 Next の予想外の動きについて
投稿日時: 21/12/22 17:54:44
投稿者: taichi

Sub aa() <--- 予想外の動き
    Dim rr As Range, n As Integer
    n = 1
    For Each rr In [A1:C5].Columns(2)
      MsgBox n
      rr.Value = n
      n = n + 1
    Next
End Sub
sub aa では n=1 で Nextを抜け出てしまい、[B1]だけに1が入るのではなくて
[B1:B5]全体に1が入ります。 sub aa の書き方でも sub bb と同じ
動きt方をするものとばかり思っていました。何故でしょうか?
 
Sub bb() <--- 予想通りの動き
    Dim rr As Range, n As Integer
    n = 1
    For Each rr In [A1:C5].Columns(2).cells
      MsgBox n
      rr.Value = n
      n = n + 1
    Next
End Sub

回答
投稿日時: 21/12/22 18:09:04
投稿者: WinArrow
投稿者のウェブサイトに移動

プロシジャ(aa)では、列を指定している。
プロシジャ(bb)では、ここのセルを指定しています。
 
従って、そのような結果になったことは、当然です。
 
プロシジャ(aa)を
>For Each rr In [A1:C5].Columns(2)

For Each rr In Range("B1:B5")
と指定すれば、(bb)と同じになります。

回答
投稿日時: 21/12/22 18:37:00
投稿者: WinArrow
投稿者のウェブサイトに移動

(aa)と(bb)の違いを検証する方法
  
(aa)
    For Each rr In [A1:C5].Columns(2)
      MsgBox rr.Address
      rr.Value = n
      n = n + 1
    Next
  
(bb)
    For Each rr In [A1:C5].Columns(2).cells
      MsgBox rr.Address
      rr.Value = n
      n = n + 1
    Next
  
これで(aa)はループしていないことが分かります。
とすれば、For 〜Nextは無駄になりますから
↓のように記述することも可能です。
    With [A1:c5].Columns(2)
        .Formula = "=ROW()"
        .Value = .Value
    End With
 

投稿日時: 21/12/22 22:14:11
投稿者: taichi

WinArrow さん、いつもありがとうございます。
 
名前を付け長方形型のセル範囲に対して、その範囲内の特定の列に
For Each 〜 Next で操作をしたかったのです。
 
For Each rr in Range("名前付きセル範囲").Columns(3)
といった具合です。
 
[A1:C5].Columns(2) と Range("B1:B5") は同じセル範囲を
表していると思っていたので、
Sub aa の方でコードを書いてみました。

回答
投稿日時: 21/12/22 23:23:59
投稿者: WinArrow
投稿者のウェブサイトに移動

引用:

[A1:C5].Columns(2) と Range("B1:B5") は同じセル範囲を
表していると思っていた

 
[A1:C5]

Range("B1:B5")
は、考え方jは同じです。
しかし、
>[A1:C5].Columns(2)
Columns(2)が付くと、別ものです。

回答
投稿日時: 21/12/23 00:00:10
投稿者: simple

既に指摘いただいていること以上のことはありませんが。
 
これを実行してみてください。

Sub test()
    Debug.Print [A1:C5].Columns(2).Count    ’1 が返る
    Debug.Print Range("B1:B5").Count        ' 5 が返る
End Sub

上は、列を一つの単位として考えるので、 1つです
下は、各セルを一つの単位として考えるので、5つです。
 
For each を使って列挙するときは、
この「単位」の数え方と整合的な考え方をします。
上は、ひとつの列を 取り出して処理して終わり。
下は、ひとつひとつのセルに対しての処理を5回繰り返す。
 
既に説明があったように、
・列をひとつの単位として考えるか、
・セル単位で考えるか
の相違です。
これは、仕様であり、あなたが頭を切り替えて、
そうした仕様を前提に使うほかありません。もし、いくら気に入っていなくても。

回答
投稿日時: 21/12/23 08:10:46
投稿者: simple

追記です。
 
想定していることを実現するには、
Intersect([A1:C5], Columns(2))とすることになるでしょう。

   Debug.Print Intersect([A1:C5], Columns(2)).Address  ' $B$1:$B$5
   Debug.Print Intersect([A1:C5], Columns(2)).Count    ' 5

また、
    Dim m As Variant
    Set m = [A1:C5].Columns
    Debug.Print m.Count         ' 3
    Debug.Print m(2).Count      ' 1
となりますが、
RangeオブジェクトのColumnsプロパティのヘルプで意味を確認するとよいでしょう。
[A1:C5].Columnsは、そのセル範囲の列たちを表し、
[A1:C5].Columns(2)は、その2番目の列という意味になりますね。
(ヘルプは機械翻訳なので、日本語がおかしいところがあります。
  右上にあるボタンを押して英語を読んだほうが正確です。)
(なお、Dim m As Variantとしましたが、RangeでOKです。
  Rangeはおかしいのではないか、という主張もありえますが。)

投稿日時: 21/12/23 14:13:41
投稿者: taichi

WinArrow さん simpleさん ありがとうございました。
 
>この「単位」の数え方と整合的な考え方をします。
>上は、ひとつの列を 取り出して処理して終わり。
>下は、ひとつひとつのセルに対しての処理を5回繰り返す。
  
>・列をひとつの単位として考えるか、
>・セル単位で考えるか
 
の説明で納得いたしました。