Ада-95. Компилятор GNAT

       

Еще один пример стека


Предположим, что мы хотим создать пакет стека, но при этом, мы не хотим переписывать все подпрограммы. Мы уже имеем реализацию связанного списка, которая может служить основой для организации стека. Заметим, что пакет Lists сохраняет значения типа Integer.

Допустим, что существует пакет Lists, который описан следующим образом:

package Lists is

type List is private;

Underflow : exception;

procedure Insert_At_Head(Item : in out List; Value : in Integer); procedure Remove_From_Head(Item : in out List; Value : out Integer);

procedure Insert_At_Tail(Item : in out List; Value : in Integer); procedure Remove_From_Tail(Item : in out List; Value : out Integer);

function Full(Item : List) return Boolean; function Empty(Item : List) return Boolean;

-- возвращает пустой проинициализированный список function Init return List;

private

. . .

end Lists;

Позже можно будет использовать возможность Ады, которая позволяет скрыть полное описание типа в приватной секции.

В данном случае, описывается абсолютно новый тип (поскольку в этом заинтересован клиент), который реально реализован как производный тип.

В результате, клиент не может это "увидеть", и детали реализации остаются надежно спрятанными "под капотом".

Таким образом, реализация стека осуществляется следующим образом:

with Lists;

package Stacks is

type Stack is private;

Underflow : exception;

procedure Push(Item : in out Stack; Value : in Integer); procedure Pop(Item : in out Stack; Value : out Integer);

function Full(Item : Stack) return Boolean; function Empty(Item : Stack) return Boolean;

-- возвращает пустой проинициализированный стек function Init return Stack;

private

-- создаем новый тип, производя его от уже существующего -- это остается спрятанным от клиента, который будет -- использовать наш новый тип

type Stack is new Lists.List;

-- тип Stack наследует все операции типа List

-- это неявно описывается как -- -- procedure Insert_At_Head(Item : in out Stack; Value : in Integer); -- procedure Remove_From_Head(Item : in out Stack; -- Value : out Integer); -- . . . -- function Full(Item : Stack) return Boolean; -- function Empty(Item : Stack) return Boolean; -- function Init return Stack;

end Stacks;

<


Теперь нам необходимо установить связь между неявно описанными подпрограммами (унаследованными от типа List), и подпрограммами, которые должны быть публично доступны.

Это достаточно просто выполняется с помощью механизма "транзитного вызова".

package body Stacks is

procedure Push(Item : in out Stack; Value : in Integer) is

begin

Insert_At_Head(Item, Value); end Push; -- эту процедуру можно сделать более эффективной, используя: pragma Inline(Push);

procedure Pop(Item : in out Stack; Value : out Integer) is

begin

Remove_From_Head(Item, Value); exception => when Lists.Underflow => raise Stacks.Underflow; end Pop;

. . .

end Stacks;

Следует заметить, что все это справедливо для публично описанных подпрограмм, имена которых отличаются от имен неявно описанных подпрограмм (тех, которые унаследованы от типа List).

Однако, в спецификации пакета, присутствуют две функции Full, которые имеют профиль:

function Full(Item : Stack) return Boolean;

В данном случае, одна функция описана в публично доступной секции, а вторая, неявно описана в приватной секции.

Что же происходит на самом деле?

Спецификация первой функции в публично доступной секции пакета - это только обещание предоставить эту функцию.

Таким образом, это еще не реализованая функция, ее реализация будет выполнена где-то в теле пакета.

Кроме этой функции существует другая, неявно описанная функция, которая, по-случаю, имеет такие же имя и профиль.

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

Например, предположим, что Lists.Full всегда возвращает "ложь" (False), для индикации того, что связанный список никогда не заполнен полностью и всегда может увеличиваться.

Разработчику стекового пакета может понадобиться принудительное ограничение размера стека.

Например, так чтобы стек мог содержать максимум 100 элементов.

После чего разработчик стекового пакета соответствующим образом пишет функцию Stacks.Full.



Это будет не корректно для реализации функции Full по-умолчанию, которая наследуется от реализации связанного списка.

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

Единственным способом вызвать функцию Full пакета Lists, будет явный вызов этой функции.

package body Stacks is

function Full(Item : Stack) return Boolean is

begin

-- вызов оригинальной функции Full из другого пакета return Lists.Full(Lists.List(Item)); -- конверсия типа параметра end Full;

. . .

end Stacks;

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

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

Таким образом, Ада просто предусматривает решение проблемы, которая является неизбежным последствием наличия различных пространств имен.

Вариант языка в котором отсутствуют пространства имен - еще хуже чем такое решение.

Поэтому, "не надо выключать пейджер, только потому, что не нравятся сообщения".

Это очень существенное замечание, и не следует двигаться дальше, пока суть этого замечания не понятна.

Вид всего тела пакета Stacks может быть следующим:

use Lists;

package body Stacks is

procedure Push(Item : in out Stack; Value : in Integer) is

begin

Insert_At_Head(Item, Value); end Push;

procedure Pop(Item : in out Stack; Value : out Integer) is

begin

Remove_From_Head(Item, Value);

exception => when Lists.Underflow => raise Stacks.Underflow; end Pop; -- примечательно, что исключение объявленное в другом пакете -- "транслируется" в исключение описанное внутри этого пакета

function Full(Item : Stack) return Boolean is

begin

return Lists.Full(List(Item)); end Full;

function Empty(Item : Stack) return Boolean is

begin

return Lists.Empty(List(Item)); end Empty;

-- возвращает пустой проинициализированный стек function Init return Stack is

begin

return Stack(Lists.Init); end Init;

end Stacks;


Содержание раздела