[oodisc] OOo Basic - динамические переменные: как правильно описать и использовать

Владислав Орлов software на pro-za.com.ua
Чт Янв 27 17:43:10 MSK 2005


Добрый вечер, коллеги!
Нужно расширить набор функций Calc'а несколькими специальными прибамбасами.
Поставленные задачи лучше всего (ИМХО!) решаются с помощью динамических 
переменных ? одно- и двунаправленные списки, деревья и прочее похожее.

Главный вопрос: Как правильно работать с динамическими объектами из Бэйсика?

Например: (пример намерено упрощенный, реальные задачи ? сортировки, сложения 
и вычитания диапазонов вида (1, 14-17, 5-9) ? (14, 1, 8, 9) = (5-7, 15-17) 
слишком громоздкие, чтобы засылать их в рассылку)
REM  * Сортировка массива с исключением дублей
Option Explicit  

Type elementOfTreeType
  Val As Long
  Left As elementOfTreeType
  Right As elementOfTreeType
End Type

Sub Main
Call TestTree(Array(2, 8, 4, 3, 1, 6, 7))
Call TestTree(Array(3, 1, 2, 8, 4, 2, 8, 4, 7))
Call TestTree(Array(1, 2, 3, 3, 1, 2, 7))
Call TestTree(Array(1030, 412, 362, 217))
Call TestTree(Array(3, 1, 6, 7, 5, 2, 4, 6, 7, 3, 2, 5))
End Sub

Sub makeTree(curEl As elementOfTreeType, curVal as Long)
' Если именно такого элемента нет - вставить на свое место в дереве:
Dim newEl As New elementOfTreeType      ' ?????!!!!
If curEl.Val = curVal Then Exit Sub ' Такой элемент уже был, повторно не 
вносим
If curEl.Val < curVal Then      ' Идем направо 
        If NOT IsNull(curEl.Right) Then
                makeTree(curEl.Right, curVal)
        Else
                newEl.Val = curVal
                curEl.Right = newEl
        End If
Else ' curEl.Val > curVal       ' Идем налево 
        If NOT IsNull(curEl.Left) Then
                makeTree(curEl.Left, curVal)
        Else
                newEl.Val = curVal
                curEl.Left = newEl
        End If
End If  
End Sub

Sub aroundTree(curEl As elementOfTreeType, sRez As String)
' Обходим дерево, заполняя строку результата
        If IsNull(curEl) Then Exit Sub 
        aroundTree(curEl.Left, sRez) ' Пока можно влево - идем влево
        ' Текущий элемент вставляем в строку результата
        If Len(sRez) > 0 Then sRez = sRez + ", " + CInt(curEl.Val) Else sRez = 
CInt(curEl.Val) 
        aroundTree(curEl.Right, sRez) ' И сколько можно - идем вправо
End Sub

Sub TestTree (arrInt)
' Вывести на экран две строки - вход.массив и его же (отсортированный и без 
дублей)
Dim sIn As String,  sOut As String
Dim I As Integer, L As Integer, U As Integer

L = LBound(arrInt())
U = UBound(arrInt())

' Строка входящих
sIn = ""
For I = L To U
        If Len(sIn) > 0 Then sIn = sIn + ", "
        sIn = sIn + CInt(arrInt(I))
Next I

' Готовим дерево. Корневой элемент определяем отдельно:
Dim root As New elementOfTreeType       ' Здесь его и опишем и заполним 
' ?????!!!!
root.Val = arrInt(L)

' Оталкиваясь от корня - заполняем дерево
For I = L+1 To U
        makeTree(root, arrInt(I))
Next I

' Обходим дерево, заполняя строку результата
sOut = ""
aroundTree(root, sOut)

' Вывод результата
MsgBox  "Было >" + sIn + "<" +chr(10) + "Стало >" + sOut + "<" 
End Sub

Это все работает. И результат выдает. Но рождает кучу вопросов:
1.Будет ли это работать всегда? Не сломается ли на оооочень большом массиве? 
Ведь никаким явным образом мусор за собой не убирается. Или End Sub уже 
достаточно, чтобы почиститься за собой?
2.Если в строках, помеченных ' ?????!!!! убрать слово New, все продолжает 
работать ? то есть в дерево включаются новые элементы. А откуда для них 
берется память?
3.В makeTree оператор ?Dim newEl? срабатывает при каждом входе в процедуру. То 
есть, создается куча пустышек, из которых будет задействована только 
последняя? Или не создается?
4.Должен ли я явно создавать и удалять объекты? Или Бэйсик делает это все сам? 
Те примеры кода, которые уже просмотрел (?Useful Macro Information For 
OpenOffice By Andrew Pitonyak?, The Original Documentation is 
http://www.pitonyak.org/AndrewMacro.sxw ), содержат строки типа 
CreateObject(...), но они все относятся к каким-то 
"com.sun.star.чего-то-там"... Или это самое "чего-то-там" содержит готовое 
решение всех моих задач и я опять изобретаю велосипед?
5.В окне контрольных значений можно увидеть только значение отдельной 
переменной. Существует ли простой способ отслеживать значения типа  newEl.Val 
напрямую (без предварительного переприсвоения временной переменной)?

[Дополнено обсуждением с Alexej Kryukov]
> Локальные переменные в Бейсике существуют только внутри процедуры,
> где были объявлены, и только пока она выполняется. Если Вам
> вдруг в порядке исключения понадобилась статическая переменная,
> то ее и объявлять надо как Static.
Ну да... И нарваться на ошибку времени выполнения - "недостаточный размер 
стека..."... 
А прикольно было бы попробовать построить ДИНАМИЧЕСКИЙ список или дерево на 
СТАТИЧЕСКИХ переменных! Как там в "Понедельник начинается в субботу"? "А 
какой смысл решать проблему, если у нее уже есть решение?!!"
> Пользовательская структура -- это ведь не объект в 
> строгом смысле, т. к. потребный размер известен заранее, так что
> память выделяется при декларации, как для встроенных типов.
То есть, после каждого DIM, который ссылается на указанный тип мы резервируем 
память под структуру и освободим ее после выхода из процедуры?
Тогда какого же рожна правильно работает  makeTree?!!
Она действительно описывает новый структурированный элемент, заполняет 
(иногда :-) ) каким-то значением, в готовое дерево засовывает адрес этого 
нового элемента, а по выходу из процедуры - утилизирует его? Но если его УЖЕ 
НЕТ, почему обход дерева (aroundTree) находит его? Просто не успели затереть 
на маленьких тестовых массивах? А были бы большие данные - начались бы глюки, 
связанные с повторным использованием памяти? 
Или все наши порождения живут не до выхода из "процедуры, где были объявлены, 
и только пока она выполняется", а до выхода из MAIN'а? Или они вообще живут 
вечно (ну, пока ООо запущен)? И где тогда взять мозгов, чтобы проработать 
день ни разу не перезапустив программу? То есть - чиститься за собой нужно?..
> Поскольку Вы ее вызываете рекурсивно, то -- да, конечно, создается.
> А как же иначе? Не нравится -- объявляйте переменную как Static.
Ну, про это мы уже говорили - нарвемся на "глухой" цикл при обходе дерева, 
состоящего из одного только корня...
> Что он там делает -- бес его знает, но, во всяком случае, стандартных
> способов удаления объекта не предусмотрено. 
Какой-то dispose() явно есть... Но все примеры, к нему относящиеся, начинались 
с того самого "com.sun.star.чего-то-там"... А я в ЭТОМ пока плаваю (сильнее, 
чем во всем остальном :-) )
> Впрочем, как я сказал, локальные переменные уничтожаются автоматически по 
завершении процедуры.
По крайней мере - должны... Но ведь остаются же!

Всего доброго!
Владислав Орлов aka JohnSUN


Подробная информация о списке рассылки Oo-discuss