Основы программирования — второй семестр 08-09; Михалкович С.С.; V часть — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Абстрактные типы данных)
 
(не показаны 74 промежуточные версии 8 участников)
Строка 1: Строка 1:
[[Категория:Конспекты]]
+
[[Категория:Основы программирования]]
== АТД — Абстрактные Типы Данных ==
+
== АТД — Абстрактные Типы Данных. Классы как реализация АТД ==
 
=== Что мы знаем о классах ===
 
=== Что мы знаем о классах ===
 
# Класс — это составной тип данных
 
# Класс — это составной тип данных
Строка 8: Строка 8:
 
# Можно создавать шаблоны классов, параметризованные одним или несколькими типами
 
# Можно создавать шаблоны классов, параметризованные одним или несколькими типами
  
<small>Лекция 10</small>
+
=== Понятие абстрактного типа данных ===
 
 
=== Абстрактные типы данных ===
 
 
До сих пор мы сталкивались с конкретными типами данных, которые характеризуются:
 
До сих пор мы сталкивались с конкретными типами данных, которые характеризуются:
 
* ''набором допустимых значений''
 
* ''набором допустимых значений''
Строка 29: Строка 27:
 
Этот принцип называется '''''принципом сокрытия реализации''''' (или '''''защитой доступа''''').
 
Этот принцип называется '''''принципом сокрытия реализации''''' (или '''''защитой доступа''''').
  
Рассмотрим пример абстрактного типа данных — стек.
+
А теперь рассмотрим пример абстрактного типа данных — стек.
  
=== АТД стек ===
+
=== АТД Стек ===
 +
:'''Стек''' — это набор данных, устроенный по принципу '''''LIFO''''' (Last In First Out) — последним пришел — первым вышел.
  
Стек - это набор данных, устроенный по принципу LIFO (Last In First Out) - последним пришел - первым вышел.
+
Мы уже знакомы со стеком. Хорошими примерами могут служить ''программный стек'', ''колода карт'' или ''магазин автомата''. <br />
 +
Т.о. стек следует представлять как стопку объектов, положенных один на другой. В каждый момент можно:
 +
* ''положить'' значение ''на вершину'' стека (<tt>Push</tt>, втолкнуть значение в стек)
 +
* ''посмотреть'' значение ''на вершине'' стека (<tt>Top</tt>)
 +
* ''снять'' значение ''с вершины'' стека (<tt>Pop</tt>)
 +
* проверить, ''пуст ли'' стек (<tt>IsEmpty</tt>)
 +
Причем, если предмет последним положили на вершину стопки, то он будет снят первым - отсюда и название LIFO.
 +
<br />
 +
[[Изображение:Stack0.gif|180px|Стек]] <br />
  
Стек следует представлять как стопку предметов, положенных один на другой. В каждый момент можно положить предмет на вершину стека, посмотреть предмет на вершине стека, снять предмет с вершины и проверить, пуст ли стек. Причем, если предмет последним положили на вершину стопки, то он будет снят первым - отсюда и название LIFO.
+
Описывать стек будем в виде класса.
  
Хорошим примером стека является колода карт и магазин автомата.
+
<xh4>Интерфейс класса Stack</xh4>
 
+
<source lang="Delphi">
Опишем операции со стеком
+
type
# Положить значение на вершину стека (Push, втолкнуть значение в стек)
+
  /// Шаблон класса Stack
# Вернуть значение на вершине стека (Peek)
+
  Stack<T> = class
# Взять значение с вершины стека и вернуть его (Pop, вытолкнуть значение из стека)
+
    constructor Create;
# Пуст ли стек (IsEmpty)
+
   
 +
    /// Кладет элемент x на вершину стека
 +
    procedure Push(x: T);
  
Опишем интерфейс стека в виде класса:
+
    /// Возвращает элемент типа T, снимая его с вершины стека
 +
    function Pop: T;
 +
    /// Возвращает значение элемента на вершине стека
 +
    function Top: T;
  
<source lang="Delphi">
+
    /// Возвращает истину, если стек пуст
type Stack = class
+
    function IsEmpty: boolean;
  procedure Push(i: integer);
+
  end;
  function Pop: integer;
 
  function Peek: integer;
 
  function IsEmpty: boolean;
 
end;
 
 
</source>
 
</source>
  
Воспользуемся им в клиентской программе
+
Чтобы обеспечить '''''защиту доступа''''' к коду класса, следует поместить описание класса в ''модуль'' и ''откомпилировать'' его, или же создать ''библиотеку''. <br />
 +
При создании модуля интерфейс класса помещается в интерфейсную секцию модуля, а реализация методов класса — в секцию реализации модуля.
  
'''Задача.''' Вычисление постфиксного выражения.  
+
Теперь, когда у нас есть интерфейс класса <tt>Stack</tt> , напишем клиентскую программу. <br />
Пусть в строке a хранится выражение в обратной польской бесскобочной нотации, например:
+
<u>''Задача''</u>. Вычислить выражение, записанное в ''обратной польской записи (ПОЛИЗ)''.
 
+
: Пусть в строке <tt>a</tt> хранится выражение в обратной польской бесскобочной нотации, например: <br /> <tt>a = <span style="color: Blue">'598+46**7+*'</span></tt>  <br />Условимся, что каждый символ представляет собой либо цифру, являющуюся числом, либо знак, являющийся операцией.  
a='598+46**7+*'  
+
<xh4> Алгоритм вычисления выражения в ПОЛИЗ </xh4>
 
+
Цикл по символам {
Каждый символ представляет собой или число из одной цифры или знак операции. Требуется найти значение выражения.
+
  если текущий символ — цифра, то
 
+
    положить её на стек
'''Алгоритм.''' Если считано число, то поместим его на стек. Если считан знак операции, то применим данную операцию к двум последним операндам, лежащим на стеке, и запишем на стек вместо них полученное значение. По окончании работы алгоритма стек должен содержать единственное значение - ответ.
+
  иначе, если текущий символ — знак операции, то {
 +
    снять со стека два последних числа
 +
    совершить над ними указанную операцию
 +
    поместить результат на стек
 +
  }
 +
}
 +
В результате работы этого алгоритма на стеке останется единственное число, являющееся значением выражения.
  
 
Для указанной строчки алгоритм выполнит со стеком следующие операции:
 
Для указанной строчки алгоритм выполнит со стеком следующие операции:
 
+
ε (пусто)
 +
5
 +
5 9
 +
5 9 8
 +
 
  5 9 8  
 
  5 9 8  
+  
+
    +  
 
  5 17
 
  5 17
 +
 +
5 17 4
 
  5 17 4 6  
 
  5 17 4 6  
  *  
+
   
 +
5 17 4 6
 +
      *  
 +
5 17 24
 +
 
  5 17 24
 
  5 17 24
*  
+
    *  
 
  5 408
 
  5 408
 +
 
  5 408 7  
 
  5 408 7  
  +  
+
   
 +
5 408 7
 +
      +  
 
  5 415  
 
  5 415  
  *
+
   
  2075
+
5 415
 
+
  *
Пусть класс Стек определен в модуле Collections.
+
  '''2075'''
Код клиентской программы:
+
Запрограммируем этот алгоритм. <br />
 +
Пусть класс <tt>Stack</tt> определен в модуле <tt>Collections</tt>. <br />
 +
<u>''Код клиентской программы''</u>:
 
<source lang="Delphi">
 
<source lang="Delphi">
 
uses Collections;
 
uses Collections;
 +
 
var  
 
var  
   a: string;
+
   a: string := '598+46**7+*';
   s: Stack := new Stack;
+
   s: Stack<real> := new Stack<real>;
 +
 
 
begin
 
begin
  read(a);
+
   for var i := 1 to a.Length do
   for var i:=1 to Length(a) do
 
 
     case a[i] of
 
     case a[i] of
 
       '0'..'9': s.Push(StrToInt(a[i]));
 
       '0'..'9': s.Push(StrToInt(a[i]));
       '+': s.Push(s.Pop+s.Pop);
+
       '+': s.Push(s.Pop + s.Pop);
       '*': s.Push(s.Pop*s.Pop);
+
       '*': s.Push(s.Pop * s.Pop);
 +
      '-': begin
 +
            var minuend := s.Pop;
 +
            var subtrahend := s.Pop;
 +
            s.Push(minuend - subtrahend);
 +
          end;
 +
      '/': begin
 +
            var dividend := s.Pop;
 +
            var divisor := s.Pop;
 +
            s.Push(dividend / divisor);
 +
          end;
 
     end;
 
     end;
 
   writeln(s.Pop);
 
   writeln(s.Pop);
Строка 103: Строка 144:
 
</source>
 
</source>
  
Заметим, что клиентская программа ничего не знает, как устроен стек внутри - она пользуется стеком лишь как абстрактным типом данных
+
'''Замечание 1.''' Именно деление на интерфейс и реализацию позволило нам приступить к написанию клиентской программы, имея только интерфейс класса <tt>Stack</tt> и ничего не зная о его реализации. <br />
 +
Т.о. в большом проекте налицо разделение обязанностей:
 +
* Одна группа программистов — разработчики библиотек — создают АТД в виде классов и предоставляют остальным интерфейс этих классов
 +
* Другая группа программистов пользуется этими классами, как АТД, вызывая методы интерфейсов этих классов
 +
При таком способе разделения обязанностей обычно используется следующий сценарий:
 +
* Вначале быстро создается первая реализация класса (неэффективная) и предоставляется клиентам
 +
* Клиенты с помощью этой реализации пишут клиентские программы
 +
* В этот момент группа разработчиков класса делает более эффективную реализацию, после чего меняет на неё исходную. <br />При этом, все уже написанные клиентские программы продолжают работать.
 +
'''Замечание 2.''' Поскольку интерфейс впоследствии поменять практически нельзя (в отличие от реализации), разработка интерфейсов является важнейшим мероприятием на начальном этапе разработки проекта.
 +
 
 +
<xh4> Реализация стека на базе массива </xh4>
 +
<source lang="Delphi">
 +
unit Collections;
 +
 
 +
interface
 +
 
 +
type
 +
  /// Шаблон класса Stack
 +
  Stack<T> = class
 +
  private    // содержимое этой секции недоступно из клиентской программы 
 +
    /// Массив элементов стека
 +
    datas: array of T;
 +
    /// Индекс первого пустого элемента
 +
    tp: integer; 
 +
 
 +
  public    // содержимое этой секции открыто для клиентской программы
 +
    constructor Create;
 +
 
 +
    /// Кладет элемент x на вершину стека
 +
    procedure Push(x: T);
 +
 
 +
    /// Возвращает элемент типа T, снимая его с вершины стека
 +
    function Pop: T;
 +
    /// Возвращает значение элемента на вершине стека
 +
    function Top: T;
 +
 
 +
    /// Возвращает истину, если стек пуст
 +
    function IsEmpty: boolean;
 +
  end;
 +
 
 +
implementation
 +
 
 +
/// Максимальный размер стека
 +
const MAX_STACK = 1000;
 +
 
 +
constructor Stack<T>.Create;
 +
begin
 +
  tp := 0;
 +
  SetLength(datas, MAX_STACK);
 +
end;
 +
 
 +
{Кладет элемент x на вершину стека}
 +
procedure Stack<T>.Push(x: T);
 +
begin
 +
  Assert(tp < MAX_STACK);
 +
  datas[tp] := x;
 +
  tp += 1;
 +
end;
 +
 
 +
{Возвращает элемент типа T, снимая его с вершины стека}
 +
function Stack<T>.Pop: T;
 +
begin
 +
  Assert(not IsEmpty);
 +
  result := datas[tp-1];
 +
  tp -= 1;
 +
end;
 +
 
 +
{Возвращает значение элемента на вершине стека}
 +
function Stack<T>.Top: T;
 +
begin
 +
  Assert(not IsEmpty);
 +
  result := datas[tp-1];
 +
end;
 +
 
 +
{Возвращает истину, если стек пуст}
 +
function Stack<T>.IsEmpty: boolean;
 +
begin
 +
  result := (tp <= 0);
 +
end;
 +
 
 +
end.
 +
</source>
 +
 
 +
<xh4> Реализация стека на базе односвязного линейного списка </xh4>
 +
<source lang="Delphi">
 +
unit Nodes;
 +
 
 +
interface
 +
 
 +
type
 +
  /// Узел с одним полем связи
 +
  SingleNode<DataType> = class
 +
    /// Значение
 +
    data: DataType;
 +
    /// Ссылка на следующий элемент
 +
    next: SingleNode<DataType>;
 +
   
 +
    constructor (dt: DataType; nxt: SingleNode<DataType>);
 +
    begin
 +
      data := dt;
 +
      next := nxt;
 +
    end;
 +
  end;
 +
 
 +
implementation
 +
 
 +
end.
 +
</source> <br />
 +
<source lang="Delphi">
 +
unit Collections;
 +
 
 +
interface
 +
 
 +
uses Nodes;
 +
 
 +
type
 +
  /// Шаблон класса Stack
 +
  Stack<T> = class
 +
  private    // содержимое этой секции недоступно из клиентской программы 
 +
    /// Указатель на вершину стека
 +
    tp: SingleNode<T> := nil; 
 +
   
 +
  public    // содержимое этой секции открыто для клиентской программы
 +
    constructor Create;
 +
   
 +
    /// Кладет элемент x на вершину стека
 +
    procedure Push(x: T);
 +
 
 +
    /// Возвращает элемент типа T, снимая его с вершины стека
 +
    function Pop: T;
 +
    /// Возвращает значение элемента на вершине стека
 +
    function Top: T;
 +
   
 +
    /// Возвращает истину, если стек пуст
 +
    function IsEmpty: boolean;
 +
  end;
 +
 
 +
implementation
 +
 
 +
constructor Stack<T>.Create;
 +
begin
 +
  tp := nil;
 +
end;
 +
 
 +
{Кладет элемент x на вершину стека}
 +
procedure Stack<T>.Push(x: T);
 +
begin
 +
  tp := new SingleNode<T>(x, tp);
 +
end;
 +
 
 +
{Возвращает элемент типа T, снимая его с вершины стека}
 +
function Stack<T>.Pop: T;
 +
begin
 +
  Assert(not IsEmpty);
 +
  result := tp.data;
 +
  tp := tp.next;
 +
end;
 +
 
 +
{Возвращает значение элемента на вершине стека}
 +
function Stack<T>.Top: T;
 +
begin
 +
  Assert(not IsEmpty);
 +
  result := tp.data;
 +
end;
 +
 
 +
{Возвращает истину, если стек пуст}
 +
function Stack<T>.IsEmpty: boolean;
 +
begin
 +
  result := (tp = nil);
 +
end;
 +
 
 +
end.
 +
</source>
 +
 
 +
=== АТД и класс Очередь ===
 +
:'''Очередь''' — это набор данных, устроенный по принципу '''''FIFO''''' (First In First Out) — первым пришел — первым вышел.  <br />
 +
[[Изображение:Queue0.gif|220px|Очередь]] <br />
 +
 
 +
В каждый момент можно:
 +
* ''добавить'' значение ''в хвост'' очереди (<tt>Enqueue</tt>)
 +
* ''убрать'' значение ''из головы'' очереди (<tt>Dequeue</tt>)
 +
* проверить, ''пуста ли очередь'' (<tt>IsEmpty</tt>)
 +
Оформим '''АТД «очередь»'''  в виде шаблона класса.
 +
 
 +
<xh4> Интерфейс класса Queue </xh4>
 +
<source lang="Delphi">
 +
type 
 +
  /// Шаблон класса Queue
 +
  Queue<T> = class
 +
    constructor Create;
 +
   
 +
    /// Добавляет элемент x в хвост очереди
 +
    procedure Enqueue(x: T);
 +
   
 +
    /// Возвращает элемент типа T, убирая его из головы очереди
 +
    function Dequeue: T;
 +
   
 +
    /// Возвращает истину, если очередь пуста
 +
    function IsEmpty: boolean;
 +
  end;
 +
</source>
 +
 
 +
<xh4> Реализация очереди на базе односвязного линейного списка </xh4>
 +
<source lang="Delphi">
 +
unit Collections;
 +
 
 +
interface
 +
 
 +
uses Nodes;
 +
 
 +
type
 +
  // Здесь описан шаблон класса Stack
 +
 
 +
  /// Шаблон класса Queue
 +
  Queue<T> = class
 +
  private
 +
    /// Голова очереди
 +
    head: SingleNode<T>;
 +
    /// Хвост очереди
 +
    tail: SingleNode<T>;
 +
   
 +
  public
 +
    constructor Create;
 +
   
 +
    /// Добавляет элемент x в хвост очереди
 +
    procedure Enqueue(x: T);
 +
   
 +
    /// Возвращает элемент типа T, убирая его из головы очереди
 +
    function Dequeue: T;
 +
   
 +
    /// Возвращает истину, если очередь пуста
 +
    function IsEmpty: boolean;
 +
 
 +
  end;
 +
 
 +
implementation
 +
 
 +
// Здесь реализованы методы шаблона класса Stack
 +
 
 +
constructor Queue<T>.Create;
 +
begin
 +
  head := nil;
 +
  tail := nil;
 +
end;
 +
 
 +
{Добавляет элемент x в хвост очереди}
 +
procedure Queue<T>.Enqueue(x: T);
 +
begin
 +
  if IsEmpty then
 +
  begin
 +
    head := new SingleNode<T>(x, nil);
 +
    tail := head;
 +
  end
 +
  else  // if not IsEmpty
 +
  begin
 +
    tail.next := new SingleNode<T>(x, nil);
 +
    tail := tail.next;
 +
  end;
 +
end;
 +
 
 +
{Возвращает элемент типа T, убирая его из головы очереди}
 +
function Queue<T>.Dequeue: T;
 +
begin
 +
  Assert(not IsEmpty);
 +
 
 +
  result := head.data;
 +
  head := head.next;
 +
  if head = nil then
 +
    tail := nil;
 +
end;
 +
 
 +
{Возвращает истину, если очередь пуста}
 +
function Queue<T>.IsEmpty: boolean;
 +
begin
 +
  result := (head = nil);
 +
end;
 +
 
 +
end.
 +
</source>
  
=== Классы: основные понятия ===
+
====Примеры использования АТД Queue====

Текущая версия на 13:17, 2 мая 2014

АТД — Абстрактные Типы Данных. Классы как реализация АТД

Что мы знаем о классах

  1. Класс — это составной тип данных
  2. Класс, как и запись, содержит поля и методы
  3. Переменная типа класс и переменная типа запись по-разному хранятся в памяти (ссылочная и размерная организация данных)
  4. Для создания объекта класса и связывания его с переменной класса вызывается специальная функция-метод, называемая конструктором
  5. Можно создавать шаблоны классов, параметризованные одним или несколькими типами

Понятие абстрактного типа данных

До сих пор мы сталкивались с конкретными типами данных, которые характеризуются:

  • набором допустимых значений
  • представлением в памяти
  • набором допустимых операций, которые можно выполнять над объектами данного типа

Абстрактный тип данных — это тип данных, доступ к которым осуществляется только через некоторый набор действий (операций, команд).
Этот набор действий называется интерфейсом абстрактного типа данных.
То, как реализован абстрактный тип данных, самим АТД не определяется.

Итак, абстрактный тип данных - это интерфейс, набор операций без реализации.

Класс является одной из возможных реализацией абстрактного типа данных (АТД).
Т.е. класс определяет интерфейс абстрактного типа данных и дает реализацию этого интерфейса (без которой использование АТД невозможно).

Деление операций, расположенных в классе, на интерфейс и реализацию очень важно в современном программировании и называется принципом отделения интерфейса от реализации.
Он заключается в том, что клиентская программа, пользующаяся классом, использует только его интерфейс (в то время, как его реализация важна только разработчикам класса).

Более того, реализацию в классе принято скрывать от клиента специальными конструкциями.
Этот принцип называется принципом сокрытия реализации (или защитой доступа).

А теперь рассмотрим пример абстрактного типа данных — стек.

АТД Стек

Стек — это набор данных, устроенный по принципу LIFO (Last In First Out) — последним пришел — первым вышел.

Мы уже знакомы со стеком. Хорошими примерами могут служить программный стек, колода карт или магазин автомата.
Т.о. стек следует представлять как стопку объектов, положенных один на другой. В каждый момент можно:

  • положить значение на вершину стека (Push, втолкнуть значение в стек)
  • посмотреть значение на вершине стека (Top)
  • снять значение с вершины стека (Pop)
  • проверить, пуст ли стек (IsEmpty)

Причем, если предмет последним положили на вершину стопки, то он будет снят первым - отсюда и название LIFO.
Стек

Описывать стек будем в виде класса.

<xh4>Интерфейс класса Stack</xh4>

type 
  /// Шаблон класса Stack
  Stack<T> = class 
    constructor Create;
     
    /// Кладет элемент x на вершину стека
    procedure Push(x: T);

    /// Возвращает элемент типа T, снимая его с вершины стека
    function Pop: T;
    /// Возвращает значение элемента на вершине стека
    function Top: T;

    /// Возвращает истину, если стек пуст
    function IsEmpty: boolean;
  end;

Чтобы обеспечить защиту доступа к коду класса, следует поместить описание класса в модуль и откомпилировать его, или же создать библиотеку.
При создании модуля интерфейс класса помещается в интерфейсную секцию модуля, а реализация методов класса — в секцию реализации модуля.

Теперь, когда у нас есть интерфейс класса Stack , напишем клиентскую программу.
Задача. Вычислить выражение, записанное в обратной польской записи (ПОЛИЗ).

Пусть в строке a хранится выражение в обратной польской бесскобочной нотации, например:
a = '598+46**7+*'
Условимся, что каждый символ представляет собой либо цифру, являющуюся числом, либо знак, являющийся операцией.

<xh4> Алгоритм вычисления выражения в ПОЛИЗ </xh4>

Цикл по символам {
  если текущий символ — цифра, то
    положить её на стек
  иначе, если текущий символ — знак операции, то {
    снять со стека два последних числа
    совершить над ними указанную операцию
    поместить результат на стек
  }
}

В результате работы этого алгоритма на стеке останется единственное число, являющееся значением выражения.

Для указанной строчки алгоритм выполнит со стеком следующие операции:

ε (пусто)
5
5 9
5 9 8 

5 9 8 
   + 
5 17

5 17 4 
5 17 4 6 

5 17 4 6
      * 
5 17 24

5 17 24
    * 
5 408

5 408 7 

5 408 7
     + 
5 415 

5 415
 *
2075

Запрограммируем этот алгоритм.
Пусть класс Stack определен в модуле Collections.
Код клиентской программы:

uses Collections;

var 
  a: string := '598+46**7+*';
  s: Stack<real> := new Stack<real>;

begin
  for var i := 1 to a.Length do
    case a[i] of
      '0'..'9': s.Push(StrToInt(a[i]));
      '+': s.Push(s.Pop + s.Pop);
      '*': s.Push(s.Pop * s.Pop);
      '-': begin
             var minuend := s.Pop;
             var subtrahend := s.Pop;
             s.Push(minuend - subtrahend);
           end;
      '/': begin
             var dividend := s.Pop;
             var divisor := s.Pop;
             s.Push(dividend / divisor);
           end;
    end;
  writeln(s.Pop);
  Assert(s.IsEmpty);
end.

Замечание 1. Именно деление на интерфейс и реализацию позволило нам приступить к написанию клиентской программы, имея только интерфейс класса Stack и ничего не зная о его реализации.
Т.о. в большом проекте налицо разделение обязанностей:

  • Одна группа программистов — разработчики библиотек — создают АТД в виде классов и предоставляют остальным интерфейс этих классов
  • Другая группа программистов пользуется этими классами, как АТД, вызывая методы интерфейсов этих классов

При таком способе разделения обязанностей обычно используется следующий сценарий:

  • Вначале быстро создается первая реализация класса (неэффективная) и предоставляется клиентам
  • Клиенты с помощью этой реализации пишут клиентские программы
  • В этот момент группа разработчиков класса делает более эффективную реализацию, после чего меняет на неё исходную.
    При этом, все уже написанные клиентские программы продолжают работать.

Замечание 2. Поскольку интерфейс впоследствии поменять практически нельзя (в отличие от реализации), разработка интерфейсов является важнейшим мероприятием на начальном этапе разработки проекта.

<xh4> Реализация стека на базе массива </xh4>

unit Collections;

interface

type
  /// Шаблон класса Stack
  Stack<T> = class
  private    // содержимое этой секции недоступно из клиентской программы  
    /// Массив элементов стека
    datas: array of T;
    /// Индекс первого пустого элемента
    tp: integer;  
   
  public     // содержимое этой секции открыто для клиентской программы
    constructor Create;

    /// Кладет элемент x на вершину стека
    procedure Push(x: T);

    /// Возвращает элемент типа T, снимая его с вершины стека
    function Pop: T;
    /// Возвращает значение элемента на вершине стека
    function Top: T;

    /// Возвращает истину, если стек пуст
    function IsEmpty: boolean;
  end;
   
implementation

/// Максимальный размер стека
const MAX_STACK = 1000;

constructor Stack<T>.Create;
begin
  tp := 0;
  SetLength(datas, MAX_STACK);
end;

{Кладет элемент x на вершину стека} 
procedure Stack<T>.Push(x: T);
begin
  Assert(tp < MAX_STACK);
  datas[tp] := x;
  tp += 1;
end;

{Возвращает элемент типа T, снимая его с вершины стека}
function Stack<T>.Pop: T;
begin
  Assert(not IsEmpty);
  result := datas[tp-1];
  tp -= 1;
end;

{Возвращает значение элемента на вершине стека}
function Stack<T>.Top: T;
begin
  Assert(not IsEmpty);
  result := datas[tp-1];
end;

{Возвращает истину, если стек пуст}
function Stack<T>.IsEmpty: boolean;
begin
  result := (tp <= 0);
end;

end.

<xh4> Реализация стека на базе односвязного линейного списка </xh4>

unit Nodes;

interface

type
  /// Узел с одним полем связи
  SingleNode<DataType> = class
    /// Значение
    data: DataType;
    /// Ссылка на следующий элемент
    next: SingleNode<DataType>;
    
    constructor (dt: DataType; nxt: SingleNode<DataType>);
    begin
      data := dt;
      next := nxt;
    end;
  end;

implementation

end.

unit Collections;

interface

uses Nodes;

type
  /// Шаблон класса Stack
  Stack<T> = class
  private    // содержимое этой секции недоступно из клиентской программы  
    /// Указатель на вершину стека
    tp: SingleNode<T> := nil;  
    
  public     // содержимое этой секции открыто для клиентской программы
    constructor Create;
    
    /// Кладет элемент x на вершину стека
    procedure Push(x: T);

    /// Возвращает элемент типа T, снимая его с вершины стека
    function Pop: T;
    /// Возвращает значение элемента на вершине стека
    function Top: T;
    
    /// Возвращает истину, если стек пуст
    function IsEmpty: boolean; 
  end;

implementation

constructor Stack<T>.Create;
begin
  tp := nil;
end;

{Кладет элемент x на вершину стека} 
procedure Stack<T>.Push(x: T);
begin
  tp := new SingleNode<T>(x, tp);
end;

{Возвращает элемент типа T, снимая его с вершины стека}
function Stack<T>.Pop: T;
begin
  Assert(not IsEmpty);
  result := tp.data;
  tp := tp.next;
end;

{Возвращает значение элемента на вершине стека}
function Stack<T>.Top: T;
begin
  Assert(not IsEmpty);
  result := tp.data;
end;

{Возвращает истину, если стек пуст}
function Stack<T>.IsEmpty: boolean;
begin
  result := (tp = nil);
end;

end.

АТД и класс Очередь

Очередь — это набор данных, устроенный по принципу FIFO (First In First Out) — первым пришел — первым вышел.

Очередь

В каждый момент можно:

  • добавить значение в хвост очереди (Enqueue)
  • убрать значение из головы очереди (Dequeue)
  • проверить, пуста ли очередь (IsEmpty)

Оформим АТД «очередь» в виде шаблона класса.

<xh4> Интерфейс класса Queue </xh4>

type  
  /// Шаблон класса Queue
  Queue<T> = class
    constructor Create;
    
    /// Добавляет элемент x в хвост очереди
    procedure Enqueue(x: T);
    
    /// Возвращает элемент типа T, убирая его из головы очереди
    function Dequeue: T;
    
    /// Возвращает истину, если очередь пуста
    function IsEmpty: boolean; 
  end;

<xh4> Реализация очереди на базе односвязного линейного списка </xh4>

unit Collections;

interface

uses Nodes;

type
  // Здесь описан шаблон класса Stack

  /// Шаблон класса Queue
  Queue<T> = class
  private
    /// Голова очереди
    head: SingleNode<T>;
    /// Хвост очереди
    tail: SingleNode<T>;
    
  public
    constructor Create;
    
    /// Добавляет элемент x в хвост очереди
    procedure Enqueue(x: T);
    
    /// Возвращает элемент типа T, убирая его из головы очереди
    function Dequeue: T;
    
    /// Возвращает истину, если очередь пуста
    function IsEmpty: boolean;
  
  end;

implementation

// Здесь реализованы методы шаблона класса Stack

constructor Queue<T>.Create;
begin
  head := nil;
  tail := nil;
end;

{Добавляет элемент x в хвост очереди} 
procedure Queue<T>.Enqueue(x: T);
begin
  if IsEmpty then
  begin
    head := new SingleNode<T>(x, nil);
    tail := head;
  end
  else  // if not IsEmpty
  begin
    tail.next := new SingleNode<T>(x, nil);
    tail := tail.next;
  end;
end;

{Возвращает элемент типа T, убирая его из головы очереди}
function Queue<T>.Dequeue: T;
begin
  Assert(not IsEmpty);
  
  result := head.data;
  head := head.next;
  if head = nil then
    tail := nil;
end;

{Возвращает истину, если очередь пуста}
function Queue<T>.IsEmpty: boolean;
begin
  result := (head = nil);
end;
  
end.

Примеры использования АТД Queue