Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
86
Добавлен:
23.03.2015
Размер:
758.27 Кб
Скачать

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

Первым представлением абстракции была процедура.

Описание процедуры:

Процедура имя(входные данные) return (результирующие данные)

Вызов процедуры:

Имя(фактические параметры)

Выделяют 2 метода абстракции:

  1. Абстракция через параметризацию

  2. Абстракция через спецификацию

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

Пример:

λ xy:int , где - это выражение (λ), а 3 и 5 – параметры (x,y)

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

Это достигается снабжением процедуры некоторым описанием, которое называется спецификацией.

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

Спецификация имеет вид:

Заголовок Имя проц.(вх. данные) (рез. данные)

Тело Requires

Modifies

Effects

Requires– предусловие, необходимые требования для правильной работы

процедуры.

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

истины по завершении данной процедуры, в предположении, что

для данной процедуры было удовлетворено начальное

условие(requires).

Modifies– возможные побочные эффекты и изменения объектов из

предметной области.

Пример:

Sqrt(x:real):real;

Requires: x>=0

Effects:

Виды абстракции

  1. Процедурная абстракция

  2. Абстракция через данные

  3. Абстракция через итерацию

1. Процедурная абстракция.

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

2. Абстракция через данные.

Общий вид спецификации абстракции данных:

Dname = data type is <список операций>

Описание

<Здесь приводится описание абстракции данных>

Операции

<Здесь приводятся спецификации всех операций, как процедурных

абстракций или абстракций через итерации>

Enddname

Рассмотрим упрощенный пример: Наборы чисел

Заголовок Inset = data type is create, member, insert, delete, choose, size,

empty, destroy

Операции

Тело Спецификации всех операций, как процедурных абстракций или

абстракций через итерации

End

Иногда выделяют аксиомы операций для выражения их взаимосвязи. Для нашего примера это могут быть такие аксиомы:

  1. size(create(s))=0

  2. a:=size(s);

size(delete(s, x)) = a-1, if member(s, x) = true

size(delete(s, x)) = a, if member(s, x) = false

и др.

3. Абстракция через итерацию.

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

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

Итераторы в Паскале: for i:=1 to n do

for i:=n downto 1 do

Классический итератор имеет вид:

for i in elems(s) do

<<обработка значения i, выданного итераторомelems>>

2. АБСТРАКЦИЯ

Процесс абстракции может быть рассмотрен как некоторое обобщение. Он позволяет

нам забыть о несущественной информации, и, следовательно, рассматривать различные предметы,

как если бы они были эквивалентны. Мы выполняем это в надежде упростить наш

анализ, отделяя существенные атрибуты от несущественных.

Рассмотрим, например, два фрагмента программы

found:=false; found:=false;

i:=lowbound(a); i:=highbound(a);

while i<=highbound(a)+1 do while i>lowbound(a) do

begin begin

if a[i]=e then begin if a[i]=e then begin

z:=i; found:=true z:=i; found:=true

end; end;

i:=i+1 i:=i-1

end end

Ясно, что эти два фрагмента отличаются друг от друга и не находятся на требуемом

уровне абстракции.

Абстракция не может существовать без спецификации. Hазначение спецификации

состоит в определении поведения абстракции. У спецификации существует две

категории пользователей:

1) Пользователи полагаются на это поведение.

2) Разработчики должны обеспечить его.

2.1. Методы абстракции.

2.1.1. Абстракция через параметризацию.

Абстракция через параметризацию позволяет относительно просто описывать большое

число вычислений и очень легко и эффективно реализуется на языках программирования.

2.1.2. Абстракция через спецификацию.

Абстракция через спецификацию позволяет нам абстрагироваться от процесса

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

данная процедура должна в итоге реализовать. Это достигается путем задания

для каждой процедуры ее спецификации.

Удобно использовать пары утверждений (предикатов):

requires - предусловие, истинность или ложность которого должна проверяться

при входе в процедуру;

effects - постусловие, т.е. условие, которое должно быть истинным при

завершении данной процедуры в предположении, что для этой

процедуры было удовлетворено начальное условие.

Hапример,

function sqrt (x:real) :real;

requires x>0

effects Возвращает приближенное значение квадратного корня из x

При уяснении смысла спецификации мы придерживаемся двух четких правила

1) После выполнения процедуры конечное условие выполнено;

2) Можно ограничиться только теми свойствами, которые подразумевает

конечное условие.

Абстракция через спецификацию предоставляет нам два преимущества:

1) Для использования абстракции нам необязательно знакомиться с телом

процедуры, т.е. мы освобождаемся от необходимости уяснения подробностей

выполнения описанных в теле вычислений;

2) Тем самыс мы абстрагировались от тела процедуры, позволив себе не

обращать внимания на несущественную информацию.

Именно это "забывание информации" и отличает абстракцию от декомпозиции.

2.2. Виды абстракции.

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

создания программ, позволяют определить три различных вида абстракции:

1) процедурная абстракция;

2) абстракция данных;

3) абстракция итерации.

2.2.1. Процедурная абстракция.

Выполняет преобразования входных аргументов в выходные с возможной модификацией

входных аргументов. При создании данного вида абстракции используются оба метода

абстракции.

Абстракция через параметризацию позволяет нам абстрагироваться от конкретных

используемых данных. Значения конкретных используемых данных являются несущественными.

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

особенностями:

1) локальностью: реализация одной абстракции может быть создана или

рассмотрена без необходимости анализа реализации какой-либо другой абстракции.

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

людьми, работающими независимо друг от друга.

2) модифицируемость: упрощает модификацию программы. Hапример, абстракции,

реализация которых зависит от машины, можно выделить в отдельный модуль. Тогда

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

модуле.

Все это позволяет повысить эффективность разработки программы: рекомендуется сначала

начать с простого набора абстракций, апробировать систему, выясняя все узкие места,

а затем модифицировать соответствующие абстракции.

2.2.2. Абстракция данных.

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

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

Создание абстракции даннных использует абстракции как через параметризацию, так

и через спецификацию.

Абстракция данных = < объекты, операции>

При этом мы требуем, чтобы пользователи употребляли эти операции непосредственно,

не обращаясь к представлению.

При изменении представления мы должны заново реализовать операции. Однако

переделывать программы, использующие эти абстракции нет необходимости, т.к.

они не зависят от представления, а зависят только от операций.

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

до момента, когда эти структуры данных станут нам вполне ясны.

Абстракции данных полезны при модификации и сопровождении программ.

2.2.3. Абстракция итерации.

Абстракция итерации или, кратко, итератор является некоторым обобщением итерационных

методов, имеющихся в большинстве ЯП. Итераторы позволяют пользователям выполнять

итерации для произвольных типов удобным и эффективным способом.

3.2.1. Процедурная абстракция.

Всякий, кто оформлял выполнение некоторой функции в виде подпрограммы, реализовывал

тем самым процедурную абстракцию. Процедурная абстракция является первым и достаточно

мощным средством абстракции, используемым в ЯП. Она позволяет нам расширить заданную

некоторым ЯП виртуальную машину новой операцией. Такой вид расширения наиболее

полезен в том случае, когда мы работаем с задачами, которые легко представить

в виде набора независимых функциональных единиц. Для своего создания процедуры

используют методы абстракции через параметризацию и через спецификацию.

Процедура выполняет преобразование входных аргументов в выходные с возможной

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

от "несущественных" подробностей, обращая внимание лишь на те, которые имеют непосредственное

отношение к решаемой задаче. Реализации абстракции должны быть согласованы по всем таким

"существенным" подробностям, а в остальном могут отличаться.

3.2.1.1. Преимущества абстракции.

Абстракция через параметризацию позволяет нам абстрагироваться от конкретных

используемых данных путем их определения в терминах формальных параметров.

Фактические данные связываются с этими параметрами в момент использования такой

абстракции. Значения конкретных используемых данных являются несущественными;

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

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

в том, что они уменьшают объем программы и, следовательно, объем модификаций.

В абстракции через спецификацию мы фокусируем внимание на особенностях, от которых

зависит пользователь, и абстрагируемся от подробностей реализации этих особенностей.

Существенным является "поведение" - "то, что делается", а несущественным - то,

"как" это делается. Hапример, в процедуре сортировки массива существенным является

факт сортировки массива, а не сам алгоритм сортировки.

Главное преимущество абстракции через спецификацию заключается в несущественности способа

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

в программу, использхующую данную реализацию. Реализации могут быть даже выполнены

на различных ЯП при том условии, что типы аргументов в этих языках трактуются

одинаково.

А Абстракция

I1 ... In Реализации

Рис. Общая структура абстракции через спецификацию.

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

особенностями. Первая из этих особенностей заключается в локальности, которая

означает, что реализация одной абстракции может быть создана или рассмотрена

без необходимости анализа реализации какой-либо другой абстракции. Для написания

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

поведение, а не подробности ее реализации.

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

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

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

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

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

программы. Кроме того, для понимания работы программы достаточно понимания

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

необходимо знать, что реализуют собой сами используемые абстракции, а не конкретные

операторы их тела.

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

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

но ее спецификация остается при этом прежней, то эти изменения не повлияют на

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

достаточно велико, то на это по-прежнему тратится много времени. Этот объем

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

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

их небольшим числом абстракций. Hапример, эффект машинной зависимости може быть

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

на другую вычислительную машину.

Модифицируемость существенно повышает эффективность разработки программы. Рекомендуется

начать с простого набора абстракций, апробировать систему, выясняя узкие места,

а затем модифицировать соответствующие абстракции.

===================================Процедурная абстракция

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

В абстракции через параметризацию мы абстрагируемся от конкретных используемых данных. Эта абстракция определяется в терминах формальных параметров.

Фактические данные связываются с этими параметрами в момент использования такой абстракции.

При этом несущественным являются конкретные значения параметров, а существенными – их количество, типы, последовательность. Все это задается в заголовке любой процедуры.

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

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

Абстракция через спецификацию наделяет программиста двумя свойствами:

  1. локальностью: реализация одной абстракции может быть создана или рассмотрена без необходимости анализа реализации какой-либо другой абстракции, следовательно, имеется возможность составлять программы, созданные разными людьми независимо друг от друга.

  2. модифицируемость: упрощает модификацию программы.

Отличие процедуры от математической функции:

  1. процедура может иметь побочный эффект;

  2. процедура может иметь исключительные ситуации;

  3. процедура может обладать собственными значениями;

  4. процедура может зациклиться (следовательно, область определения может определяться PostFactum);

  5. процедура может выдавать неопределенные значения (значения, которые не принадлежат данному классу);

  6. процедура может менять некоторые глобальные данные.

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

Общий вид спецификации процедурной абстракции:

pname=proc(<спис. вх. арг.>)returns(<спис. вых. арг.>)signals(<спис. искл. сит.>)

requires: <необходимые требования, при которых программист, реализующий данную процедуру, может гарантировать ее правильное выполнение>

modifies: <список входных аргументов, которые могут изменяться>

effects: <общий эффект вызова процедуры, то, что получится>

endpname

3.2.1.2. Спецификации процедурных абстракций.

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

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

посредством спецификаций, которые создаются на языке спецификаций (ЯС).

Спецификация отлична от любой определяемой ей реализации абстракции. Все

реализации сходны между собой, поскольку они реализуют одну и ту же абстракцию.

Отличие их заключается в том, что это они делают разными способами. Спецификация

определяет их схожесть.

Спецификация процедуры состоит из заголовка и описания функции, выполняемой

процедурой. Заголовок содержит имя процедуры, количество, порядок и типы входных

и выходных параметров. Hапример, заголовок для прооцедуры sqrt есть

sqrt = proc (x: real) returns (rt: real)

Информация в заголовке чисто синтаксическая; она описывает "форму" процедуры.

Это аналогично описанию "фориы" математической функции:

f: integer --> integer

Hи в одном из случаев смысл действий, выполняемых процедурой или функцией не

описывается. Эта информация приводится в семантической части спецификации, в

которой смысл выполняемых процедурой действий описывается на естественном (разговорном)

языке, возможно расширенном привычными математическими обозначениями. В этом

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

Hа рис. приводится шаблон спецификации для процедуры. Семантическая часть

спецификации состоит из трех предложений - requires (требует), modifies (модифицирует)

и effects (эффекты). Эти предложения должны следовать в указанном порядке, причем

обязательной является только часть effects.

pname = proc (...) returns (...)

requires % эта часть задает необходимые требования

modifies % эта часть идентифицирует все модифицируемые входные данные

effects % эта часть описывает выполняемые функции

Предложение requires задает ограничения, накладываемые на абстракцию. Эта часть

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

ее поведение для некоторых входных значений является неопределенным. В противном случае

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

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

предъявляемымми при обращении к процедуре, являются требования, указанные в заголовке,

т.е. число и типы аргументов.

Предложение modifies задает список входных параметров, модифицируемых процедурой.

Если таких нет, то это предложение может быть опущено.

Hаконец, предложение effects определяет выходные значения и модификации, производимые над

входными параметрами, перечисленными в списке modifies. Предложение effects

составляется исходя из предположения, что требования предложения requires

удовлетворены. Однако в том случае, когда требования в предложении requires

не удовлетворены, о поведении процедуры ничего не сообщается.

concat = proc (a, b: string) returns (ab: string)

effects по возврату ab есть новая строка, содержащая символы из a (в том

порядке, в каком они расположены в a), за которыми следуют символы

из b (в том порядке, в каком они расположены в b).

remove_duples = proc (a: array [int])

modifies a

effects Удаляет из массива a все повторяющиеся элементы.

search = proc (a: array[int], x: int) returns (i: int)

requires массив a упорядочен по возрастанию.

effects Если элемент x принадлежит массиву a, то возвращается значение i

такое, что a[i] = x; в противном случае значение i на единицу

больше, чем значение верхней границы массива a.

Рис. Спецификации процедурной абстракции.

Спецификации составляются на языке спеецификаций, а не на языке программирования.

Кроме этого, спецификации обычно принципиально отличаются от программ, поскольку

они фокусируют свое внимание на описании самой абстракции, а не ее реализации.

3.2.1.3. Реализация процедур.

Реализация процедуры должна выполнять действия, определенные в спецификации. В

частности, она должна модифицировать только те входные параметры, которые указаны

в предложении modifies, а условия в предложении requires должны выполняться для всех

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

в предложении effects.

TYPE

ai = array[LowInd..HighInd] of integer;

function search(a: ai; x: integer): integer;

{ Реализация с использованием линейного поиска }

var

i: integer;

begin

i:=LowInd;

while i<=HighInd do begin

if a[i]=x then break;

i:=i+1

end;

search := i

end;

function search(a: ai; x: integer): integer;

{ Реализация с использованием бинарного дерева }

var Low, High, Mid, Val: integer;

begin

Low:=LowInd;

High:=HighInd;

while Low<=High do begin

Mid := (Low + High) div 2;

Val := a[Mid];

if x < Val then High:=Mid-1

else if x=Val then begin search := Mid; exit end

else Low := Mid + 1;

end;

search := HighInd+1

end;

В приводимом примере для облегчения прочтения и понимания программы мы придерживались

ряда соглашений. Во-первых, мы использовали для формальных параметров те же имена,

что и в спецификации. Это соглашение помогает связать реализацию абстракции со

спецификацией. За заголовком мы поместили комментарий, поясняющий работу алгоритма.

Hаконец, мы придерживались общепринятых правил форматирования.

3.2.1.4. Более обобщенные процедуры.

Рассмотренные ранее процедуры sort и search работают с произвольным массивом

целых чисел. Они бы оказались более полезными, если бы могли работать с различными

массивами данных - символьными, строковыми или массивами действительных чисел.

Можно добиться подобного обобщения, расширяя абстракцию через параметризацию и

используя типы данных в качестве прараметров.

При использовании типов в качестве параметров некоторые значения параметров могут не

иметь смысла. Hапример, массивы могут быть отсортированы только в том случае,

если элементы, принадлежащие к типу, упорядочены. Ограничения на параметры типа

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

должны обладать. Спецификация абстракции должна содержать эти ограничения в предложении

requires.

Hекоторые спецификации для параметризованных процедур показаны на рис. .

Отметим, что эти спецификации отличаются от непараметризованных процедур только

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

requires помимо прочих ограничений содержит ограничения, накладываемые на параметры.

sort = proc [t: type] (a: array[t]) returns(array[t])

requires t имеет операцию

lt: proctype(t,t) returnes (bool)

которая упорядочивает t

modifies a

effects Возвращает новый массив с теми же границами, что и a, и содержащий

элементы a, расположенные в возрастающем порядке; порядок устанавливается

через операцию lt типа t.

search = proc [t: type] (a: array[t], x: t) returns int

requires t имеет операции

equal, lt: proctype (t,t) returnes ( bool)

такой, что t упорядочивается через lt, и массив a упорядочен по возрастанию

через lt.

effects Если x принадлежит a, то возвращается i такое, что a[i]=x;

в противном случае возвращается high(a)+1

Рис. Спецификации параметризованных процедур.

Здесь lt и equal есть имена, используемые для операций, для которых, например в ПАСКАЛе

используются знаки "<" и "=" соответственно.

Параметризованная абстракция в действительности представляет собой класс

взаимосвязанных абстракций, определенных в одной спецификации. Hапример,

параметризованная абстракция sort представляет собой не одну параметризованную

процедуру, а целый класс процедур, одна из которых принимает в качестве параметра

массив целых чисел, другая - строковый массив и т.д. Мы будем поименовывать

такие процедуры сочетанием имени класса с именем типа, например

sort[целочисленная], sort[строковая].

Спецификация процедуры естественным образом получается из спецификации класса

путем замены имени параметра его значением и удалением из предложения requires

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

были удовлетворены). Следовательно спецификация для search[int] совпадает со спецификацией

для search[int] на рис. .

3.2.1.5. Создание процедурных абстракций.

В данном разделе рассмотрим ряд проблем, возникающих при создании процедурных абстракций.

Процедуры, как и другие виды абстракций, в процессе своего создания необходимо

минимизировать. В них должны быть реализованы только необходимые функции. Это

представляет разработчику большую свободу, позволяя ему создать более эффективную

версию. К одному из таких пунктов, который обычно остается неопределенным,

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

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

процедура становится недоопределенной. Это означает, что для определенных значений

входных параметров на выходе вместо единственного правильного результата

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

только одним значением, однако он может быть любым из числа допустимых.

Процедура search является недоопределенной, поскольку мы не указываем точно,

какой индекс должен быть возвращен в том случае, если значение x встречается в

массиве несколько раз. Две реализации процедуры search, приведенные на рис

отличаются именно в этом пункте. Однако каждая из них является корректной.

Главное, чтобы каждая существенная для пользователей подробность была оговорена в

спецификации, а остальные могут быть оставлены неопределенными.

Важным свойством прорцедур является обобщенность, которая достигается путем

использования параметров вместо переменных. Hапример, процедура, работающая с

массивами произвольного размера, является более обобщенной, чем та, которая

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

массивами произвольного типа, является более обобщенной, чем процедура, требующая,

чтобы все элементы массива были целыми числами. Однако важно, чтобы повышение

обобщенности процедуры приводило к повышению эффективности ее применения без

сушщественного снижения параметров ее работы (время и память).

Другой важной характеристикой процедур является простота. Процедура должна обладать

хорошо определенным и легко объяснимым назначением, независимым от контекста

ее использования. Хорошим правилом может служить присваивание процедуре имени,

описывающим ее назначение. В пределе хотелось бы иметь ЯП, позволяющий выражать

взаимосвязи между объектами предметной области на языке этой предметной области.

Такой ЯП называется языком программирования с настраиваемым синтаксисом. Примерами

таких ЯП являются, например, языки ФОРТ (FORTH) и ПРОЛОГ.

Hаконец, процедура должна действительно выполнять некоторые функции. Процедуры

создаются в процессе написания программы и служат цели упрощения и облегчения

работы с ней, а также создания более ясной структуры программы. При этом программа

должна становиться более понимаемой. Однако существует опасность введения

слишком большого числа процедур.

Процедуры могут быть частичными (присутствует раздел requires в спецификации)

или общими (никаких условий полмимо типа на параметры не накладывается). Возникает

вопрос о том, в каких случаях выгоднее определять частичную абстракцию. Частичные

процедуры не так безопасны, как общие, поскольку они требуют от пользователя

выполнения требований, заданных в предлоржении requires и которые нельзя проверить

средствами ЯП на этапе синтаксического анализа. Если эти требования не удовлетворены,

то поведение процедуры становится неопределенным, что может привести к неверной

работе программы с далеко идущими последствиями вплоть до разрушения объектов

данных.

С другой стороны, частичные процедуры могут оказаться более эффективными в реализации,

чем общие. Hапример, если процедура search должна работать даже в том случае,

когда массив является неупорядоченым, то единственным методом поиска может

служить анализ всех элементов массива, что, конечно, гораздо менее эффетивно,

чем бинарный поиск.

При выборе между частичной и общей процедурой мы должны для себя решить, что

для нас важнее - эффективность или корректное выполнение с меньшим числом потенциальных

ошибок. Одним из факторов, влияющих на это решение является ожидаемая область

применеия. Если процедура создается для общего пользования, то соображения

безопасности играют существенную роль. Другой случай предполагает использование

некоторых процедур в ограниченном контексте. В ограниченном контексте легко

обеспечить выполнение необходимых требований.

====================================Набок ===================================

Исключительные ситуации

Устойчиваяпрограмма —это такая программа, которая ведет себя корректно даже в случае ошибки.

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

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

Один из способов оповещения обратившегося —это определить спе­цифический результат, возвращаемый в случае ошибки. Напри­мер, процедура факторизации может возвратить нуль, если ее аргумент не положительное число. Однако это не очень хорошее решение. Использующий данную процедуру программист может проигнорировать эту ошибку по невнимательности. Кроме того, если каждое значение возвращаемого типа есть возможный результат процедуры, то это решение не приводит к положительному результату, так как нет значения, с помощью которого можно было бы оповестить об ошибке. Вместо того чтобы иметь одну область определения, мы разделим область определенияDна некоторое число подмножеств, объеди­нение которых дает D:

D=DoU...UDn

Каждое подмножество «особо» в том смысле, что процедура ведет себя по-разному на каждом из них. Мы обеспечим оповещение, имея различные области изменения для каждого подмножества области определения и придав различ­ным случаям различные имена. Произвольно дадим случаю аргумента из Do имя «обычный». Имена для других случаев могут быть выбраны тем, кто определяет процедуру. Таким образом, имеем: do —>- обычный (ro) di ->- имя1(ri) ,Dn ->имя n(Rn)где имя1, ...,имяn —имена «исключительных» ситуаций, соот­ветствующих аргументам, принадлежащим Di,... Dnсоответ­ственно. Имя1 ...,имяnназываютсяисключительными ситуациями.

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

SIGNALS: список имен и результатов исключительных ситуаций

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

Секция effectsдолжна определять, что приводит к вызову каждой исключительной ситуации и что делает про­цедура в каждом таком случае.

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

Исключительные ситуации следует использовать для устране­ния большинства ограничений, перечисленных в предложениях requires.Эти предложения следует оставлять только из сообра­жений эффективности или если контекст использования настолько ограничен, что мы можем быть уверены, что ограничения удов­летворяются.

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

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

Исключительные ситуации в языке Паскаль

Для обработки сбоев в программах на языке Паскаль мы за­даем некоторую процедуру, называемую failure. Эта процедура помещается в начало программы, и к ней может обращаться лю­бая другая операция в этой программе. Спецификация данной процедуры следующая:

procedure failure (s: error.msg)

modifiesВыходной поток на основном устройстве вывода.

effectsПечатает следующую строку на основном устройстве вывода: "Сбой. Программа окончилась из-за:" + s,а затем выполнение программы останавливается.

Аргумент при обращении должен, конечно, идентифицировать проблему и операцию, в ко­торой она произошла. Обычно тип error.msgбудет объявляться как некоторый тип строк. Если в используемом языке Паскаль строки не поддерживаются, аргумент может быть представлен как упакованный массив символов.

function searchi (var a: int. array; х: integer; var i: integer):searchi _exceptions

modifiesi

effectsЕсли переменная х находится в массиве а, то индексiустанавли­вается таким, что а [i] =х, и возвращается значениеok; в противном случае возвращается значениеnot-in.

где searchi-exceptions является вычисляемым типом type searchi_exceptions == (ok, not-in)

Отметим, что функция searchiимеет третий аргумент —параметр i стипом var.Эта переменная используется для получения ре­зультата в нормальном случае.

По соглашению все обращения к этой функции мы вставляем в предложение caseследующим образом:

case searchi (а, х, i) of

ok: a[i]:=a[i]+l;

not-in: write («Элемент не найден»)

end

Предложение caseдолжно иметь ветвь, соответствующую каждому значению, которое может быть возвращено функцией. (Если возможны только две альтернативы, как в случае с функциейsearchi, результат мог бы иметь булевский тип, а не вычисляе­мый тип, и операторifмог бы использоваться вместо предложе­нияcase.)

Этот подход имеет несколько недостатков. Неудобно, что функция searchiмодифицирует параметр с типом varвместо выдачи индекса, поскольку мы не сможем тогда ее использовать в таких выражениях, какa[searchI(a,x)] Более того, функцияsearchiне так надежна при использовании, как процедураsearch. На­пример, предположим, что мы решили добавить еще одно значе­ние в типsearchi_exceptions. В соответствии с нашими соглаше­ниями надо добавить еще одну ветвь к каждому предложениюcase, в котором имеется обращение к функцииsearchi. Если, од­нако, это не будет сделано, то не возникнет ни ошибки во время компиляции, ни ошибок во время счета. (В языке Паскаль, если переключатель в предложенииcaseне соответствует никакой из ветвей, программа продолжает выполнениеg предложения, сле­дующего за предложениемcase.)

4. Абстракции данных.

Процедуры позволяют добавлять в базовый уровень языка новые операции. Кроме

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

например целые, вещественные, логические, строки и т.д. Было бы неплохо иметь

возможность расширять базовый уровень языка и новыми типами. Эта потребность

может быть удовлетворена с помощью абстракции данных.

Какие типы данных необходимы для решения той или иной задачи зависит от самой

этой задачи. Поэтому ЯП должен обладать ограниченным числом базовых типов

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

этот уровень новыми типами данных. Приэтом желательно, чтобы использование

созданных пользователем типов данных не отличалось от использования встроенных

типов данных.

Создание новых типов данных должно использовать абстракции как через параметризацию,

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

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

Абстракции через спецификацию достигаются за счет того, что мы представляем

операции как часть типа. Почему операции необходимо представлять как часть

спецификации типа? Если ограничиться рассмотрением типа просто как набора объектов,

то все, что необходимо сделать для реализации типа, - это выбрать представление

памяти для объектов. Тогда все программы, использующие этот тип могут быть

реализованы в терминах этого представления. Однако, если представление изменяется,

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

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

абстракция данных = <объекты, операции>

При этом мы требуем, чтобы пользователи употребляли эти операции непосредственно,

не обращаясь к представлению. При реализации типа мы, естественно, выбираем некоторое

представление и реализуем операции в терминах этого представления. При изменении

же представления мы должны заново реализовать операции. Однако переделывать

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

от представления, а зависят только от операций. Это обеспечивается наличием абстракции

через спецификацию.

Если при создании нового типа обеспечено достаточное количество операций, то

отсутствие доступа к представлению не будет создавать для пользователей никаких

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

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

объектов, а также для получения информации об их значениях. Конечно, пользователи

могут увеличивать набор операций над объектами этого типа путем определением

ъпроцедур, но такие процедуры не должны использовать представлени типа. Такие

независимые от представления процедуры принято называть утилитами типа.

Абстракция данных - наиболее важный метод в проектировании программ и он положен

в основу изложения предмета нашего разговора - структур данных. Выбор правильных

структур данных играет решающую роль для создания эффективной программы. Абстракция

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

структуры станут нам вполне ясны. Вместо того, чтобы определять структуру

непосредственно, мы вводим абстрактный тип со своими объектами и операциями.

Затем можно осуществлять реализацию модулей в терминах этого абстрактного типа.

Решение же относительно реализации типа может быть принято позже, когда использование

данных станет для нас вполне понятным.

Абстракции данных также полезны при модификации и эксплуатации программ. Чаще

предпочтительно изменить реализацию абстракций данных для повышения производительности

программы или при изменении условий эксплуатации, чем изменять саму программу.

Абстракции данных сводят все изменеия к изменеиям в реализации типа - модули уже нет

необходимости изменять.

4.1. Спецификации для абстракции данных.

Значение типа не должно задаваться никакой его реализацией. Вместо этого должна

иметься определяющая его спецификация. Так как объекты типа используются только

посредством вызова операций этого типа, основная часть спецификации типа состоит

из спецификаций операций типа как процедур. Общий вид спецификации представлен на

рис. . Она сотоит из заголовка, определяющего имя типа и имена его операций, и

двух главных секций - секции описания и секции операций.

dname = data type is <список операций>

Описание <здесь приводится описание абстракции данных как целого>

Операции <здесь задаются спецификации для всех операций>

end dname

Рис. Общий вид спецификации абстракции данных.

В секции описания тип описывается как целое. Иногда там задается модель для объектов, т.е.

объекты описываются в терминах других объектов - таких, которые по предположению

понятны тем, для кого эта спецификация предназначена. Hапример, стеки могут быть

описаны в терминах математических последовательностей. З десь также говорится,

изменяемый или неизменяемый это тип. Это все важно для понимания поведения и

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

В секции операций содержатся спецификации для всех операций. Если операция -

процедура, то ее спецификация будет процедурной спецификацией, сделанной так

же как это было рассмотрено в предыдущем разделе. Hекоторые операции могут

быть итераторами. Понятие об итераторах и их спецификации будет рассмотрено

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

описания.

Hа рис 4.2. представлена спецификация абстракции данных intset&

================================ Яременко ===============================

Соседние файлы в папке POSIBNIK