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

156 Глава 7

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

В языке Паскаль требуется, щобы все идентификаторы были объявлены до того, как они используются. Для написания вза­имно рекурсивных операций исильзуют^я формальные объявле­ния. Формальное объявление задает заголовок некоторой опе­рации, не задавая ее тела. Обвдц формат программы на языке Паскаль, соответствующий ограничениям на объявление, а затем на использование, всегда будет следующим: 1) реализация про­цедуры failure; 2) реализации всех абстрактных типов, которые будут использоваться в данной программе, включая и генераторы; 3) реализации всех операций, которые не являются частью не-когарого абстрактного тела, а также объявления констант, ти­пов и переменных и формальныесбъявления; 4) тело данной про­граммы.

Структура программы, соответствующей нашему примеру, была бы следующей: 1) реализация процедуры failure; 2) реализа­ция бинарного дерева целых чисе.т intbintree; 3) реализация оче­реди queue [intbintree]; 4) реализация функции breadth.first. serch; 5) другие объявления и тцо программы.

76. Обзор главы

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

В нашем случае мы обратились к языку Паскаль — языку, который имеет относительно примитивные механизмы абстракций. Мы определили, как надо модифицировать абстракции процедур, итераций и данных, чтобы соответствовать языку Паскаль, и пред­ложили соглашения по программированию, которые могут быть использованы для реализации абстракций данных, генераторов, полиморфных абстракций и обработки исключительных ситуаций па языке Паскаль. Наш подход 8 языку Паскаль может быть относительно просто адаптирован для других языков программи­рования (в течение нескольких лет мы читали курс, в котором использовался язык ПЛ/1). Мы рассмотрим такую адаптацию ч гл. 15.

Использование языка Паскаль

Дополнительная литература

Garland, Stephen J. 1986. Introduction to Computer Science with Applica­tions in Pascal. Mass.: Addison-Wesley Publishing Co.

Упражнения

7.1. Реализуйте тип бинарного дерева целых чисел intbintree, заданный на рис. 7.12.

7.2. Специфицируйте некоторую абстракцию bintree [type], основанную на абстракции бинарного дерева целых чисел intbintree, заданную на рис. 7,12.

7.3. Реализуйте очередь целых чисел intqueue (рис. 7.1) так, чтобы доступ к первому и последнему элементам и их удаление могли быть сделаны за постоян­ное время. Надо ли изменять реализацию функции breadth-first-search для того, чтобы использовать преимущества этой лучшей реализации очереди целых чисел inttjueue?

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

7.5. Модифицируйте функцию breadth-first-search (рис. 7.13) так, чтобы она работала с обобщенным деревом, которое специфицировано в результате Вашего ответа на упражнение 7.4.

7.6. 'Специфицируйте и реализуйте некоторую абстракцию стека и исполь­зуйте ее для реализации некоторой функции, которая выполняет поиск в ширину.

7.7. Специфицируйте и реализуйте некоторый генератор, который воз­вращает все листья в бинарном дереве целых чисел intbintree (рис. 7.12).

7.8. Что реализовано в программе, приведенной на рис. 7,14? (Предпола­гается, что сделаны спецификации, приведенные на рис, 7.1 и 7.10.)

program test: var i, sum: integer; q: intqueue; qi, qil: qelems; begin q := q-new;

for i := I to 3 do q.append (q, i); sum :== 0 qi := qelems_create (q); while not (qelems-done (qi)) do begin qil '.= qelems-create (q); while not (qelems-done (qil)) do

sum := sum + qelems-next (qil); qelems-destroy (qil); sum :== sum+ qelems_next (qi); end; qelems-destroy (qi); q .destroy (q); write Ln (sum) end {программа test)

Рис. 7.14. Программа на языке Паскаль»

8. Спецификации

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

8.1. Спецификации и необходимые наборы спецификаций

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

Определим/значение спецификации через набор' >ёсех про­граммных модулей, ей удовлетворяющих. Назовем это необходи­мым набором спецификаторов для данной спецификации. В ка­честве примера рассмотрим спецификацию

р = ргос (у: int) returns (х: int)

requires у j> 0 effects x >• у '.' ''•^'",'"•...!^,'^•'•.•''^

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

р = ргос (у: int) returns (int) return (у + 1) end р р = ргос (у: int) returns (int) return (у * 2) end р р = ргос (у: int) returns (int) return (у + 3) end р

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

Спецификг.ции

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

.2. Некоторые критерии) к спецификациям

применяемые

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

(" 8.2.1. Ограниченность

Lf Имеется большое различие между знанием о пригодности не­которых членов набора спецификаторов, и знанием того, что под­ходящими являются все члены. Это аналогично отличию знания о возможности работы программы с некоторыми наборами вход­ных значений, от знания возможности ее работы со всеми вход­ными значениями. Мы будем говорить о подобном отличии при обсуждении тестирования программ (см. гл. 9).'./Хорошая специ­фикация должна быть ограничена настолько, чтобы позволить выявить любую реализацию, не устраивающую пользователей данной абстракции. Это требование лежит в основе почти всех применений спецификаций. '/'

В общем случае обсуждение того, является ли спецификация достаточно ограниченной, предполагает рассмотрение задач, воз­лагаемых на членов набора спецификаторов,^меется ряд распро­страненных ошибок, которые почти всегда приводят к неадек­ватно ограниченным абстракциям^? К одной из таких ошибок' относитс^неполное задание требований в предложении requires.^/ Например, на рис. 8.1 приведены три спецификации итератора elems для портфеля целых чисел. (Портфель (bag) представляет собой обычный набор данных за тем исключением, что элементы в нем могут повторяться. Например, портфель bag [int] может содержать, число 3 дважды. Портфели иногда называют мульти-наборами.) В первой спецификации не дается ответа на вопрос, что произойдет, если b изменится в цикле, использующем elems. Она, следовательно, допускает реализации с резко отличающимся поведением. Например, приведет ли изменение b к изменению зна­чений, возвращаемых elems?

160 Глава S

elems = iter (b: bag [t]) yields (e: t) effects Поочередно выдает каждый элемент из Ь.

elems == iter (b: bag [t]) yields (e: t)

requires b не изменяется в цикле, использующем elems. effects Поочередно выдает каждый элемент из b.

elems = iter (b: bag [t]) yields (e: t)

requires b не изменяется в цикле, использующем elems. effects Выдает все элементы из b в произвольном порядке. Каждый эле­мент из b выдается столько же раз, сколько раз он встречается в b.

Рнс. 8.1. Три спецификации для elems.

Одним из способов решения данной проблемы является вве­дение требования о том, чтобы переменная b не изменялась вну­три использующего elems цикла. Это требование задано во вто­рой спецификации. Эта спецификация может бьпь, а может и не быть достаточно ограниченной, поскольку она не накладывает ограничений на порядок, в котором возвращаются элементы. Лучше, если спецификация будет определять порядок или содер­жать фразу «в произвольном порядке». Кроме того, в специфшка-ции не указано, что делается в том случае, если какой-нибудь элемент встречается в b более одного раза. В ней даже не говорится явно, что elems возвращает только те элементы, которые содер­жатся в b. В третьей спецификации рассмотренные недостатки устранены.

К другим ошибкам относится отсутствие полного списка ис­ключительных ситуаций и описания .поведения программы для граничных случаев. Например, пусть операция string^indexs получает в качестве аргументов строки si и s2, и, если si является подстрокой в s2, возвращает индекс вхождения в s2 первого сим­вола из si. Например: &iring,$'ndexs ("ab", "abc"») == 2

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

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

iiidexs = proc (si, s2: string) returns (i: int)

effects Если si входит в s2 как подстрока, то i есть последний индекс вхождения si в s2. i равно 0, если si не входит в s2, и рапно 1, если si есть пустая строка. indexs ("be", "abcbc") = 2 indexs (" ", "a") = I

Рис, 8.2, Спецификация операции strin^indexs,

Спецификации

^8.2.2. Обобщенность

Хорошаяуспецификация должна быть достаточно обобщенной, позволяя сократить до минимума число исключаемых, однако приемлемых программ.^Важность критерия обобщенности может быть не столь очевидна, как критерия ограниченности./Жела-тельно не только сократить ограничения на число приемлемых реализаций, но и обеспечить возможность использования наиболее удачных (более эффективных или «элегантных») из нихУНапример, спецификация

sqrt = proc (sq: real, e: real) returns (root: real)

requires sq^O & e;> .001. effects 0 ^ (root * root —sq) ^ e.

ограничивает разработчика алгоритмами, которые отыскивают приближения, больщ^е или равные фактическому значению квад­ратного корня;, Эт^Чотраничение может привести к неоправдан­ному снижению эффективности.

Желательно создавать спецификации максимально обобщен­ными. Это привело нас к «дефинитивному» стилю, который, и ис­пользован в данной книге.у Такая, спецификация яйй^Ь^ечис-ля^ свойства, которыми должны обладать спецификаторы. Аль­тернативой дефинитивной спецификации является операционная спецификация^Вместо описания свойств спецификаторов опера­ционная спецификация приводит способ конструирования их. Например,

search = proc (a: array [int], х: int) returns (i: int) signais (not.in) effects Анализирует a[low], a[low+ 1],...,поочгредно ii возвращает индекс элемента, равного х. Сигнализирует not-in, если ни один из эле­ментов не равен х.

есть операционная спецификация операции serach, в то время как

search = proc (a: array [int], х: int) returns (i: int) signals (not-in) effects Возвращает i, такое, что a[i] = х; х. Сигнализирует not_in, если такое i отсутствует.

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

"У Операционные спецификации имеют ряд преимуществ.1^Что более существенно./они &равнительно легко составляются опыт­ными программистами—главным образом по причине того, что их составление очень сильно напоминает сам процесс программи-

(> Листов Б., Гатэг Дж.

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