Пару раз видел тут на планете, как мэтры предупреждали (Hugo, Михаил Витальевич), что при цикле по массивам, полученным из ключей и элементов словаря, их соответствие не обеспечивается — то есть i-ый элемент массива может быть НЕ РАВЕН i-ому ключу:
Тест
Код
Sub t()
Dim dic As New Dictionary
Dim arrKeys(), arrItems()
Dim x, iKey, iItem, t!, tt!, i&
tt = Timer
t = Timer
For i = 1 To 100000
dic(i) = "element " & i
Next i
Debug.Print "Fill", Fix(1000 * (Timer - t))
t = Timer
arrKeys = dic.Keys
arrItems = dic.Items
Debug.Print "Arrays", Fix(1000 * (Timer - t))
t = Timer
For i = 0 To UBound(arrKeys)
iKey = arrKeys(i)
iItem = arrItems(i)
x = dic(iKey)
If x <> iItem Then Debug.Print x; "<>"; iItem
Next i
Debug.Print "Out", Fix(1000 * (Timer - t))
Debug.Print "Done", Fix(1000 * (Timer - tt))
End Sub
Вопросы: есть ли этому подтверждения, сталкивался ли кто-то с этим, как поймать такой баг и где прочитать о не обеспечении соответствия?
Словари и так шустрые, но получение 2ух массивов (ключей и элементов) из словаря иногда бывает намного удобнее цикла по элементам и хочется уверенно использовать этот способ
!!! ВАЖНО !!! Обсуждается подтверждение несоответствия при раннем связывании, т.к. при позднем сюрпризы привычны (я его не использую)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Да, я сиё тоже упоминал; сам я это взял, емнип, у Hugo, более того, один раз, года четыре назад сам на это нарвался, причем словарь был не большой - чуть более ста элементов. С тех пор пару keys - items я не использую; прохожу по ключам, значения беру из словаря по ключу - это чуть дольше но надежно. А то что ни разу не нарывался... так а как ты ошибку заметишь, если специально внимательно не проверять?
Такая ошибка может возникнуть(уж точно чаще), если используется позднее связывание. Равно как может появится фокус с неочисткой ключей методом RemoveAll. При раннем связывании проблем быть не должно. Но это не точно При раннем такой бяки не встречал.
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
Hugo написал: Я про это кажется читал в какой-то статье.
вот так и рождаются мифы.
Жена приходит домой и говорит: - Ой, у нас на работе бабы говорят, что тебя скоро повысят по службе. - С чего бы это, у меня никаких заслуг нет. Немного погодя его действительно повысили по службе. Через некоторое время жена приходит и говорит: - Ой, у нас на работе бабы говорят, что у тебя какие-то нелады с документами, что будет ревизия, и тебя посадят. Мужик удивляется: - С чего они это взяли, твои бабы? Все у меня в порядке. Действительно, вскоре была ревизия, и мужика посадили. Взволнованная жена прибегает к нему на свидание: - Ой, была у адвоката, такого хорошего защитника тебе нашла... - Хрен с ним, с адвокатом. ты скажи, что бабы-то на работе говорят?
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
с этим сталкивался, при многократном запуске макроса сразу после Set d= CreateObject("Scripting.Dictionary") d.count кажет 1 или d.count пишет 0, а d.exists(...) пишет уже есть не говорю, что всегда, но на нескольких проектах это повторялось постоянно
до использования раннего связывания, я часто с этим (неочистка словаря методом .RemoveAll при позднем связывании) сталкивался и это довольно легко выловить. Данная проблема не относится к вопросу
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Алексей, я не понял в чем вопрос? В том, что в словаре не обеспечивается и неопределен порядок следования элементов? Да это так. Объект Dictionary является эквивалентом ассоциативного массива языка PERL. Ассоциативные массивы в Perl являются реализацией хэш-таблиц. В отличие от обыкновенных массивов, у ассоциативных массивов порядок следования элементов не определен и к ним нельзя обратиться по числовому индексу. Хеш таблицу нельзя сравнивать с массивом, в т.ч. и по порядку следования элементов. Матчасть: https://docs.microsoft.com/ru-ru/office/vba/language/reference/user-interface-help/dictionary-object#remarks
bedvit, приветствую, Виталий! Вопрос не про порядок (хотя я не замечал, чтобы он отличался от порядка наполнения), а про соответствие ключа и значения по индексам при выгрузке "коллекций" .Keys и .Items в отдельные массивы. Странно, что ты спрашиваешь, ведь я даже тест прикрепил…
Цитата
bedvit: по поводу работы методов при раннем связывании и не работы при позднем
Попробовал сделать файл без библы с таким кодом, но ничего
Код
Sub t()
Dim dic As Object
Dim x, n&
Set dic = CreateObject("Scripting.Dictionary")
For n = 1 To 100000
dic.Add n, 0
Next n
Debug.Print dic.Count
dic.RemoveAll
Debug.Print dic.Count
End Sub
в отличие от несоответствия, которого я не встречал, НЕочистка методом .RemoveAll мне встречалась не раз (на позднем)
UPD по ссылке на codenet: «В отличие от обыкновенных массивов, у ассоциативных массивов порядок следования элементов не определен и к ним нельзя обратиться по числовому индексу»к элементам массива спокойно можно обращаться по ключу (.Items(i) или .Items()(i) — для позднего) + порядок такой же, как при наполнении (во всяком случае, не замечал иного)
Пруф (скрин+файл)
«При вызове keys возвращается список ключей ассоциативного массива. При вызове values возвращается список значений ассоциативного массива. Эти функции возвращают списки в одинаковом порядке, хотя он и не имеет ничего общего с порядком добавления элементов в массив» Ну вот тут как раз написано, что соответствие есть, но надо учитывать, что тут не о словарях…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Добрый день. Никогда не сталкивался ни с одной из перечисленных в теме проблем Scripting.Dictionary, как и упоминаний о них на зарубежных Excrl/VBA-форумах. Полагаю, что какие-то проблемы могли быть вызваны иными причинами кода, не связанными непосредствено со Scripting.Dictionary. Если кому-то вдруг попадется пример с воспроизведением проблем, выложите, пожалуйста, с удовольсвием бы проанализировал.
ZVI, приветствую! Я вряд ли поймаю, т.к. использую ранее связывание, а вообще НЕочистка через .RemoveAll на позднем встречается настолько часто (при этом сходу воспроизвести в простом коде не вышло), что её заменяют (в решениях на форуме) на Set dic = Nothing уже чуть ли не автоматически
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Добрый вечер, Алексей. Запутаться можно и с ранним связыванием, вот, например: не запуская кода, показанного ниже, что ожидать в 3-х сообшениях?
Код
Sub Test()
'Требуется VBE-Tools-References-Microsoft Scripting Runtime
Dim dic As New Dictionary
dic.Add "Key", "Item"
MsgBox dic.Count()
Set dic = Nothing
If dic Is Nothing Then
MsgBox "dic is nothing"
Else
MsgBox "dic is not nothing"
End If
MsgBox dic.Count()
End Sub
Здесь все работает правильно, но, не понимая некторых внутренних правил, можно ошибиться в причинах поведения. Сравнивать dic.RemoveAll c Set dic = Nothing не очень-то корректно, т.к. это разные методы с разным результатом RemoveAll только удаляет элементы , но оставляет объект живым, а Set to Nothing принудительно анигилирует и сам объект. С поздним связыванием Set dic = Nothing в конце кода рекомендуется для устранения утечек памяти, которые иногда могут проявиться. Полагаю, что с претензиями к объектам Scripting.Dictionary тоже что-то не учли в коде, т.к. с их непредсказуемостью я ни разу не сталкивался. Хорошо бы, конечно, ссылку на конкретный пример с проблемой, если вдруг попадется - сообщите, пожалуйста.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
это важно - спасибо Про позднее, понятно, а вот раннее с другим подключением я даже не подумал бы
В массиве по-другому
Код
Option Explicit
Option Private Module
'====================================================================================================
Sub DicTestInArray()
Dim dic As Dictionary, dicNew As New Dictionary, dicOld As Object
Dim x, arrDic(), i&
Set dic = New Dictionary
Set dicOld = CreateObject("Scripting.Dictionary")
ReDim arrDic(2)
Set arrDic(0) = dic
Set arrDic(1) = dicNew
Set arrDic(2) = dicOld
For i = 0 To UBound(arrDic)
arrDic(i).Add "key1", "item1"
arrDic(i).Add "key2", "item2"
Next i
For Each x In arrDic
Debug.Print "One", x.Count; x.Count()
Next x
For i = 0 To UBound(arrDic)
Set arrDic(i) = Nothing
Debug.Print "Two", arrDic(i) Is Nothing
Next i
On Error Resume Next
For i = 0 To UBound(arrDic)
arrDic(i).Add "key1", "item1"
arrDic(i).Add "key2", "item2"
Next i
For Each x In arrDic
Debug.Print "Three", x.Count; x.Count()
Next x
End Sub
'====================================================================================================
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Алексей, у Вас код другой, в нем так и должно быть. Вот пример с массивом по теме сообщения #20:
Код
'Требуется VBE-Tools-References-Microsoft Scripting Runtime
Sub Test1()
Dim i As Long
Dim a(0 To 2) As New Dictionary
For i = 0 To UBound(a)
Set a(i) = Nothing
Debug.Print i, a(i) Is Nothing
Next
End Sub
' Для сравнения
Sub Test2()
Dim i As Long
Dim a(0 To 2) As Dictionary
For i = 0 To UBound(a)
Set a(i) = New Dictionary
Next
For i = 0 To UBound(a)
Set a(i) = Nothing
Debug.Print i, a(i) Is Nothing
Next
End Sub
у меня обычный массив со словарями внутри, а у вас словарные массивы одноэтапного или двухэтапного присвоения — так что это просто ещё вариант За тесты спасибо
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄