Вопрос в названии: почему функция для проверки МАССИВА срабатывает на ДИАПАЗОН? Как проверять: с помощью TypeName() — вернёт "Range" Что не устраивает: при передаче аргумента в функцию или процедуру иногда возникает необходимость проверить её на "массивность". Выясняется, что не всегда это можно сделать одной лишь VBA.IsArray()
Тест
Код
Sub t()
Dim x ' если объявить «x As Range», то ничего не изменится
Set x = Range("A1:A2")
Debug.Print IsArray(x), VarType(x), TypeName(x) ' вернёт True / 8204 (8192 + 12) / Range
End Sub
Рабочие VBA-функции для определения массива
Код
'====================================================================================================
' 0 = не массив или нестандартный массив; 1 = одномерный с ноля; 2 = двумерный с единиц
Function PRDX_ArrayType(arr, Optional MsgNotArray As Boolean, Optional MsgBadArray As Boolean, Optional MsgNot1x As Boolean, Optional MsgNot2x As Boolean) As Long
Dim d&, l&, u&, ll&, uu&
If Not IsArray(arr) Or TypeName(arr) = "Range" Then
If MsgNotArray Then MsgBox "Элемент НЕ ЯВЛЯЕТСЯ МАССИВОМ имеет тип «" & TypeName(arr) & "»!", vbCritical, "PRDX_ArrayType"
Exit Function
End If
d = PRDX_ArrayDimensions(arr)
If d < 1 Or d > 2 Then
If MsgBadArray Then MsgBox "Элемент ЯВЛЯЕТСЯ МАССИВОМ, но имеет НЕСТАНДАРТНОЕ КОЛИЧЕСТВО размерностей: «" & d & "»!", vbCritical, "PRDX_ArrayType"
Exit Function
End If
l = LBound(arr, 1): u = UBound(arr, 1)
If d = 1 Then
If l <> 0 Or u < 0 Then
If MsgBadArray Then MsgBox "Элемент ЯВЛЯЕТСЯ ОДНОМЕРНЫМ МАССИВОМ, но имеет НЕКОРРЕКТНЫЕ ГРАНИЦЫ: «" & l & "» и/или «" & u & "»!", vbCritical, "PRDX_ArrayType"
Exit Function
End If
PRDX_ArrayType = 1
If MsgNot2x Then MsgBox "Элемент является ОДНОмерным, А НЕ ДВУмерным массивом!", vbExclamation, "PRDX_ArrayType"
ElseIf d = 2 Then
If l <> 1 Or u < 1 Then
If MsgBadArray Then MsgBox "Элемент ЯВЛЯЕТСЯ ДВУМЕРНЫМ МАССИВОМ, но имеет НЕКОРРЕКТНЫЕ ГРАНИЦЫ 1ой размерности: «" & l & "» и/или «" & u & "»!", vbCritical, "PRDX_ArrayType"
Exit Function
End If
ll = LBound(arr, 2): uu = UBound(arr, 2)
If ll <> 1 Or uu < 1 Then
If MsgBadArray Then MsgBox "Элемент ЯВЛЯЕТСЯ ДВУМЕРНЫМ МАССИВОМ, но имеет НЕКОРРЕКТНЫЕ ГРАНИЦЫ 2ой размерности: «" & ll & "» и/или «" & uu & "»!", vbCritical, "PRDX_ArrayType"
Exit Function
End If
PRDX_ArrayType = 2
If MsgNot1x Then MsgBox "Элемент является ДВУмерным, а не ОДНОмерным массивом!", vbExclamation, "PRDX_ArrayType"
End If
End Function
'----------------------------------------------------------------------------------------------------
Function PRDX_ArrayDimensions(arr) As Long
Dim i&, n&
On Error GoTo ex
Do: i = i + 1: n = UBound(arr, i): Loop
ex: PRDX_ArrayDimensions = i - 1
End Function
'----------------------------------------------------------------------------------------------------
Function PRDX_ArrayElements(arr) As Long
Dim i&, n&
n = 1
For i = 1 To PRDX_ArrayDimensions(arr)
n = n * (UBound(arr, i) - LBound(arr, i) + 1)
Next i
PRDX_ArrayElements = n
End Function
'====================================================================================================
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: почему функция для проверки МАССИВА срабатывает на ДИАПАЗОН?
потому что свойство по умолчанию для объекта Range - это Value. А VBA, как нам всем известно, любит посвоевольничать с определением типов и принудительном их преобразовании в тот или иной момент. Вот и здесь - скорее всего видя функцию, которая предпочтительнее работает с массивами, VBA делает нужные манипуляции с объектом, который может быть без проблем преобразован в массив. А вообще не совсем корректно изначально использовать эту функцию с подобными типами. Она не для этого. Поэтому и удивляться тут нечему
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
и снова Игорь прислал ХЗ что и к чему Во-первых, это не документация (вот она), а сайт "excelfunctions.net", а во-вторых что там объясняется? По поводу диапазона или псевдосрабатываний ничего нет. По поводу "Возвращает логическое значение Boolean, указывающее, является ли переменная массивом" - ну так Dim x As Range означает, что переменная является ДИАПАЗОНОМ и что-то функции плевать на это …
если передать в массив одну ячейку, то будет просто значение - это понятно, но к чему это тут? [для определения массивности в пограничных случаях]
Изменено: Jack Famous - 02.12.2020 11:02:59(Понял пример от RAN)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: а вот тут не понял - а для чего же она?
она для того, чтобы брать переменную типа Variant и определить является ли она массивам. Но она не предназначена напрямую для работы с переменными объектного типа. Поэтому при любом удобном случае она сделает внутренние преобразования, чтобы попробовать запихнутый объект привести именно к массиву. И если это не получается - получим False. Не то, чтобы прям так, но в справке есть на это намек:
Цитата
sArray is especially useful with variants containing arrays
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
вот то-то и оно, что "пытается", а никто не просит + VarType() туда же И вот что сложного сделать ещё один опциональный аргумент "Try", для того, чтобы контролировать эти "попытки", а то вот такая дурь получается
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
это не дурь, к сожалению. По крайней мере, я считаю, что это не дурь VBA. Вы пихаете в функцию то, что она проверять не должна(объект). Поэтому она для начала всеми путями пытается привести аргумент к тому виду, который функция воспринимает. И если получается - работает с тем, во что получилось преобразовать. Если уж считать это дурью, то и нехилую часть функций VBA тоже надо туда отнести Например, как часто все нужные аргументы InStr добрая половина "кодеров" приводит к требуемому типу String? А Mid, Left, Rigth? Думаю, не очень. И часто пихаются туда именно объекты(вроде такого):
Код
lp = Mid(Cells(1,1), 3,2)
А все преобразования за нас делает VBA. Хоть я и против такого подхода, но никто и не запрещает, зная, что это не будет ошибкой. В других языках такая роскошь непозволительна - мало того, что свойство по умолчанию для объекта надо будет указать(.Value), так еще и к типу String его привести, т.к. по умолчанию он Variant Иначе будут у нас ошибки несовпадения типов. Вот так вот. Поэтому это все же скорее фича, которая приносит больше пользы, чем разочарования(в данном конкретном случае).
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
вот чисто по логике названия, функция IsArray() (в пер. ЯвляетсяМассивом()) должна принимать в себя вообще что угодно и говорить, является ли это массивом. А если делаете функцию, которая не отличает диапазон от массива то и называйте её IsArrayOrRange(), а то обман, получается
Цитата
Дмитрий(The_Prist) Щербаков: как часто все нужные аргументы InStr добрая половина "кодеров" приводит к требуемому типу String? А Mid, Left, Rigth?
за других не скажу, но, например, диапазон в массив я всегда передаю через .Value Я вообще за строгость - тем более, что регулировать эти "преобразования" никак нельзя. Нужно передать в функцию текст, так и пусть она выдаёт ошибку при другом аргументе — это всяко лучше, чем измерять "длину" числа, не преобразовав его в текст и получать без нареканий Len(x)=8, при x#=1
Главное, я, как умная Маша предусматриваю в функциях кучу вариантов развития событий, а мелкомягкие плюют на всё и отсылают "учить матчасть" (а в этом случае, даже там прямо ничего не сказано, а ведь техническая документация не терпит двусмысленности, иносказаний и прочих вариантов трактования)
Я против, короче. Глупо со стороны разработки и неуважительно — по отношению к пользователям
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Алексей, ну ведь уже все обсосали вроде. В любом случае, чем диапазон не массив - массив если смотреть с точки зрения значений и объект если рассматривать с точки зрения остальных свойств.
Добрый день, коллеги! Мы с неизвестным подтипом Variant работаем так:
Код
Sub MyMacro(arg)
Dim nDims As Long ' число измерений arg. Если 0, то не инициализированный массив. Если -1, то не массив
Dim vType As Long ' тип (см. описание функции VarType) переменной arg или элементов массива arg
nDims = -1
If IsObject(arg) Then
vType = vbObject
Else
vType = VarType(arg)
If vType >= vbArray Then
vType = vType - vbArray
nDims = GetNdims(arg)
End If
End If
' Далее обработка ......
' Debug.Print vType, nDims
End Sub
' Возвращает число измерения массива (не более 99). Если 0, то arr не массив или не инициализированный массив
Function GetNdims(arr) As Long
Dim i As Long, j As Long
On Error GoTo Out
For i = 1 To 99
j = UBound(arr, i)
GetNdims = GetNdims + 1
Next i
Out:
End Function
БМВ: чем диапазон не массив - массив если смотреть с точки зрения значений
сегодня ты говоришь "чем диапазон не массив", а завтра Родину продашь? Да всем диапазон не массив — это разные, блин, вещи. У меня полно функций, которые принимают в качестве аргумента И массив, И диапазон — в зависимости от того, ЧТО именно идёт разная обработка. Тут уже не до размышлений о положении взглядов - тут ИЛИ-ИЛИ
Цитата
sokol92: Мы с неизвестным подтипом Variant работаем так:
приветствую, Владимир! Спасибо за вариант определения типа массива через отсечение базовой константы vbArray = 8192. К сожалению, диапазон отсечь таким образом нельзя, насколько я понял… Да и, в принципе, проблем-то с ОДНОЗНАЧНЫМ определением массива нет (If IsArray(var) And TypeName(var)<>"Range"), просто бомбит от очередного тяп-ляпа
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Jack Famous написал: диапазон в массив я всегда передаю через .Value
а я не про диапазон в массив я вообще - про другие функции. Это о том, откуда ноги растут у преобразований и почему "гнать волну" на этот счет не этично
Цитата
Jack Famous написал: Нужно передать в функцию текст, так и пусть она выдаёт ошибку при другом аргументе
тогда твоя функция отсюда уже давно бы ругнулась Ибо есть там такое:
Код
Function PRDX_Trim(vl) As Boolean ' заменяет неразрывные пробелы на обычные и удаляет лишние. Возвращает True/False, в зависимости от того, произведены ли изменения
Dim orig, x
...
If InStr(vl, " ") Then vl = Replace(vl, " ", " ")
это как раз то, о чем я и писал - все мы иногда используем "дурь" VBA себе на благо vl - должна быть приведена к типу String, ибо именно этот тип она требует. x там тоже As Variant, но ты подразумеваешь, что в ней всегда будет только String. Так что...Есть куда расти Вот как-то так. без обид - но назвался груздем....
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
ну во-первых есть разница (и немалая, как по мне) между "функция преобразовывает переменную в строку, потому что с другими типами она не сработает или сработает некорректно" и "результат работы функции не соответствует названию и/или для корректной работы нужно плотно изучить матчасть - настолько всё неочевидно"
Смотри: выше я приводил пример, а теперь распишу подробнее
Код
Sub t()
Dim a, b$, c#, d!, e&
a = 1: b = a: c = a: d = a: e = a
Debug.Print a, b, c, d, e ' 1 1 1 1 1
Debug.Print Len(a), Len(b), Len(c), Len(d), Len(e) ' 1 1 8 4 4
End Sub
то есть — результат работы функции зависит от типа переменной. Я считаю, что это недопустимо. В таком случае, нет ничего плохого в том, чтобы Len()преобразовывала аргумент в текстовый, если он таковым не является, как вышеупомянутые строковые функции. Также нормальным был бы вариант, при котором передача аргумента НЕтекстового типа приводила бы к ошибке типа Run-time error '6': (появится при попытке деления на 0, например) с последующей остановкой выполнения кода или отменой компиляции. Можно ещё не прерывать код, но возвращать ошибку типа Len(2.5)=#ERR. Мне не очень такой вариант, но это всё-равно лучше, чем вернуть не то
Итого: в преобразовании как таковом нет ничего плохого, если оно способствует получению корректного результата и не меняет исходные данные Пример "хорошего" преобразования: строковые функции InStr, Left Пример "плохого" преобразования: превращение данных при вставке на лист, IsArray()
По моей функции: • почему вариативный аргумент vl — функция исправляет переданное значение, а передать я могу и текст и число. Значит и вернуть должен тот же тип, что передал. orig вариативный по той же причине А в других функциях я могу, например, передавать в функцию текст или число, а получать обратно (не в качестве результата работы функции, т.к. обычно оно булевое, а в качестве замены передаваемой переменной) массив или диапазон — на такие фокусы только вариативная переменная способна • почему x вариативный — в данной функции можно было объявить и текстовой, без разницы, НО есть много других функций, где вариативность такой "рабочей" переменной обусловлена тем же, что и у переменных выше. То есть она вариативна например для того, чтобы можно было проверить на "числовость" и закрепить x=--x или же просто для универсальности — такая вариативная переменная в коде может хранить любые данные, потому-то она и "рабочая"
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
переведите смысл проверки IsArray(Х) так: можно-ли содержимое Х рассматривать как массив? если ответ утвердительный смело обращайтесь к i-ому элементу массива Х(i) а как определить тип переменной сами знаете: VarType, TypeName
если выискивать нелогичности в этом мире - легко впасть в полное отчаяние от их количества а если потрудиться и найти логику - вы с восхищением начинаете понимать гармоничность этого мира и это ваш личный выбор - жить в светлой гармонии или в мрачном хаосе
ПыСы один мой знакомый часто сетует, что живет среди уродов и отъявленных негодяев! на что я отвечаю, что живу среди нормальных, добрых, отзывчивых и т.д. людей. а он сам выбрал в окружении кого ему жить! и меня очень сильно удивляет его выбор)) не ошибитесь с выбором, удачи!
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
А с чего ты взял, что функция IsArray пытается из Range сделать массив не по этой причине:
Цитата
Jack Famous написал: потому что с другими типами она не сработает или сработает некорректно
? и откуда у тебя получилось "все не очевидно" в этом случае, а в случае с InStr все для тебя наглядно в справке? Там ведь тоже ни слова о том, что функция любой аргумент будет пытаться преобразовать: будь то Range или Double? Там четко написано - нужен String. Давай тогда уже с тобой копнем в самую глубинку и разберемся, почему же ты гонишь волну напрасно. Ты же целенаправленно Range используешь? Так давай заглянем и в Range. Можно найти такое(https://docs.microsoft.com/ru-ru/office/vba/api/excel.range(object)):
Код
The default member of Range forwards calls without parameters to the Value property and calls with parameters to the Item member.
это означает, что любая функция, в которую ты передашь этот объект без указания свойства(даже любая операция с этим объектом), и его тип не будет соответствовать ожидаемому этой функцией(т.е. она не ожидает видеть именно Range) - функция для начала возьмет свойство для объекта по умолчанию. И будет работать с ним. Об этом я уже упоминал. Вот почему я изначально написал, что функция не предназначена для работы с объектами, т.к. объект это сложная структура по сути, а не что-то одиночное и конкретное(не зря же он передается по ссылке - Set). И для работы с объектами надо подготовиться лучше - многие встроенные функции VBA не работают с ними напрямую. Все же стоит помнить - VBA это не сам Excel, это встроенный язык, который распространен на все офисные приложения. IsArray - встроенная в этот язык функция, которая ничего про Range не знает. Т.к. Range это посторонний для VBA тип данных, относящийся к подключенной библиотеке MS Excel. Отсюда и все эти преобразования(берутся значения свойств по умолчанию, как того требует спецификация языка).
А это просто апофеоз:
Цитата
Jack Famous написал: результат работы функции не соответствует названию
Алексей, скажи честно, для тебя функция ПСТР давно стала четко говорить о том, что именно она возвращает?
В общем я за то, что это фича. Притом четко документированная на уровне языка VBA. Наше порой незнание тонкостей работы с тем или иным типом и его взаимодействия с функциями VBA не означает, что все, что нас не устраивает, является багом.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
А по-моему это замечательно, что VBA сам преобразовывает данные, под нужный тип. Вспомним, что это простой язык, для экономистов, которым надо быстро посчитать что-то, обработать, а программировать они слабо умеют (вообщем, для таких, как я). Меня это устраивает. Но можно писать на другом сильно типизированном языке, тогда будет вам счастье, для каждого типа будет своя функция (перегрузка функций). И что бы перевести тип переменной ещё нужно будет постараться, что бы взлетел проект, без ошибок где нибудь дальше из-за преобразования. Нужно будет самому контролировать все эти преобразования и держать в памяти. Для истинных ценителей. Думаю Алексею понравится, серьезно.
Jack Famous написал: тема жива пока жива память о ней
но место её уже в Курилке.
Цитата
bedvit написал: А по-моему это замечательно, что VBA сам преобразовывает данные, под нужный тип.
Согласен что это упрощает жизнь, но и немного расхолаживает. Я долгое время писал VLOOKUP( ... ;... ;...; False) потом стал ленивее VLOOKUP( ... ;... ;...; 0) а потом совсем обленился VLOOKUP( ... ;... ;... пару раз такое сокращение приводило к нежелательному результату, хоть и в другой функции но тут привычка может сработать.
Ігор Гончаренко: это ваш личный выбор - жить в светлой гармонии или в мрачном хаосе
оооо)) я тут не для упражнений в софистике вопрос задал
Цитата
Дмитрий(The_Prist) Щербаков: функция для начала возьмет свойство для объекта по умолчанию … IsArray - встроенная в этот язык функция, которая ничего про Range не знает
в таком ключе соглашусь
Цитата
Дмитрий(The_Prist) Щербаков: для тебя функция ПСТР давно стала четко говорить о том, что именно она возвращает?
локаль — это отдельный вид садизма от мелкомягких. Даже комментровать не буду — настолько это плохо
Цитата
bedvit: Нужно будет самому контролировать все эти преобразования и держать в памяти. Для истинных ценителей. Думаю Алексею понравится, серьезно
кроме шуток, я как раз подумываю про Си или Дельфи (и во многом как раз из-за "прозрачности" и контроля), но, к сожалению, помимо желания знать, нужно понимать, куда это применить и пока на этот вопрос ответа нет
Цитата
БМВ: Согласен что это упрощает жизнь, но и немного расхолаживает
плюс, в исключительных случаях срабатывает некорректно. Пара примеров: • при скачкам внутри цикла For Each x In …по меткам очень редко, но значение x теряется и при очередном шаге оно просто пустое • Range.Count не всегда подразумевает Range.Cells.Count — и это не был подсчёт областей. Отловить не удалось
Теперь я стараюсь чаще писать "как положено" и избегать меток внутри циклов (кроме самых больших ветвлений)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
bedvit, вот все что написали, это прямо про М под капотом PQ. Никаких тебе преобразований, и порой это вынуждает городить для простых расчетов довольно сложные цепочки преобразований. Но есть и плюс: это все заранее известно и никаких неожиданностей типа указанных Алексеем выше нет. Алексей, не устаю повторять - переходите на темную сторону силы! Тем более баги и странности в курилке мы уже документируем!
да перехожу я, перехожу вот и темы создаю по PQ и в работе нередко использую — мне вот знаний по PP намного сильнее не хватает
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄