Страницы: 1
RSS
Баг или фича. Диапазон проходит проверку на «массивность» | IsArray(Range)=True
 
Доброго времени суток, Планетяне!

Вопрос в названии: почему функция для проверки МАССИВА срабатывает на ДИАПАЗОН?
Как проверять: с помощью TypeName() — вернёт "Range"
Что не устраивает: при передаче аргумента в функцию или процедуру иногда возникает необходимость проверить её на "массивность". Выясняется, что не всегда это можно сделать одной лишь VBA.IsArray()

Тест
Рабочие VBA-функции для определения массива
Изменено: Jack Famous - 02.12.2020 11:33:38
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Алексей, я за фичу и скорее ноги её растут из возможности прямого присвоений диапазону массиву и обратно.
По вопросам из тем форума, личку не читаю.
 
Доброе время суток
Пусть в ячейке А1 активного рабочего листа число. Насколько могу судить - такой код никого не смущает - регулярно вижу
Код
Public Sub test()
    Dim x As Range
    Set x = Range("A1")
    Debug.Print x + 4
End Sub
А как разговор про IsArray пошёл - так какие-то сомнения :)
 
Как вариант
Код
Sub t()
Dim x, y
Set x = Range("A1:A2")
y = Range("A1:A2").Value
Debug.Print TypeName(x)
Debug.Print TypeName(y)
End Sub


 
Цитата
Jack Famous написал:
почему функция для проверки МАССИВА срабатывает на ДИАПАЗОН?
потому что свойство по умолчанию для объекта Range - это Value. А VBA, как нам всем известно, любит посвоевольничать с определением типов и принудительном их преобразовании в тот или иной момент. Вот и здесь - скорее всего видя функцию, которая предпочтительнее работает с массивами, VBA делает нужные манипуляции с объектом, который может быть без проблем преобразован в массив.
А вообще не совсем корректно изначально использовать эту функцию с подобными типами. Она не для этого. Поэтому и удивляться тут нечему :)
Изменено: Дмитрий(The_Prist) Щербаков - 02.12.2020 09:08:32
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Цитата
БМВ: ноги её растут из возможности прямого присвоений диапазону массиву и обратно
как в воду глядел (я тоже так думал, но было интересно других послушать)
Цитата
Андрей VG: такой код никого не смущает
, приветствую! Согласен - мы привыкли к некоторым "фичам", поэтому шаг влево-право кажется таким необычным
Цитата
Александр Моторин: Как вариант
не понял "варианта" - про то, что TypeName() различает диапазон я уже указал
Цитата
Дмитрий(The_Prist) Щербаков: VBA, как нам всем известно, любит посвоевольничать
, собственно, ты подробно описал, то что я "примерно думал"  :D
Цитата
Дмитрий(The_Prist) Щербаков: не совсем корректно изначально использовать эту функцию с подобными типами. Она не для этого
а вот тут не понял - а для чего же она? Для определения "заполненности" массива?  :)

Всем спасибо!
Изменено: Jack Famous - 02.12.2020 10:03:56
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Документация - лучший источник ответов
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
Цитата
Jack Famous написал:
а для чего же она?
Как вариант
Код
Dim ar
ar=[a1].value
If Not (IsArray(ar) then
Redim ar(1 to 1, 1 to 1)
ar(1,1) = [a1].value
End If
 
Цитата
Ігор Гончаренко: Документация - лучший источник ответов
и снова Игорь прислал ХЗ что и к чему  :D
Во-первых, это не документация (вот она), а сайт "excelfunctions.net", а во-вторых что там объясняется? По поводу диапазона или псевдосрабатываний ничего нет. По поводу "Возвращает логическое значение Boolean, указывающее, является ли переменная массивом" - ну так Dim x As Range означает, что переменная является ДИАПАЗОНОМ и что-то функции плевать на это …
Цитата
RAN: Как вариант
если передать в массив одну ячейку, то будет просто значение - это понятно, но к чему это тут? [для определения массивности в пограничных случаях]
Изменено: Jack Famous - 02.12.2020 11:02:59 (Понял пример от RAN)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
а вот тут не понял - а для чего же она?
она для того, чтобы брать переменную типа Variant и определить является ли она массивам. Но она не предназначена напрямую для работы с переменными объектного типа. Поэтому при любом удобном случае она сделает внутренние преобразования, чтобы попробовать запихнутый объект привести именно к массиву. И если это не получается - получим False.
Не то, чтобы прям так, но в справке есть на это намек:
Цитата
sArray is especially useful with variants containing arrays
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Цитата
Дмитрий(The_Prist) Щербаков: попробовать запихнутый объект привести именно к массиву
вот то-то и оно, что "пытается", а никто не просит + VarType() туда же  :evil:
И вот что сложного сделать ещё один опциональный аргумент "Try", для того, чтобы контролировать эти "попытки", а то вот такая дурь получается  :D
Изменено: Jack Famous - 02.12.2020 11:35:27
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
вот такая дурь получается
это не дурь, к сожалению. По крайней мере, я считаю, что это не дурь VBA. Вы пихаете в функцию то, что она проверять не должна(объект). Поэтому она для начала всеми путями пытается привести аргумент к тому виду, который функция воспринимает. И если получается - работает с тем, во что получилось преобразовать.
Если уж считать это дурью, то и нехилую часть функций VBA тоже надо туда отнести :) Например, как часто все нужные аргументы InStr добрая половина "кодеров" приводит к требуемому типу String? А Mid, Left, Rigth? Думаю, не очень. И часто пихаются туда именно объекты(вроде такого):
Код
lp = Mid(Cells(1,1), 3,2)
А все преобразования за нас делает VBA. Хоть я и против такого подхода, но никто и не запрещает, зная, что это не будет ошибкой. В других языках такая роскошь непозволительна - мало того, что свойство по умолчанию для объекта надо будет указать(.Value), так еще и к типу String его привести, т.к. по умолчанию он Variant :) Иначе будут у нас ошибки несовпадения типов.
Вот так вот. Поэтому это все же скорее фича, которая приносит больше пользы, чем разочарования(в данном конкретном случае).
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Цитата
Дмитрий(The_Prist) Щербаков: я считаю, что это не дурь VBA
ещё какая — смотрите …
Цитата
Дмитрий(The_Prist) Щербаков: Вы пихаете в функцию то, что она проверять не должна(объект)
вот чисто по логике названия, функция IsArray() (в пер. ЯвляетсяМассивом()) должна принимать в себя вообще что угодно и говорить, является ли это массивом. А если делаете функцию, которая не отличает диапазон от массива то и называйте её IsArrayOrRange(), а то обман, получается
Цитата
Дмитрий(The_Prist) Щербаков: как часто все нужные аргументы InStr добрая половина "кодеров" приводит к требуемому типу String? А Mid, Left, Rigth?
за других не скажу, но, например, диапазон в массив я всегда передаю через .Value
Я вообще за строгость - тем более, что регулировать эти "преобразования" никак нельзя. Нужно передать в функцию текст, так и пусть она выдаёт ошибку при другом аргументе — это всяко лучше, чем измерять "длину" числа, не преобразовав его в текст и получать без нареканий Len(x)=8, при x#=1  :D

Главное, я, как умная Маша предусматриваю в функциях кучу вариантов развития событий, а мелкомягкие плюют на всё и отсылают "учить матчасть" (а в этом случае, даже там прямо ничего не сказано, а ведь техническая документация не терпит двусмысленности, иносказаний и прочих вариантов трактования)

Я против, короче. Глупо со стороны разработки и неуважительно — по отношению к пользователям
Изменено: Jack Famous - 02.12.2020 17:39:54
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Алексей, ну ведь уже все обсосали вроде. В любом случае, чем диапазон не массив - массив если смотреть с точки зрения значений и объект если рассматривать с точки зрения остальных свойств.
По вопросам из тем форума, личку не читаю.
 
Добрый день, коллеги! Мы с неизвестным подтипом 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 - 02.12.2020 17:59:56
Владимир
 
Цитата
БМВ: чем диапазон не массив - массив если смотреть с точки зрения значений
сегодня ты говоришь "чем диапазон не массив", а завтра Родину продашь?  :D
Да всем диапазон не массив — это разные, блин, вещи. У меня полно функций, которые принимают в качестве аргумента И массив, И диапазон — в зависимости от того, ЧТО именно идёт разная обработка. Тут уже не до размышлений о положении взглядов - тут ИЛИ-ИЛИ  :)
Цитата
sokol92: Мы с неизвестным подтипом Variant работаем так:
приветствую, Владимир! Спасибо за вариант определения типа массива через отсечение базовой константы vbArray = 8192. К сожалению, диапазон отсечь таким образом нельзя, насколько я понял… Да и, в принципе, проблем-то с ОДНОЗНАЧНЫМ определением массива нет (If IsArray(var) And TypeName(var)<>"Range"), просто бомбит от очередного тяп-ляпа  :evil:
Изменено: Jack Famous - 02.12.2020 18:12:11
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Для диапазона (и для любых других объектов) в #15 будет определен тип vbObject, что, на мой взгляд, правильно.
Изменено: sokol92 - 02.12.2020 18:15:48
Владимир
 
Цитата
Jack Famous написал:
диапазон в массив я всегда передаю через .Value
а я не про диапазон в массив  ;)  я вообще - про другие функции. Это о том, откуда ноги растут у преобразований и почему "гнать волну" на этот счет не этично  :D
Цитата
Jack Famous написал:
Нужно передать в функцию текст, так и пусть она выдаёт ошибку при другом аргументе
тогда твоя функция отсюда уже давно бы ругнулась  :D  Ибо есть там такое:
Код
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. Так что...Есть куда расти  ;)
Вот как-то так. без обид - но назвался груздем....  :)  
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
Уходим в оффтоп…  :D
Цитата
Дмитрий(The_Prist) Щербаков: все мы иногда используем "дурь" VBA себе на благо
ну во-первых есть разница (и немалая, как по мне) между "функция преобразовывает переменную в строку, потому что с другими типами она не сработает или сработает некорректно" и "результат работы функции не соответствует названию и/или для корректной работы нужно плотно изучить матчасть - настолько всё неочевидно"
Смотри: выше я приводил пример, а теперь распишу подробнее
то есть — результат работы функции зависит от типа переменной. Я считаю, что это недопустимо. В таком случае, нет ничего плохого в том, чтобы Len() преобразовывала аргумент в текстовый, если он таковым не является, как вышеупомянутые строковые функции. Также нормальным был бы вариант, при котором передача аргумента НЕтекстового типа приводила бы к ошибке типа Run-time error '6': (появится при попытке деления на 0, например) с последующей остановкой выполнения кода или отменой компиляции. Можно ещё не прерывать код, но возвращать ошибку типа Len(2.5)=#ERR. Мне не очень такой вариант, но это всё-равно лучше, чем вернуть не то

Итого: в преобразовании как таковом нет ничего плохого, если оно способствует получению корректного результата и не меняет исходные данные
Пример "хорошего" преобразования: строковые функции InStr, Left
Пример "плохого" преобразования: превращение данных при вставке на лист, IsArray()

По моей функции:
  • почему вариативный аргумент vl — функция исправляет переданное значение, а передать я могу и текст и число. Значит и вернуть должен тот же тип, что передал. orig вариативный по той же причине
А в других функциях я могу, например, передавать в функцию текст или число, а получать обратно (не в качестве результата работы функции, т.к. обычно оно булевое, а в качестве замены передаваемой переменной) массив или диапазон — на такие фокусы только вариативная переменная способна  :)
  • почему x вариативный — в данной функции можно было объявить и текстовой, без разницы, НО есть много других функций, где вариативность такой "рабочей" переменной обусловлена тем же, что и у переменных выше. То есть она вариативна например для того, чтобы можно было проверить на "числовость" и закрепить x=--x или же просто для универсальности — такая вариативная переменная в коде может хранить любые данные, потому-то она и "рабочая"
Изменено: Jack Famous - 02.12.2020 22:11:11
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Jack Famous,  Алексей,
Вопрос был баг или фича. Фича VBA и на этом точка.
По вопросам из тем форума, личку не читаю.
 
Цитата
БМВ: Вопрос был баг или фича. Фича
я всё-таки склоняюсь к багу  :)
Цитата
БМВ: на этом точка
тема жива пока жива память о ней  :D
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
переведите смысл проверки IsArray(Х) так:
можно-ли содержимое Х рассматривать как массив?
если ответ утвердительный смело обращайтесь к i-ому элементу массива Х(i)
а как определить тип переменной сами знаете: VarType, TypeName

если выискивать нелогичности в этом мире - легко впасть в полное отчаяние от их количества
а если потрудиться и найти логику - вы с восхищением начинаете понимать гармоничность этого мира
и это ваш личный выбор - жить в светлой гармонии или в мрачном хаосе

ПыСы
один мой знакомый часто сетует, что живет среди уродов и отъявленных негодяев!
на что я отвечаю, что живу среди нормальных, добрых, отзывчивых и т.д. людей. а он сам выбрал в окружении кого ему жить! и меня очень сильно удивляет его выбор))
не ошибитесь с выбором, удачи!
Программисты - это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете!
 
А с чего ты взял, что функция IsArray пытается из Range сделать массив не по этой причине:
Цитата
Jack Famous написал:
потому что с другими типами она не сработает или сработает некорректно
? :)
и откуда у тебя получилось "все не очевидно" в этом случае, а в случае с InStr все для тебя наглядно в справке? :D  Там ведь тоже ни слова о том, что функция любой аргумент будет пытаться преобразовать: будь то 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 написал:
результат работы функции не соответствует названию
Алексей, скажи честно, для тебя функция ПСТР давно стала четко говорить о том, что именно она возвращает?  :D

В общем я за то, что это фича. Притом четко документированная на уровне языка VBA. Наше порой незнание тонкостей работы с тем или иным типом и его взаимодействия с функциями VBA не означает, что все, что нас не устраивает, является багом.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
 
А по-моему это замечательно, что VBA сам преобразовывает данные, под нужный тип. Вспомним, что это простой язык, для экономистов, которым надо быстро посчитать что-то, обработать, а программировать они слабо умеют (вообщем, для таких, как я). Меня это устраивает. Но можно писать на другом сильно типизированном языке, тогда будет вам счастье, для каждого типа будет своя функция (перегрузка функций). И что бы перевести тип переменной ещё нужно будет постараться, что бы взлетел проект, без ошибок где нибудь дальше из-за преобразования. Нужно будет самому контролировать все эти преобразования и держать в памяти. Для истинных ценителей. Думаю Алексею понравится, серьезно.
«Бритва Оккама» или «Принцип Калашникова»?
 
Цитата
Jack Famous написал:
тема жива пока жива память о ней  
но место её уже в Курилке.

Цитата
bedvit написал:
А по-моему это замечательно, что VBA сам преобразовывает данные, под нужный тип.
Согласен что это упрощает жизнь, но и немного расхолаживает.  Я долгое время писал VLOOKUP( ... ;... ;...; False) потом стал ленивее VLOOKUP( ... ;... ;...; 0) а потом совсем обленился VLOOKUP( ... ;... ;...;)  пару раз такое сокращение приводило к нежелательному результату, хоть и в другой функции но тут привычка может сработать.
По вопросам из тем форума, личку не читаю.
 
Цитата
Ігор Гончаренко: это ваш личный выбор - жить в светлой гармонии или в мрачном хаосе
оооо)) я тут не для упражнений в софистике вопрос задал
Цитата
Дмитрий(The_Prist) Щербаков: функция для начала возьмет свойство для объекта по умолчанию … IsArray - встроенная в этот язык функция, которая ничего про Range не знает
в таком ключе соглашусь  :)
Цитата
Дмитрий(The_Prist) Щербаков: для тебя функция ПСТР давно стала четко говорить о том, что именно она возвращает?
локаль — это отдельный вид садизма от мелкомягких. Даже комментровать не буду — настолько это плохо  :D
Цитата
bedvit: Нужно будет самому контролировать все эти преобразования и держать в памяти. Для истинных ценителей. Думаю Алексею понравится, серьезно
кроме шуток, я как раз подумываю про Си или Дельфи (и во многом как раз из-за "прозрачности" и контроля), но, к сожалению, помимо желания знать, нужно понимать, куда это применить и пока на этот вопрос ответа нет  :oops:
Цитата
БМВ: Согласен что это упрощает жизнь, но и немного расхолаживает
плюс, в исключительных случаях срабатывает некорректно. Пара примеров:
  • при скачкам внутри цикла For Each x In …по меткам очень редко, но значение x теряется и при очередном шаге оно просто пустое
  • Range.Count не всегда подразумевает Range.Cells.Count — и это не был подсчёт областей. Отловить не удалось

Теперь я стараюсь чаще писать "как положено" и избегать меток внутри циклов (кроме самых больших ветвлений)  :)
Изменено: Jack Famous - 03.12.2020 10:13:29
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
bedvit, вот все что написали, это прямо про М под капотом PQ. Никаких тебе преобразований, и порой это вынуждает городить для простых расчетов довольно сложные цепочки преобразований. Но есть и плюс: это все заранее известно и никаких неожиданностей типа указанных Алексеем выше нет.
Алексей, не устаю повторять - переходите на темную сторону силы! 8) Тем более баги и странности в курилке мы уже документируем!  :D
Вот горшок пустой, он предмет простой...
 
Цитата
PooHkrd: переходите на темную сторону силы
да перехожу я, перехожу  :D  вот и темы создаю по PQ и в работе нередко использую — мне вот знаний по PP намного сильнее не хватает  :)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Страницы: 1
Наверх