Продолжим создание программы, начатой в части 3.1.
Построение узлов. Описание блока:
Рассмотрим структуру блока 2.
Укрупнённая блок-схема для БЛОК2.
Оформим это описание в блок-схему:
Данной блок-схеме будет соответсвовать следующий код.
В процедуру передаётся ссылка на узел и массив ключей.
В результате работы процедуры будет построено полное дерево узлов, соответствующее ключам.
procedure Tform1.pr_BLOCK2(node_bunch:TTReeNode;
ARR_of_KEYS:T_ARR_of_KEYS);
var b:boolean; node_child, node_match:TTReeNode;
BOOL_NODE:T_BOOL_NODE;
begin
b:=true;
//цикл до тех пор, пока индикатор цикла b не станет равным false
while b do
begin //1
//найти в грозди узел, совпадающий с ключом
node_match:=f_BLOCK21(node_bunch,ARR_of_KEYS);
//если узел, совпадающий с ключом, есть
if node_match <> nil then
begin
//возвращаем дочерний узел по отношению к узлу node_match
BOOL_NODE:=f_BLOCK22(node_match,ARR_of_KEYS);
node_bunch:=BOOL_NODE.node;
b:=BOOL_NODE.b;
end;
//если узла, совпадающего с ключом, нет
if node_match = nil then
begin//2
//дополнить гроздь и сформировать дочерние узлы
pr_BLOCK23(node_bunch,ARR_of_KEYS);
b:=false;
end;//2
end; //1
end;
Описание работы процедуры.
BLOCK2 осуществляет вставку узлов дерева в зависимости от ряда условий. Первое условие — найдено ли совпадение значения ключа с одним из значений в грозди узлов на соответствующем уровне.
Второе условие — проверка логической переменной «b» при организации цикла. Само значение переменной «b» формируется или в блоке «BLOCK2», или передаётся из блока «BLOCK22». В «BLOCK22» передаётся ссылка на узел и проверяется, если ли у этого узла дочерний узел. Если дочернего узла нет, то внутри записи «BOOL_NODE» возвращается «nil» и «false».
Поиск узла, совпадающего с ключом. Описание блока:
В функцию передаётся ссылка на узел и массив ключей. Возвращает функция ссылку на узел, совпадающий с ключом.
function Tform1.f_BLOCK21(node:TTreeNode;ARR_of_KEYS:T_ARR_of_KEYS):TTreeNode;
var ARR_of_NODES:T_ARR_of_NODES; key:string;
begin
//получить ключ
key:=ARR_of_KEYS[node.Level];
//для данного узла node построить гроздь
ARR_of_NODES:=f_GET_ARR_of_NODES(node); //ОТЛАЖЕНО
//поиск совпадения (match) узла с ключом. Если совпадение есть, возвращается
узел.
node:=f_GET_NODE_FIND_MATCH(key,ARR_of_NODES); //ОТЛАЖЕНО
result:= node;
end;
Описание работы функции f_BLOCK21.
В функцию передаётся ссылка на узел «node». В иерархии дерева этот узел имеет уровень «node.Level».
В цепочке ключей этому уровню соответствует ключ «key:=ARR_of_KEYS[node.Level]».
Функция f_GET_ARR_of_NODES возвращает массив узлов (гроздь).
Эта функция (метод) возвращает динамический массив узлов, соответствующих данному уровню и принадлежащих ветви, на которой расположен узел «node».
Функция «f_GET_NODE_FIND_MATCH» определяет, есть ли совпадение одного из узлов грозди с ключом (match-совпадение). Если совпадение найдено, возвращается ссылка на узел, иначе «nil».
Создать гроздь, содержащую узел. Описание блока:
Оформим это описание в блок-схему:
//получить массив узлов (построить гроздь)
В функцию передаётся ссылка на узел. Обратно функция возвращает массив узлов.
function Tform1.f_GET_ARR_of_NODES(node:TTReeNode):T_ARR_of_NODES;
var arr_of_node:T_ARR_of_NODES; node_next:TTReeNode;
i:integer;
begin
i:=1;
//устанавливаем размерность динамического массива равной 1
setlength(arr_of_node,1);
//записываем узел в массив
arr_of_node[i-1]:=node;
//получить гроздь узлов, содержащих текущий узел node
while node<>nil do
begin
//на том же уровне взять узел, следующий после узла node
node_next:=node.getNextSibling;
//если узел не nil, то добавляем его в массив
if node_next<>nil then
begin
//Увеличиваем i на 1
inc(i);
//устанавливаем новую размерность динамического массива
setlength(arr_of_node,i);
//добавляем узел в массив
arr_of_node[i-1]:=node_next;
end;//if
//делаем узел node_next текущим (развязываем циклическую ссылку)
node:=node_next;
end;//while
result:=arr_of_node;
end;
Описание работы функции f_GET_ARR_of_NODES
В функцию предается ссылка на узел «node». Необходимо создать массив всех узлов, находящихсяна на одной ветви с узлом «node» и лежащих с ним на одном уровне (построить гроздь узлов, содержащих узел «node»).
Для развязки циклических ссылок вводим дополнительную переменную «node_next».
Функция возвращает массив узлов одного уровня — «гроздь узлов».
Найти узел, совпадающий с ключом. Блок-схема:
Функция возвращает ссылку на узел (или «nil»).
//в грозди ищем совпаление узла с ключом. При нахождении совпадения функция возвращает найденный узел, иначе возвращается nil
function Tform1.f_GET_NODE_FIND_MATCH(key:string;
ARR_of_NODES:T_ARR_of_NODES):TTReeNode;
var i,n_nodes:integer;
begin
//определить количество переменных в массиве (количество узлов в грозди)
n_nodes:=length(ARR_of_NODES);
//найти совпадение
for i:=0 to n_nodes-1 do
begin
if ARR_of_NODES[i].Text=key then
begin
result:=ARR_of_NODES[i];
exit;
end;
end;//for
result:=nil;
end;
Получить дочерний узел. BLOCK22
В блок передаётся ссылка на узел «node_match» и массив ключей «ARR_of_KEYS».
Функция возвращает запись типа «T_BOOL_NODE»
Задача функции — осуществление поиска узла, дочернего по отношению к узлу «node_match». Если дочерний узел есть, то он просто возвращается функцией.
Если такого узла нет (node_child=nil), то проверяется, можно ли продолжить цепочку узлов (то есть, есть ли ключи более высокого уровня, чем у узла «node_match» — процедура « pr_ADD_CHILD_NODES»).
function Tform1.f_BLOCK22(node_match:TTReeNode;ARR_of_KEYS:T_ARR_of_KEYS):T_BOOL_NODE;
var node_child:TTReeNode; BOOL_NODE:T_BOOL_NODE;
begin
//получить узел, дочерний по отношению к node_match
node_child:=node_match.getFirstChild;
BOOL_NODE.node:=node_child;
BOOL_NODE.b:=true;
result:=BOOL_NODE;
if node_child=nil then
begin //2
//добавить все дочерние узлы, если таковые могут быть
pr_ADD_CHILD_NODES(node_match, ARR_of_KEYS);
//признак выхода из цикла while в блоке BLOCK2
BOOL_NODE.b:=false;
result:=BOOL_NODE;
end; //2
end;
Процедура pr_ADD_CHILD_NODES.
Пусть на данный момент использовано три строки списка:
В результате будут выстроены узлы, расположенные на нулевом, первом, втором и третьем уровнях.
Следующей строкой будет:
На третьем уровне имеется соответствие одного из значений грозди третьего уровня (а именно — разд_3) с ключом, соответсвующим третьему уровню.
Как видно из уже построенной части дерева, у узла «разд_3» нет дочерних узлов.
Однако есть ключи, соответствующие четвёртому и пятому уровням.
Поэтому добавляем сначала дочерний узел четвёртого уровня:
А затем дочерний узел пятого уровня:
Реализация процедуры.
procedure TForm1.pr_ADD_CHILD_NODES(node:TTReeNode; ARR_of_KEYS:T_ARR_of_KEYS);
var n_level,n_key:integer;
begin
//определяем уровень узла node
n_level:=node.Level;
//определяем количество ключей
n_key:=length(ARR_of_KEYS);
while n_level<n_key-1 do
begin
node:=self.TreeView1.Items.AddChild(node,ARR_of_KEYS[n_level+1]);
n_level:=node.Level;
end;
end;
BLOCK23
Этот блок служит для добавления в гроздь нового узла. А также дочерних по отношению к нему узлов.
Этот блок вызывается, если при поиске совпадения узлов грозди с ключом такого совпадения не обнаружено. Тогда ключ добавляется в гроздь.
procedure Tform1.pr_BLOCK23(node_bunch:TTReeNode;ARR_of_KEYS:T_ARR_of_KEYS);
begin
//добавить в гроздь узел и все дочерние узлы
pr_ADD_NODES(node_bunch, ARR_of_KEYS);
end;
procedure TForm1.pr_ADD_NODES(node:TTReeNode; ARR_of_KEYS:T_ARR_of_KEYS);
var i,n_level,n_key:integer; key:string;
begin
//определить уровень переданного узла
n_level:=node.Level;
//взять ключ, соответствующий данному уровню
key:=ARR_of_KEYS[n_level];
//добавить узел в гроздь
node:=self.TreeView1.Items.Add(node,key);
pr_ADD_CHILD_NODES(node,ARR_of_KEYS);
end;
Далее полученное дерево можно сохранить в файле и загружать его по мере надобности.
Заключение.
Ещё раз посмотрим блок-схему взаимодействия разных блоков, входящих в «БЛОК2»:
В программе использованы методы:
node:=self.TreeView1.Items.AddFirst(nil,«название_узла»). Если в дереве нет узлов, то метод вставляется самый первый узел и возвращает ссылку на него.
Иначе, если AddFirst(«узел»,«название_узла»), то вставляемый узел станет первым в грозди, содержащей «узел».
node:=TreeView1.Items.AddChild(«родительский_узел», «название_узла»). Метод создаёт узел, дочерний по отношению к родительскому узлу,
node:=self.TreeView1.Items.Add(«узел»,«название_узла»). Метод вставляет узел после «узел» и возвращает ссылку на вставленный узел.
node:=TreeView1.Items.GetFirstNode. Метод возвращает ссылку на самый первый (то есть с абсолютным индексом «0») узел дерева. Если такого узла нет, то возвращает «nil».
node_next:=node.getNextSibling. На том же уровне, что и узел node, взять и вернуть следующий за ним узел.
node_child:=node.getFirstChild. Взять и вернуть узел, дочерний по отношению к узлу node.
Начало смотри «Delphi. Панель Win32. Компонент TreeView. Часть 3_1.»
Следующая статья «Delphi. Панель Win32. Компонент TreeView. Часть 4«