Option Base 1
Option Explicit
Option Private Module
'====================================================================================================
Sub Test()
Dim x, arr
Dim rng As Range, cl As Range
Set rng = Range("A1:C2"): Debug.Print "Range:"
For Each cl In rng
Debug.Print cl.Value
Next cl
arr = rng.Value: Debug.Print "Array:"
For Each x In arr
Debug.Print x
Next x
End Sub
'====================================================================================================
'====================================================================================================
Sub TdstArr4D()
Dim arr4D(0 To 1, 1 To 3, 3 To 4, 3 To 5)
Dim el, aRes(), r&
Dim d1&, d2&, d3&, d4&
ReDim aRes(ArrElements(arr4D))
For d4 = LBound(arr4D, 4) To UBound(arr4D, 4)
For d3 = LBound(arr4D, 3) To UBound(arr4D, 3)
For d2 = LBound(arr4D, 2) To UBound(arr4D, 2)
For d1 = LBound(arr4D, 1) To UBound(arr4D, 1)
arr4D(d1, d2, d3, d4) = d1 & "-" & d2 & "-" & d3 & "-" & d4
r = r + 1: aRes(r) = arr4D(d1, d2, d3, d4)
Next d1
Next d2
Next d3
Next d4
r = 0
For Each el In arr4D
r = r + 1: If el <> aRes(r) Then Stop: End
Next el
End Sub
'====================================================================================================
Function ArrDimensions(arr) As Long
Dim i&, n&
On Error GoTo ex
Do: i = i + 1: n = UBound(arr, i): Loop
ex: ArrDimensions = i - 1
End Function
'----------------------------------------------------------------------------------------------------
Function ArrElements(arr) As Long
Dim i&, n&
n = 1
For i = 1 To ArrDimensions(arr)
n = n * (UBound(arr, i) - LBound(arr, i) + 1)
Next i
ArrElements = n
End Function
'====================================================================================================
Цикл For Each Cell In Range идёт сначала по СТРОКАМ, потом по СТОЛБЦАМ
Правильнее будет сказать, что коллекцияячеек диапазона формируется таким образом, что цикл For Each по ней будет равносилен коду ниже
Визуализация через двойной цикл
Код
For r=1 To Range.Rows.Count
For c=1 To Range.Columns.Count
Next c
Next r
Цикл For Each Element In Array2D идёт сначала по "СТОЛБЦАМ", потом по "СТРОКАМ"
Правильнее будет сказать, что коллекцияэлементов массива формируется таким образом, что цикл For Each по ней будет равносилен коду ниже
Визуализация через двойной цикл
Код
For c=1 To Ubound(Array2D, 2)
For r=1 To Ubound(Array2D, 1)
Next r
Next c
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: Цикл For Each Cell In Range идёт сначала по СТРОКАМ, потом по СТОЛБЦАМ Цикл For Each Element In Array2D идёт сначала по СТОЛБЦАМ, потом по СТРОКАМ
Добрый день, Алексей. Хорошо, что модераторы подчистили тему, т.к. с утра здесь что-то эмоционально обсуждали и по ходу поменяли термин "перебор по" на "ИДЕТ". Термин "ИДЕТ" может толковаться по разному, например: пошел я по строкам 1-го столбца, потом по строкам 2-го. Или, наоборот: потопал по всем столбцам первой строки, потом 2-й и т.д. Об этом же с утра здесь вроде спорили.
По-моему, в начале темы всегда полезно конкретно обозначить свою проблему, например: "Используя оператор For-Each-Next, можно ошибиться в понимании порядка обработки ячеек диапазона и элементов 2-х мерного массива". А то вдруг это на самом деле окажется не проблема – мы же не знаем того, чего не знаем. При приведенном мною описании проблемы, причина (понимание устройства коллекции ячеек диапазона) не спутается со следствием – интерпретацией кем-то работы оператора For-Each-Next, который, вообще-то, всегда работает однозначно.
Диапазон ячеек, это коллекция объектов ячеек. Оператор For-Each-Next с любыми коллекциями работает в порядке следования индекса элемента коллекции от меньшего к большему. У каждой ячейки диапазона (читай – коллекции объектов) Range тоже есть свой индекс. К ячейке можно обращаться по такому индексу, например Range("A1:B2").Item(2) это ячейка B1, а не A2, как некоторые могут неправильно подумать. И оператор For-Each-Next здесь совершенно не причем, он тупо перебирает коллекцию, которую ему подсунули: ячейки диапазона (читай – элементы коллекции) в порядке возрастания индекса ячеек.
Теперь пояснения по устройству массивов. Строго говоря, у массива нет ни строк, ни столбцов, у него – размерности, каждая из которых может начинаться не обязательно с единичного индекса. Например: Dim a(0 To 3, 10 To 11) Физически же в памяти как элементы коллекции, так и элементы массивы всегда располагаются в непрерывном диапазоне адресов, как одномерный массив. Но, чтобы немного «очеловечить» их восприятие и позволить произвольный перебор, у массива есть структура, которая позволяет (логически) обращаться к элементам по логическим индексам элемента, при этом внутри индекс всегда пересчитывается в индекс одномерного, так как в памяти все элементы лежат в непрерывном диапазоне адресов.
Для 2-мерного массива представление об 1-й разрядности как о строках – это вольная интерпретация конкретного программиста, у другого это легко может ассоциироваться со столбцами или ещё с каким-либо попугаями. Это лучше конкретизировать.
С массивами оператор For-Each-Next работает по порядку физического (одномерного) хранения элементов массива. С точки зрения разрядностей массива это равносильно перебору от элемента с наименьшим индексом - a(0, 11) к наибольшему a(3, 11) для примера выше, перебирая поочередно элементы 1-й разрядностей ...-a(1,11)-a(2,11)-…, потом инкрементировать индекс 2-й разрядности, если она есть, и.т.д.
Если хочется упростить понимание обработки For-Each-Next, ассоциируя 2-мерный массив с диапазоном ячеек Excel, то порядок обработки такой: 1. Ячейки диапазона: Слева-Направо-Вниз 2. Элементы 2-мерного массива: Сверху-Вниз-Направо
Но, еще раз подчеркну, оператор For-Each-Next здесь не причем.
Пример кода для понимания устройства коллекции Range:
Код
Sub Test1()
' For-Each-Next
Dim rng As Range, cell As Range
Set rng = Range("A1:B2")
Debug.Print "Cell"
For Each cell In rng.Cells
Debug.Print cell.Address(0, 0)
Next
End Sub
Sub Test2()
' Аналог For-Each-Next
Dim i As Long, rng As Range
Set rng = Range("A1:B2")
Debug.Print "Item"
For i = 1 To rng.Cells.Count
Debug.Print rng.Item(i).Address(0, 0)
Next
End Sub
ZVI: оператор For-Each-Next здесь совершенно не при чём … с любыми коллекциями работает в порядке следования индекса элемента коллекции от меньшего к большему
это, конечно, важное замечание, но, боюсь, что более "корректное" название темы типа "Коллекция ячеек диапазона и элементов двумерного массива, полученного из этого диапазона имеют разные индексы" не оставит шансов ищущим, а также отобъёт всякое желание открывать тему даже у тех, кто её видит
Цитата
ZVI: поменяли термин "перебор по" на "ИДЕТ" … может толковаться по-разному
для этого я и показал кодом из 2ух циклов, что имею ввиду Дополнил строками с более корректным определением и добавил кавычки к "строкам" и "столбцам" массива
Подытожу с учётом новой и полной информации: Цикл For Next просто перебирает все элементы коллекции "по порядку", то есть по индексам от первого […индекса, который может быть и 0] до последнего Коллекции ячеек диапазона и элементов массива формируются по-разному, поэтому и индексы при переборе будут разные (если только это не перебор одной строки диапазона/столбца диапазона/размерности массива)
Всё, что я хотел сказать этой темой — rng(n)<>arr(n) при arr=rng.Value и rng.Areas=1
ZVI, ещё раз спасибо за полный разбор — про коллекции я не "докопал", конечно и только "по верхам" прошёл
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: боюсь, что более "корректное" название темы типа "Коллекция ячеек диапазона и элементов двумерного массива, полученного из этого диапазона имеют разные индексы" не оставит шансов ищущим, а также отобъёт всякое желание открывать тему даже у тех, кто её видит
Алексей, про краткое название темы я ничего не предлагал. По названию проблема мне не ясна, так как реально нет отличий работы цикла «For Each …» для ячеек диапазона и элементов массива - и те и другие обрабатываются в порядке индексов элементов. Для массива это неявный одномерный индекс, для ячеек - явный. Мое предложение было - чтобы не было необходимости читать цепочку кодов и о чем-то самому догадываться, описывать проблему кратко в начале темы словами, в моем тексте выше это было сформулирована так: "Используя оператор For-Each-Next, можно ошибиться в понимании порядка обработки ячеек диапазона и элементов 2-х мерного массива". Так как понимание работы оператора и его реальная работа - это не всегда одно и тоже.
ZVI: "Используя оператор For-Each-Next, можно ошибиться в понимании порядка обработки ячеек диапазона и элементов 2-х мерного массива".
если модераторы посчитают нужным, то я совсем не против Но вообще, считаю его более "тяжёлым" для поиска (особенно дублирование на английском, что я стараюсь в последнее время делать ), хоть и, безусловно, более корректным
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
ZVI написал: 2. Элементы 2-мерного массива: Сверху-Вниз-Направо
Владимир, приветствую. Осталось только вспомнить что массив не ограничен двумя измерениями :-) и появится вглубь и .... даже не знаю что делать с 4м :-)
Добрый день, Михаил. 1. Там конкретизировано, что это касается только 2-мерного массива, ассоциированного с диапазоном ячеек, 2. Опечатка, спасибо! Исправил на A1:B2
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Всех приветствую! Владимир, как всегда познавательно. Добавлю от себя немного. Да все массивы, это единый непрерывный блок в памяти. Можно сказать одномерный массив. Размерности это условность. Алексей, помнишь мою функцию по очень быстрому смены размерностей (она как раз и использует это свойство). Вся проблема (и у меня она тоже возникает постоянно) в том, что массив ячеек в Excel ( и в Excel C API) хранится как слева-направо-вниз. В таком же порядке идет пересчет ячеек. Так решили напилить. А вот массивы СОМ (в т.ч. и в VBA) это порядок сверху-вниз-направо. ДВА РАЗНЫХ подхода в хранении элементов массива! Отчего так, наверное исторически, надо гуглить. Я даже делал конвертацию элементов между массивами СОМ (VBA) и C API EXCEL Поэтому кажется, что For…Each…Next работает по разному. Но нет он просто перебирает ПО ПОРЯДКУ хранимые в памяти элементы массива.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
что кстати.... развели тут понимаешь "душевную компанию 2", междусобойчик :-)
Цитата
ZVI написал: Если хочется упростить понимание обработки For-Each-Next, ассоциируя 2-мерный массив с диапазоном ячеек Excel, то порядок обработки такой:1. Ячейки диапазона: Слева-Направо-Вниз2. Элементы 2-мерного массива: Сверху-Вниз-Направо
думаю проще описать это тем что при For-Each-Next в диапазоне меняется сперва второй индекс, потом первый, а в массиве наоборот. На самом деле интересно, почем у такую концепцию заложили разработчики, ну разе что читать привыкли в той же последовательности что и ячейки на листе.