На этом шаге мы приведем общие сведения о произвольных деревьях с размеченными листьями.
Теперь мы можем ввести общее понятие дерева с размеченными листьями. Бинарные деревья с размеченными листьями - их частный случай.
Примем следующее определение:
Дерево с размеченными листьями - это последовательность ветвей, причем ветвь - это
В языке Pascal тип данных "произвольные деревья с размеченными листьями" не предусмотрены, однако мы можем построить инкапсулированный тип данных с этим именем.
Для этого вначале в соответствии с рекурсивным определением произвольного дерева с размеченными листьями мы получаем следующее описание типа [1, т.2, с.393-394,439-443]:
type Tree = ^PlexEl; PlexEl = Record O: Tree; Case Atomic: Boolean of TRUE : (Atom: Char); FALSE: (F : Tree) End;
Учтите, что в версии TURBO Pascal существуют ограничения: запись может содержать не более одной вариантной компоненты, и эта компонента должна быть последней в записи.
Названия полей произошли от:
Функции-конструкторы, необходимые для построения произвольного дерева с размеченными листьями реализуются следующим образом [1,т.2,с.439-443]:
FUNCTION E_m_p_t_y_T_r_e_e : Tree; { Построение пустого дерева } BEGIN E_m_p_t_y_T_r_e_e := Nil END; { --------------------------------------------- } FUNCTION A_p_p_e_n_d (C: Char; B: Tree): Tree; { Добавление "листа" C к дереву B } var h: Tree; BEGIN New (h); h^.Atomic := TRUE; h^.Atom := C; h^.O := B; A_p_p_e_n_d := h END;
Иллюстрируем графически действие функции A_p_p_e_n_d('C',B):
Рис.1. Результат функции A_p_p_e_n_d('C',B)
FUNCTION J_o_i_n (A: Tree; B: Tree): Tree; { Соединение деревьев A и B с образованием общего корня } var h: Tree; BEGIN New (h); h^.Atomic := FALSE; h^.F := A; h^.O := B; J_o_i_n := h; END;
Иллюстрируем графически действие функции J_o_i_n (A,B):
Рис.2. Результат функции J_o_i_n (A,B)
Терм для составленного из объектов A,B,C,D,E,F дерева с размеченными листьями
Рис.3. Терм
L := A_p_p_e_n_d (A,A_p_p_e_n_d (B, J_o_i_n ( A_p_p_e_n_d (C,E_m_p_t_y_T_r_e_e), A_p_p_e_n_d (D,A_p_p_e_n_d (E, A_p_p_e_n_d (F,E_m_p_t_y_T_r_e_e))) ) ))
PROGRAM L_I_S_P_A_S; {$A-} { Построение терма и вывод его на экран дисплея } type TipElement = Char; Tree = ^PlexEl; PlexEl = Record O: Tree; Case Atomic: Boolean of TRUE : (Atom: TipElement); FALSE: (F : Tree) End; var Ukaz0,Ukaz1,Ukaz2,Ukaz3,Ukaz4,Ukaz5,Ukaz6: Tree; Root: Tree; { ----------------------------------------- } FUNCTION J_o_i_n (A: Tree; B: Tree): Tree; { Соединение деревьев A и B с образованием общего корня } var h: Tree; BEGIN New (h); h^.Atomic := FALSE; h^.F := A; h^.O := B; J_o_i_n := h; END; { --------------------------------------------------- } FUNCTION A_p_p_e_n_d (C: TipElement; B: Tree): Tree; { Добавление "листа" C к дереву B } var h: Tree; BEGIN New (h); h^.Atomic := TRUE; h^.Atom := C; h^.O := B; A_p_p_e_n_d := h END; { --------------------------------- } FUNCTION E_m_p_t_y_T_r_e_e : Tree; BEGIN E_m_p_t_y_T_r_e_e := Nil END; { ---------------------------------------------------- } PROCEDURE P_r_i_n_t_T_r_e_e (Root: Tree; l: Integer); { Вывод бинарного дерева Root на экран дисплея } var i: Integer; BEGIN If Root^.Atomic then begin If Root^.O<>Nil then P_r_i_n_t_T_r_e_e (Root^.O,l) else; For i:=1 to l do Write (' '); Writeln (Root^.Atom) end else begin If Root^.O<>Nil then P_r_i_n_t_T_r_e_e (Root^.O,l) else; P_r_i_n_t_T_r_e_e (Root^.F,l+1); For i:=1 to l do Write (' '); Writeln ('#') end END; { --- } BEGIN Writeln ('Построим два дерева с размеченными листьями... '); Writeln ('---------------------------------------------- '); Ukaz0 := A_p_p_e_n_d ('F',E_m_p_t_y_T_r_e_e); Ukaz1 := A_p_p_e_n_d ('E',Ukaz0); Ukaz2 := A_p_p_e_n_d ('D',Ukaz1); Ukaz3 := A_p_p_e_n_d ('K',E_m_p_t_y_T_r_e_e); Ukaz4 := A_p_p_e_n_d ('C',Ukaz3); Ukaz6 := J_o_i_n (Ukaz4,Nil); Ukaz5 := J_o_i_n (Ukaz2,Ukaz6); Ukaz1 := A_p_p_e_n_d ('B',Ukaz5); Ukaz0 := A_p_p_e_n_d ('A',Ukaz1); P_r_i_n_t_T_r_e_e (Ukaz0,0); Writeln; Writeln ('---------------------------------------------- '); Ukaz0 := A_p_p_e_n_d ('F',E_m_p_t_y_T_r_e_e); Ukaz1 := A_p_p_e_n_d ('E',Ukaz0); Ukaz2 := A_p_p_e_n_d ('D',Ukaz1); Ukaz3 := A_p_p_e_n_d ('K',E_m_p_t_y_T_r_e_e); Ukaz4 := A_p_p_e_n_d ('C',Ukaz3); Ukaz5 := J_o_i_n (Ukaz2,Ukaz4); Ukaz1 := A_p_p_e_n_d ('B',Ukaz5); Ukaz0 := A_p_p_e_n_d ('A',Ukaz1); P_r_i_n_t_T_r_e_e (Ukaz0,0); Writeln END.
Тестовый пример:
Построим два дерева с размеченными листьями...
---------------------------------------------- K C # F E D # B A ---------------------------------------------- K C F E D # B A
Полученные результаты можно графически изобразить так:
Рис.4. Графическая интерпретация
Следующая программа показывает, как можно список языка LISP, вводимый с клавиатуры, преобразовать в дерево с размеченными листьями.
PROGRAM L_I_S_P_A_S; {$A-} { Представление списка LISP в виде произвольного дерева с } { размеченными листьями } type TipElement = Char; Tree = ^PlexEl; PlexEl = Record O: Tree; Case Atomic: Boolean of TRUE : (Atom: TipElement); FALSE: (F : Tree) End; Q = String [23]; var Root: Tree; { Полученное дерево с размеченными } { листьями } List: Q; { Вводимый список LISP } i : Integer; { --------------------------------- } PROCEDURE E_n_t_e_r (var T: Tree); { Построение дерева, соответствующего списку LISP } var X: TipElement; BEGIN X := List[i]; i := i + 1; If X<>'.' then begin { Помещаем элемент X лисповского списка в дерево } If X='(' then begin New (T); T^.Atomic := FALSE; E_n_t_e_r (T^.F); E_n_t_e_r (T^.O) end else If (X IN ['A'..'z']) then begin New (T); T^.Atomic := TRUE; T^.Atom := X; E_n_t_e_r (T^.O) end else If X=')' then T := Nil end END; { ---------------------------------------------------- } PROCEDURE P_r_i_n_t_T_r_e_e (Root: Tree; l: Integer); { Вывод произвольного дерева с размеченными листьями } var i: Integer; BEGIN If Root^.Atomic then begin If Root^.O<>Nil then P_r_i_n_t_T_r_e_e (Root^.O,l) else; For i:=1 to l do Write (' '); Writeln (Root^.Atom) end else begin If Root^.O<>Nil then P_r_i_n_t_T_r_e_e (Root^.O,l) else; P_r_i_n_t_T_r_e_e (Root^.F,l+1); For i:=1 to l do Write (' '); Writeln ('#') end END; { --- } BEGIN Writeln ('Построим дерево с размеченными листьями '); Writeln ('по заданному лисповскому списку... '); Writeln ('Вводите список LISP без пробелов между атомами. '); Writeln ('Последним символом должен быть символ "."...'); Read (List); i := 1; Root := Nil; E_n_t_e_r (Root); Writeln; P_r_i_n_t_T_r_e_e (Root,0); Writeln END.
Тестовый пример:
Построим дерево с размеченными листьями по заданному лисповскому списку... Вводите лисповский с п и с о к... (CU((KE)N)(G(I(T)))). T # I # G # N E K # # U C #
FUNCTION I_s_E_m_p_t_y_T_r_e_e (A: Tree): Boolean; BEGIN I_s_E_m_p_t_y_T_r_e_e := A=Nil END; { --------------------------------------- } FUNCTION I_s_A_t_o_m (A: Tree): Boolean; { NOT I_s_E_m_p_t_y_T_r_e_e (Tree) } BEGIN I_s_A_t_o_m := A^.Atomic { AND I_s_E_m_p_t_y_T_r_e_e (A^.O) } END; { ---------------------------------------------- } FUNCTION F_i_r_s_t_B_r_a_n_c_h (A: Tree): Tree; { NOT I_s_E_m_p_t_y_T_r_e_e (A) AND NOT I_s_A_t_o_m (A) } BEGIN F_i_r_s_t_B_r_a_n_c_h := A^.F END; { -------------------------------------------------- } FUNCTION O_t_h_e_r_B_r_a_n_c_h_e_s (A: Tree): Tree; { NOT I_s_E_m_p_t_y_T_r_e_e (A) } BEGIN O_t_h_e_r_B_r_a_n_c_h_e_s := A^.O END; { ---------------------------- } FUNCTION VAL (A: Tree): Char; { I_s_A_t_o_m (a) } BEGIN VAL := A^.Atom END;
Со следующего шага мы начнем рассматривать взаимосвязь языков LISP и LOGO.