/// Модуль содержит шаблоны классов /// Stack — стек /// Queue — очередь /// DynArray — динамический массив /// SimpleSet — простое множество на основе динамического массива /// AssocArray — простой ассоциативный массив на основе динамического массива пар /// LinkedList — двусвязный список unit Collections; //------------------------------------------------------------------------------ // Модуль упрощенных классов коллекций для обучения // Версия 1.0 // Copyright (c) 2009 Белякова Ю., Михалкович С.С., Саатчи А. //------------------------------------------------------------------------------ interface // -------------------------------- SingleNode --------------------------------- type /// Узел с одним полем связи SingleNode = class private /// Значение, содержащееся в узле fData: T; /// Ссылка на следующий элемент fNext: SingleNode; /// /// Конструктор /// /// Значение в узле /// Ссылка на следующий элемент public constructor Create(pData: T; pNext: SingleNode); begin fData := pData; fNext := pNext; end; property Data: T read fData write fData; property Next: SingleNode read fNext write fNext; end; // ----------------------------------- Stack ----------------------------------- type /// Шаблон класса Stack Stack = class private /// Вершина стека fTop: SingleNode := nil; public /// Создает пустой стек constructor Create; /// /// Кладет элемент на вершину стека /// /// Новый элемент procedure Push(x: T); /// Возвращает значение элемента на вершине, снимая его со стека function Pop: T; /// Возвращает значение элемента на вершине стека, не снимая его function Top: T; /// Возвращает истину, если стек пуст function IsEmpty: boolean; /// Преобразует содержимое стека в строку function ToString: string; override; /// Выводит содержимое стека на консоль procedure Print; /// Выводит содержимое стека на консоль с переходом на новую строку procedure Println; end; // ----------------------------------- Queue ----------------------------------- type /// Шаблон класса Queue Queue = class private /// Голова очереди head: SingleNode; /// Хвост очереди tail: SingleNode; public /// Создает пустую очередь constructor Create; /// /// Добавляет элемент в хвост очереди /// /// Добавляемый элемент procedure Enqueue(x: T); /// Возвращает значение элемента в голове, удаляя его из очереди function Dequeue: T; /// Возвращает значение элемента в голове очереди, не удаляя его function Top: T; /// Возвращает истину, если очередь пуста function IsEmpty: boolean; /// Преобразует содержимое очереди в строку function ToString: string; override; /// Выводит содержимое очереди на консоль procedure Print; /// Выводит содержимое очереди на консоль с переходом на новую строку procedure Println; end; // --------------------------------- DynArray ---------------------------------- const /// Минимальная емкость, устанавливаемая при создании массива MIN_CAP = 4; /// Коэффициент увеличения емкости массива при её нехватке INC_CAP_FACTOR = 2; type /// Шаблон класса DynArray [Динамический массив с автоконтролем памяти] DynArray = class private /// Встроенный динамический массив, содержащий данные fData: array of T; /// Размер массива fSize: integer; /// Емкость массива fCap: integer; /// Устанавливает элемент с индексом ind равным x procedure SetElem(index: integer; x: T); /// Возвращает элемент массива с индексом ind function GetElem(index: integer): T; public /// Создает массив размера 0 constructor Create; /// Создает массив размера size constructor Create(psize: integer); /// /// Выделяет новую память. Емкость увеличивается. /// (Если newCap меньше текущей емкости, ничего не происходит) /// /// Новая емкость массива procedure Reserve(newCap: integer); /// /// Устанавливает новый размер массива /// /// Новый размер массива procedure Resize(newSize: integer); /// /// Добавляет элемент в конец массива /// /// Добавляемый элемент procedure Add(x: T); /// /// Вставляет элемент в указанную позицию /// /// Позиция, в которую вставляется элемент /// Вставляемый элемент procedure Insert(pos: integer; x: T); /// /// Удаляет элемент массива из указанной позиции /// /// Позиция, из которой удаляется элемент procedure Remove(pos: integer); /// /// Возвращает индекс первого элемента массива равного искомому /// или -1, если такого элемента нет /// /// Искомый элемент function Find(x: T): integer; /// Количество элементов (размер) массива property Count: integer read fSize write Resize; /// Емкость массива property Capacity: integer read fCap write Reserve; /// Позволяет обращаться к элементам массива по индексу property Elem[index: integer]: T read GetElem write SetElem; default; /// Преобразует содержимое массива в строку function ToString: string; override; /// Выводит содержимое массива на консоль procedure Print; /// Выводит содержимое массива на консоль с переходом на новую строку procedure Println; end; // -------------------------------- SimpleSet ---------------------------------- type /// Шаблон класса SimpleSet SimpleSet = class private /// Элементы множества data: DynArray; public /// Создает множество constructor Create; /// /// Добавляет элемент во множество, если его там еще нет /// /// Добавляемый элемент procedure Add(x: T); /// /// Удаляет элемент из множества, если он там есть /// /// Удаляемый элемент procedure Remove(x: T); /// /// Возвращает истину, если множество содержит элемент /// /// Искомый элемент function Contains(x: T): boolean; /// Преобразует содержимое массива в строку function ToString: string; override; /// Выводит содержимое массива на консоль procedure Print; /// Выводит содержимое множества на консоль с переходом на новую строку procedure Println; end; // -------------------------------- AssocArray --------------------------------- type /// Шаблон класса AssocArray AssocArray = class private /// Ключи keys: DynArray; /// Значения, соответствующие ключам values: DynArray; /// Устанавливает значение элемента с ключом key равным value procedure SetElem(key: KeyType; value: ValueType); /// Возвращает значение элемента с ключом key function GetElem(key: KeyType): ValueType; public /// Создает ассоциативный массив constructor Create; /// Позволяет обращаться к элементам массива по ключу property Elem[key: KeyType]: ValueType read GetElem write SetElem; default; /// Преобразует содержимое ассоциативного массива в строку function ToString: string; override; /// Выводит содержимое ассоциативного массива на консоль procedure Print; /// Выводит содержимое ассоциативного массива на консоль с переходом на новую строку procedure Println; end; // ----------------------------- LinkedListNode -------------------------------- type /// Узел с двумя полями связи LinkedListNode = class private /// Значение, содержащееся в узле fData: T; /// Ссылка на предыдущий элемент fPrev: LinkedListNode; /// Ссылка на следующий элемент fNext: LinkedListNode; public /// /// Создает новый узел /// /// Значение узла /// Ссылка на предыдущий элемент /// Ссылка на следующий элемент constructor Create(data: T; prev, next: LinkedListNode); begin fData := data; fNext := next; fPrev := prev; end; /// Значение, содержащееся в узле property Value: T read fData write fData; /// Ссылка на предыдущий элемент — только на чтение property Prev: LinkedListNode read fPrev; /// Ссылка на следующий элемент — только на чтение property Next: LinkedListNode read fNext; end; // -------------------------------- LinkedList --------------------------------- type /// Двусвязный линейный список LinkedList = class private /// Первый элемент (голова) fFirst: LinkedListNode; /// Последний элемент (хвост) fLast: LinkedListNode; public /// Создает пустой список constructor Create; /// Первый элемент (голова) — только на чтение property First: LinkedListNode read fFirst; /// Последний элемент (хвост) — только на чтение property Last: LinkedListNode read fLast; /// /// Добавляет элемент x в начало списка /// /// Добавляемый элемент procedure AddFirst(x: T); /// /// Добавляет элемент x в конец списка /// /// Добавляемый элемент procedure AddLast(x: T); /// Удаляет первый элемент списка procedure RemoveFirst(); /// Удаляет последний элемент списка procedure RemoveLast(); /// /// Добавляет элемент x перед node /// /// Ссылка на элемент, перед которым нужно добавить новый /// Добавляемый элемент procedure AddBefore(node: LinkedListNode; x: T); /// /// Добавляет элемент x после node /// /// Ссылка на элемент, после которого нужно добавить новый /// Добавляемый элемент procedure AddAfter(node: LinkedListNode; x: T); /// /// Удаляет элемент node /// /// Ссылка на элемент, который нужно удалить procedure Remove(node: LinkedListNode); /// Преобразует содержимое списка в строку function ToString: string; override; /// Выводит содержимое списка на консоль procedure Print; /// Выводит содержимое списка на консоль с переходом на новую строку procedure Println; end; //============================================================================== implementation //Сообщения исключений const PopNilStackExceptionMessage = 'Попытка снятия элемента с пустого стека'; TopNilStackExceptionMessage = 'Попытка получения элемента с пустого стека'; DequeueNilQueueExceptionMessage = 'Попытка удаления элемента из пустой очереди'; TopNilQueueExceptionMessage = 'Попытка получения элемента из пустой очереди'; NegativeArraySizeExceptionMessage = 'Попытка присвоить размеру массива отрицательное значение '; InsOutOfArrayBoundExceptionMessage = 'Попытка вставки за границей массива в позицию '; RemOutOfArrayBoundExceptionMessage = 'Попытка удаления за границей массива из позиции '; SetElemOutOfBoundExceptionMessage = 'Попытка присвоить значение элементу за границей массива с индексом '; GetElemOutOfBoundExceptionMessage = 'Попытка получить значение элемента за границей массива с индексом '; RemoveFromNilListExceptionMessage = 'Попытка удаления из пустого списка'; AddNilNodeExceptionMessage = 'Параметр node является нулевой ссылкой'; RemoveNilNodeExceptionMessage = 'Параметр node является нулевой ссылкой'; // ----------------------------------- Stack ----------------------------------- constructor Stack.Create; begin fTop := nil; end; procedure Stack.Push(x: T); begin fTop := new SingleNode(x, fTop); end; function Stack.Pop: T; begin if IsEmpty then raise new Exception(PopNilStackExceptionMessage); Result := fTop.data; fTop := fTop.next; end; function Stack.Top: T; begin if IsEmpty then raise new Exception(TopNilStackExceptionMessage); Result := fTop.data; end; function Stack.IsEmpty: boolean; begin Result := (fTop = nil); end; function Stack.ToString: string; begin Result := ''; var curElem := fTop; while curElem <> nil do begin Result += curElem.data.ToString + ' '; curElem := curElem.next; end; end; procedure Stack.Print; begin writeln(ToString); end; procedure Stack.Println; begin writeln(ToString); end; // ----------------------------------- Queue ----------------------------------- constructor Queue.Create; begin head := nil; tail := nil; end; procedure Queue.Enqueue(x: T); begin if IsEmpty then begin head := new SingleNode(x, nil); tail := head; end else begin tail.next := new SingleNode(x, nil); tail := tail.next; // элемент удаляется из хвоста очереди (т.е. хвостом становится следующий элемент) end; end; function Queue.Dequeue: T; begin if IsEmpty then raise new Exception(DequeueNilQueueExceptionMessage); Result := head.data; head := head.next; // элемент удаляется из головы очереди (т.е. головой становится следующий элемент) if head = nil then tail := nil; end; function Queue.Top: T; begin if IsEmpty then raise new Exception(TopNilQueueExceptionMessage); Result := head.data; end; function Queue.IsEmpty: boolean; begin Result := (head = nil); if Result then Assert(tail = nil); end; function Queue.ToString: string; begin Result := ''; var curElem := head; while curElem <> nil do begin Result += curElem.data + ' '; curElem := curElem.next; end; end; procedure Queue.Print; begin write(ToString); end; procedure Queue.Println; begin writeln(ToString); end; // ---------------------------------- DynArray --------------------------------- constructor DynArray.Create; begin Create(0); end; constructor DynArray.Create(pSize: integer); begin if pSize < 0 then raise new Exception(NegativeArraySizeExceptionMessage + pSize.ToString); fSize := pSize; fCap := INC_CAP_FACTOR * pSize + MIN_CAP; // Устанавливаем емкость "с запасом" SetLength(fData, fCap); end; procedure DynArray.Reserve(newCap: integer); begin if newCap > fCap then begin SetLength(fData, newCap); fCap := newCap; end; end; procedure DynArray.Resize(newSize: integer); begin if newSize < 0 then raise new Exception(NegativeArraySizeExceptionMessage + newSize.ToString); if newSize > fCap then begin Reserve(INC_CAP_FACTOR * newSize); for var i := fSize to newSize - 1 do // явным образом заполняем новые элементы fData[i] := default(T); end; fSize := newSize; end; procedure DynArray.Add(x: T); begin Resize(fSize + 1); fData[fSize - 1] := x; end; procedure DynArray.Insert(pos: integer; x: T); begin if (pos < 0) or (pos > fSize - 1) then raise new Exception(InsOutOfArrayBoundExceptionMessage + pos.ToString); Resize(fSize + 1); for var i := fSize - 2 downto pos do fData[i + 1] := fData[i]; fData[pos] := x; end; procedure DynArray.Remove(pos: integer); begin if (pos < 0) or (pos > fSize - 1) then raise new Exception(RemOutOfArrayBoundExceptionMessage + pos.ToString); for var i := pos to fSize - 2 do // сдвиг элементов влево на 1, начиная с позиции pos fData[i] := fData[i + 1]; Resize(fSize - 1); end; function DynArray.Find(x: T): integer; begin Result := -1; for var i := 0 to fSize - 1 do if fData[i] = x then begin Result := i; exit; end; end; procedure DynArray.SetElem(index: integer; x: T); begin if (index < 0) or (index > fSize - 1) then raise new Exception(SetElemOutOfBoundExceptionMessage + index.ToString); fData[index] := x; end; {Возвращает элемент массива с индексом ind} function DynArray.GetElem(index: integer): T; begin if (index < 0) or (index > fSize - 1) then raise new Exception(GetElemOutOfBoundExceptionMessage + index.ToString); Result := fData[index]; end; function DynArray.ToString: string; begin Result := ''; for var i := 0 to fSize - 1 do Result += fData[i].ToString + ' '; end; procedure DynArray.Print; begin write(ToString); end; procedure DynArray.Println; begin writeln(ToString); end; // -------------------------------- SimpleSet ---------------------------------- constructor SimpleSet.Create; begin data := new DynArray; end; procedure SimpleSet.Add(x: T); begin if data.Find(x) = -1 then data.Add(x); end; procedure SimpleSet.Remove(x: T); begin var xPos := data.Find(x); if xPos <> -1 then data.Remove(xPos); end; function SimpleSet.Contains(x: T): boolean; begin Result := (data.Find(x) <> -1); end; function SimpleSet.ToString: string; begin Result := data.ToString; end; procedure SimpleSet.Print; begin write(ToString); end; procedure SimpleSet.Println; begin writeln(ToString); end; // -------------------------------- AssocArray --------------------------------- constructor AssocArray.Create; begin keys := new DynArray; values := new DynArray; end; procedure AssocArray.SetElem(key: KeyType; value: ValueType); begin var ind := Keys.Find(key); if ind <> -1 then Values[ind] := value else begin Keys.Add(key); Values.Add(value); end; end; function AssocArray.GetElem(key: KeyType): ValueType; begin var ind := Keys.Find(key); if ind <> -1 then Result := Values[ind] else Result := default(ValueType); end; function AssocArray.ToString: string; const NewLine = #13#10; begin Result := ''; for var i := 0 to keys.Count - 1 do Result += keys[i].ToString + ' ' + values[i].ToString + NewLine; end; procedure AssocArray.Print; begin write(ToString); end; procedure AssocArray.Println; begin write(ToString); end; // -------------------------------- LinkedList --------------------------------- constructor LinkedList.Create; begin end; procedure LinkedList.AddFirst(x: T); begin var val := new LinkedListNode(x, nil, fFirst); if fFirst <> nil then fFirst.fPrev := val; fFirst := val; if fLast = nil then fLast := fFirst; end; procedure LinkedList.AddLast(x: T); begin var val := new LinkedListNode(x, fLast, nil); if fLast <> nil then fLast.fNext := val; fLast := val; if fFirst = nil then fFirst := fLast; end; procedure LinkedList.RemoveFirst(); begin if fFirst = nil then raise new Exception(RemoveFromNilListExceptionMessage); fFirst := fFirst.fNext; if fFirst = nil then fLast := nil else fFirst.fPrev := nil; end; procedure LinkedList.RemoveLast(); begin if fLast = nil then raise new Exception(RemoveFromNilListExceptionMessage); fLast := fLast.fPrev; if fLast = nil then fFirst := nil else fLast.fNext := nil; end; procedure LinkedList.AddBefore(node: LinkedListNode; x: T); begin if node = nil then raise new Exception(AddNilNodeExceptionMessage); if node = fFirst then AddFirst(x) else begin var val := new LinkedListNode(x, node.fPrev, node); node.fPrev.fNext := val; node.fPrev := val; end; end; procedure LinkedList.AddAfter(node: LinkedListNode; x: T); begin if node = nil then raise new Exception(AddNilNodeExceptionMessage); if node = fLast then AddLast(x) else begin var val := new LinkedListNode(x, node, node.fNext); node.fNext.fPrev := val; node.fNext := val; end; end; procedure LinkedList.Remove(node: LinkedListNode); begin if node = nil then raise new Exception(RemoveNilNodeExceptionMessage); if node = fFirst then RemoveFirst else if node = fLast then RemoveLast else begin node.fPrev.fNext := node.fNext; node.fNext.fPrev := node.fPrev; end; end; function LinkedList.ToString: string; begin Result := ''; var cur := fFirst; while cur <> nil do begin Result := cur.Value.ToString + ' '; cur := cur.Next; end; end; procedure LinkedList.Print; begin write(ToString); end; procedure LinkedList.Println; begin writeln(ToString); end; end.