Рекурсивная работа с Dictionary

Программирование на Visual Basic for Applications
Raptorgrrr
Начинающий
Начинающий
 
Сообщения: 7
Зарегистрирован: 27.08.2008 (Ср) 10:07

Рекурсивная работа с Dictionary

Сообщение Raptorgrrr » 27.08.2008 (Ср) 10:16

Добрый день!

Написал (на VBA 6)небольшой класс для работы с иерархиеческой структурой - можно строить дерево с бесконечным уровнем вложенности. Вот кусок


Код: Выделить всё
cDivision.cls
01 Private pName As String
02 Private pCCode As String
03 Private pMach As Boolean
04 Private pLevel As Long
05 Private pUID As Long
06 Private pParentID As Long

07 Public Units As Collection

08 Public Property Let Level(value As Long)
09    pLevel = CLng(value)
10 End Property

11 Public Property Get Level() As Long
12   Level = pLevel
13 End Property



Public Function FindEx(Name As String, Level As Long) As CSubdivision

      Dim Unit As CSubdivision
      Dim Child As CSubdivision
      Dim i As Long

10        If (CStr(Name) = pName) And (CLng(Level) = pLevel) Then
20         Set FindEx = Me
30         Exit Function
40        End If
               
           
50     For Each Child In Units
60         Set Unit = Child.FindEx(Name, Level)
70         If Not (Unit Is Nothing) Then
80             Set FindEx = Unit
90             Exit Function
100        End If
110   Next Child   
           
120       Set FindEx = Nothing
     
130 End Function


Главное тут FindEx - ищет нужную ветвь, рекурсивно высывая себя.
Процедура уходит вглубь на обну ветку, а если там ничего не нашел должен выйти выше на другую ветку, о опять начать спускаться.

Затем захотел использовать не Collection, а Scripting.Dictionary.
Замнил строки на такие:

Код: Выделить всё
07 Public Units As Scripting.Dictionary

50    For i = 0 To Units.Count
60    Set Unit = Units.Items(i).FindEx(Name, Level)
70        If Not (Unit Is Nothing) Then
80            Set FindEx = Unit
90            Exit Function
100        End If
110    Next i



Теперь возникает проблема: не хочет возращаться на уровень выше, валиться при вызове Units.Items(i).FindEx(Name, Level), если i=0.

Как бы мне правильно написать? И почему с For Each работает?

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictonary

Сообщение Viper » 27.08.2008 (Ср) 11:16

Видимо потому что индексация начинается с 1.
З.Ы. Код ужасен! :evil:
Весь мир матрица, а мы в нем потоки байтов!

Raptorgrrr
Начинающий
Начинающий
 
Сообщения: 7
Зарегистрирован: 27.08.2008 (Ср) 10:07

Re: Рекурсивная работа с Dictonary

Сообщение Raptorgrrr » 27.08.2008 (Ср) 11:54

Viper писал(а):Видимо потому что индексация начинается с 1.
З.Ы. Код ужасен! :evil:

Это как раз понятно. А в коде то что надо поменять?
Когда пишешь Units.Items(i), он (Dictonary) как ключ, или ображение к элементу i ?
Возможно код не идеален. Приведите пример хорошего кода, либо дайте ссылки, plz/

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Рекурсивная работа с Dictonary

Сообщение Хакер » 27.08.2008 (Ср) 12:12

Автор, зачем ты нумеруешь строки, и кастуешь лонг к лонгу?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Raptorgrrr
Начинающий
Начинающий
 
Сообщения: 7
Зарегистрирован: 27.08.2008 (Ср) 10:07

Re: Рекурсивная работа с Dictonary

Сообщение Raptorgrrr » 27.08.2008 (Ср) 12:17

1. Строки нумерую, что бы можно на них было ссылаться, и не я MZTools.
2. Привык типизировать. Для надежности.
А по сути вопроса?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Рекурсивная работа с Dictonary

Сообщение Хакер » 27.08.2008 (Ср) 12:23

Raptorgrrr писал(а):1. Строки нумерую, что бы можно на них было ссылаться, и не я MZTools.

Ссылаться на строки обычно незачем. Есть узкий ряд случаев, когда это нужно для goto. Т.е. узкий ряд случаев, когда можно использовать goto. Но в этих случаях прибегают к текстовым метками.

Численные метки это ужас как плохо.

MZTools в топку.

2. Привык типизировать. Для надежности.

Это не типизация, а лишняя писанина. Можешь писать CLng(Clng(clng(CLng(CLng(Clng(xxx)))))). Лучше не станет.

Но вот к чему следует привыкнуть, так это к расстановке ByVal там, где оно нужно.

А по сути вопроса?

Не вчитывался. Viper же, кажется, ответил?


P.S. Вообще же складывается впечатление, что автор до начала освоения VB довольно хорошо владел каким-то другим языком программирования.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictonary

Сообщение Viper » 27.08.2008 (Ср) 12:35

Raptorgrrr писал(а):
Viper писал(а):Видимо потому что индексация начинается с 1.
З.Ы. Код ужасен! :evil:

Это как раз понятно. А в коде то что надо поменять?
А что непонятного то? Замени
For i = 0
на
For i = 1
и спи спокойно дальше.
Ужас кода, во-первых, в метках строк, которые здесь нафиг не нужны, а во-вторых, в приведении строк к строкам, а Longов к Longам при помощи CStr и CLng, соответственно. Пример хорошего кода можешь найти на этом форуме, в Кирпичах, например.
З.Ы. Не из FORTRANа ли?
Весь мир матрица, а мы в нем потоки байтов!

Raptorgrrr
Начинающий
Начинающий
 
Сообщения: 7
Зарегистрирован: 27.08.2008 (Ср) 10:07

Re: Рекурсивная работа с Dictonary

Сообщение Raptorgrrr » 27.08.2008 (Ср) 12:39

2 Хакер
Цифры это не метки, они предназначены только для ссылки на строку, и добавлены только для этого вопроса. Т.е. д.б. типа: в строке 07 поменяю "ТО-ТО" на "ЧТО-ТО". Goto не использую, кроме случает обработки ошибок. (И где в VB try - catch?). Пишу сейчас в VBA - изучаю на ходу.
Может посмотришь мой ответ Viper?

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictonary

Сообщение Viper » 27.08.2008 (Ср) 12:46

Raptorgrrr писал(а):Может посмотришь мой ответ Viper?
Мой ответ ровно одним постом выше находится.
Весь мир матрица, а мы в нем потоки байтов!

Raptorgrrr
Начинающий
Начинающий
 
Сообщения: 7
Зарегистрирован: 27.08.2008 (Ср) 10:07

Re: Рекурсивная работа с Dictonary

Сообщение Raptorgrrr » 27.08.2008 (Ср) 12:52

2 Viper
Писал когда-то и на Fortran-77, и на Pascal, AutoLISP, Delphi, C, VBS, Java...

Индекс я поменял, неперь валиться сразу как получает item(1). Может это все таки ключ?
Приложил файлы класса и модуля.

Ребят, несмотря на ваш тон, я вам благодарен, за консультации. На других форумах вообще молчат.
Вложения
cSubdivision.zip
(3.26 Кб) Скачиваний: 59

Денис
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2734
Зарегистрирован: 07.11.2006 (Вт) 13:55
Откуда: Ейск, Краснодарский край

Re: Рекурсивная работа с Dictonary

Сообщение Денис » 27.08.2008 (Ср) 13:08

Raptorgrrr писал(а):И где в VB try - catch?


Код: Выделить всё
sub test1

On error resume next
   A1 = 1/0
On error goto 0

end sub



Код: Выделить всё
sub test2

On error Goto Test2_Error_Handler
   A1 = 1/0
On error goto 0

A2 = 2

exit Sub

Test2_Error_Handler:
  msgbox error.description

end sub
Программирование — богоизбранная дисциплина! Если бог и есть, то вселенную он скомпилировал, не иначе.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictonary

Сообщение Viper » 27.08.2008 (Ср) 13:20

Raptorgrrr писал(а):Индекс я поменял, неперь валиться сразу как получает item(1). Может это все таки ключ?
Какими словами ругается?
Raptorgrrr писал(а):Приложил файлы класса и модуля.
А вот если бы ты приложил целиком проект (выкинув все не относящееся к проблеме)то было бы еще лучше.
Весь мир матрица, а мы в нем потоки байтов!

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictonary

Сообщение Viper » 27.08.2008 (Ср) 13:58

Простенький тест показал, что, во-первых, нумерация таки начинается с 1, а во-вторых, никакой ругани. Так что просьба к автору все же выложить реальный работоспособный проект (лишнее, не относящееся к вопросу убрать) для уяснения причин ошибки.
Весь мир матрица, а мы в нем потоки байтов!

Raptorgrrr
Начинающий
Начинающий
 
Сообщения: 7
Зарегистрирован: 27.08.2008 (Ср) 10:07

Re: Рекурсивная работа с Dictonary

Сообщение Raptorgrrr » 27.08.2008 (Ср) 15:51

Выкладываю подготовленный файл. Остался толлько необходимый функционал. Для теста запустить процедуру BuildTree.
Закоментированный код - это то как я работал используя Collection.
Проблема таже - не выходит из рекурсии, валиться с ошибкой
Run-error 9
Subscript out range? т.е. выход за пределы диапазона.

Цель: поправить код так, что бы рекурсивный вызов отрабатывался без ошибок.
Вложения
Dict.zip
(23.15 Кб) Скачиваний: 72

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictonary

Сообщение Viper » 28.08.2008 (Чт) 10:37

Raptorgrrr писал(а):Проблема таже - не выходит из рекурсии, валиться с ошибкой
Run-error 9
Subscript out range? т.е. выход за пределы диапазона.
Еще бы! В FindEx имеется:
Код: Выделить всё
For i = 0 To Units.Count
Что естественно неправильно. Правильный вариант:
Код: Выделить всё
For i = 0 To Units.Count - 1
Далее, вылезала ошибка, вопящая о том, что ключ уже существует. Я так понимаю, что это опечатка на странице Excel. Поправил, стало работать нормально. Общее мнение по коду - "черт те что, и с боку бантик". При полном отсутствии комментариев, дальнейший разбор полетов невозможен. Останаовлюсь лишь на одной процедуре.
Код: Выделить всё
Private Function CreateUnit(Name As String, Level As Long, Optional Mach As Boolean, Optional CCode As String, _
                 Optional UID As Long, Optional ParentID) As CSubdivision
               
Dim Unit As New CSubdivision

Set CreateUnit = Nothing

If (Name <> vbNullString) And (Level > 0) Then
    Unit.Name = CStr(Name)
    Unit.Level = CLng(Level)
    If IsMissing(CCode) Then Unit.CCode = CCode
    Unit.UID = IIf(IsMissing(UID), 0, CLng(UID))
    Unit.ParentID = IIf(IsMissing(ParentID), 0, CLng(ParentID))
    Unit.Mach = IIf(IsMissing(Mach), False, CBool(Mach))
    Set CreateUnit = Unit
End If

End Function
Во-первых, все входные параметры должны быть ByVal. Во-вторых, Optional-параметры должны бы иметь значение по умолчанию. В принципе, если они не заданы, то для Boolean будет False, а для Long - 0. В-третьих, ParentID имеет тип Variant, почему? Логика подсказывает, что это должен быть Long. Далее, идем в текст процедуры.
Код: Выделить всё
Set CreateUnit = Nothing
Нафига? Возвращаемое значение функции и так Nothing изначально.
Код: Выделить всё
If (Name <> vbNullString) And (Level > 0) Then
Очередной раз возникает желание бить по голове. Если хочется выяснить, не пустая ли строка, проверь ее длину, ну или срfвни с нулем указатель на нее.
Код: Выделить всё
If Len(Name) Then
или
Код: Выделить всё
If StrPtr(Name) Then
Еще один нюанс. Вариант
Код: Выделить всё
If Len(Name) Then
    If Level > 0 Then
    End If
End If
Будет работать несколько шустрее, чем
Код: Выделить всё
If Len(Name) And Level > 0 Then

End If
Идем дальше
Код: Выделить всё
Unit.Name = CStr(Name)
Ну вот нафига преобразовывать строку в строку, тем более дважды, поскольку далее в процедуре присвоения свойств опять же торчит CStr? Следующая строка аналогично, но с CLng. Далее
Код: Выделить всё
If IsMissing(CCode) Then Unit.CCode = CCode
Бред в квадрате. Во-первых, IsMissing сработает только для необязательных параметров типа Variant, для всех остальных всегда будет использовано значение по по умолчанию и IsMissing заведомо False, и никакое значение Unit.CCode присвоено не будет. Но даже, если будет True, то плучается, что мы присваиваем пропущенное значение? Может наоборот? Следующие строки.
Код: Выделить всё
Unit.UID = IIf(IsMissing(UID), 0, CLng(UID))
IsMissing не сработает, всегда будет использоваться CLng(UID). Если бы вдруг сработало, то присваивали бы 0, а 0 и так значение по умолчанию, зачем огород городить? Таким образом, после прикладывания мозга к этой процедуре, она становится такой:
Код: Выделить всё
Private Function CreateUnit(ByVal Name As String, ByVal Level As Long, Optional ByVal Mach As Boolean = False, Optional ByVal CCode As String = vbNullString, _
                 Optional ByVal UID As Long = 0, Optional ByVal  ParentID As Long = 0) As CSubdivision
If Len(Name) Then
    If Level > 0 Then
        Set CreateUnit = New CSubdivision
        With CreateUnit
            .Name = Name
            .Level = Level
            .CCode = CCode
            .UID = UID
            .ParentID = ParentID
            .Mach = Mach
       End With   
    End If
End If
End Function
Весь мир матрица, а мы в нем потоки байтов!

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Рекурсивная работа с Dictonary

Сообщение Zenitchik » 28.08.2008 (Чт) 14:45

ЕМНИП, Item - просит ключ, а индексы не принимает.
Нужно работать с массивом Keys
Знание английского языка - затрудняет понимание кода

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictonary

Сообщение Viper » 28.08.2008 (Чт) 15:23

Zenitchik писал(а):ЕМНИП, Item - просит ключ, а индексы не принимает.
Нужно работать с массивом Keys
Вполне принимает, другое дело, что это несколько некорректный способ работы со словарем.
Весь мир матрица, а мы в нем потоки байтов!

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Рекурсивная работа с Dictonary

Сообщение Zenitchik » 28.08.2008 (Чт) 15:50

Хм... По моим экспериментальным данным - не принимает. Думает, что я даю ему ключ и выдает ошибку, если с таким ключем ничто не ассоциировано.
Хотя... У меня были числовые ключи... Возможно поэтому так и получалось...
Знание английского языка - затрудняет понимание кода

Raptorgrrr
Начинающий
Начинающий
 
Сообщения: 7
Зарегистрирован: 27.08.2008 (Ср) 10:07

Re: Рекурсивная работа с Dictionary

Сообщение Raptorgrrr » 29.08.2008 (Пт) 10:53

Viper за заботу о чистоте кода. Возьму за эталон. Писал на коленку в течении пары часов, о умолчальных значениях не знал.
Zenitchik , насколько я понял у меня та же проблема : даю индекс, он думает ключ, не находит , и сообщает об ошибке.

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictionary

Сообщение Viper » 29.08.2008 (Пт) 11:44

Raptorgrrr писал(а):Zenitchik , насколько я понял у меня та же проблема : даю индекс, он думает ключ, не находит , и сообщает об ошибке.
Свойство Item действительно использует ключ. Но у тебя в коде используется Items, который является массивом, соответственно используются индексы.
З.Ы. И переезжаем в VBA.
Весь мир матрица, а мы в нем потоки байтов!

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Рекурсивная работа с Dictionary

Сообщение Zenitchik » 04.09.2008 (Чт) 13:48

Items - это массив, содержащий все ключи. А ключи уже юзаем в методе Item.
Знание английского языка - затрудняет понимание кода

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Re: Рекурсивная работа с Dictionary

Сообщение Viper » 04.09.2008 (Чт) 15:00

Zenitchik писал(а):Items - это массив, содержащий все ключи. А ключи уже юзаем в методе Item.
Отнюдь. Функция Items возвращает массив содержащий все элементы словаря. Функция Keys возвращает массив, содержащий все ключи словаря. А свойство Item обеспечивает доступ к элементу по ключу.
З.Ы. Кстати, по причине того, что функция Items возвращает массив, содержащий все элементы, имеет смысл один раз получить этот массив, а не обращаться каждый раз к этой функции для получения каждого элемента. За исключением тех случаев, когда возможно изменение элементов между отдельными обращениями к функции.
Весь мир матрица, а мы в нем потоки байтов!

Zenitchik
Постоялец
Постоялец
 
Сообщения: 369
Зарегистрирован: 21.12.2006 (Чт) 14:48

Re: Рекурсивная работа с Dictionary

Сообщение Zenitchik » 04.09.2008 (Чт) 17:37

:(
Никогда себе не прощу...
Знание английского языка - затрудняет понимание кода


Вернуться в VBA

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 119

    TopList