[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