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

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

В данном разделе мы рассмотрим ряд проблем, возникающих при создании процедурных абстракций. Процедуры, как и дру­гие виды абстракций, которые мы рассмотрим позднее, в процессе своего создания необходимо минимизировать. В них должны быть реализованы только необходимые "функцйТГ Это предоставляет разработчику большую свободу, позволяя ему создать более эф­фективную версию. Например, если приведенная на рис. 3.5 процедура sort позволяет модифицировать аргумент, заданный массивом, то это уменьшает занимаемый ею объем памяти. Од­нако список значимых для пользователя подробностей такого рода должен быть ограничен.

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

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

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

Недоопределенная абстракция может иметь детерминирован­ную реализацию, т. е. такую, которая, будучи вызванной два раза с идентичными входными данными, выполняется одинаково. Обе реализации процедуры search, приведенные на рис. 3.4, являются детерминированными. (Недетерминированные реализации тре­буют использования своих собственных переменных, описанных в приложении А. В настоящей работе они не рассматриваются.)

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

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

Наконец, процедура должна действительно выполнять неко­торые функции. Процедуры создаются в процессе написания про­граммы и служат цели упрощения и облегчения работы с ней, а также создания более ясной структуры программы. При этом программа становится более легко понимаемой. Однако сущест­вует опасность введения слишком большого числа процедур. Например, приведенная на рис. 3.6 процедура merge полезна потому, что ее назначение четко выражено, а также потому, что она позволяет нам отделить друг от друга задачи сортировки и слияния. Это делает процедуру merge_sort более ясной и понятной. По всей видимости, дальнейшая декомпозиция является избыточ­ной. Например, тело цикла в процедуре merge может быть вы­делено в процедуру, однако в этом случае ее назначение трудно определить.

Некоторые описанные ранее процедуры являются частичными, другие — общими. Подобная дихотомия поднимает вопрос о том, в каких случаях выгоднее определять частичную абстракцию. Частичные процедуры не так безопасны, как общие, поскольку они требуют от пользователя выполнения требований, заданных в предложении requires. Если эти требования не удовлетворены, то поведение процедуры становится неопределенным, что может привести к неверной работе программы. Например, при задании для процедуры search неупорядоченного массива она может воз­вратить неверный индекс. Эта ошибка может оставаться необна­руженной долгое время после возврата из процедуры search. Вследствие этого причина ошибки будет неясна, а объекты дан­ных могут быть разрушены.

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

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

Другой случай предполагает использование некоторых про­цедур в ограниченном контексте. Такой была ситуация, когда процедуры merge и merge_sort вызывались только программой sort, использующей метод сортировки слиянием. В ограничен­ном контексте легко обеспечить выполнение необходимых требо­ваний. Например, два массива,, передаваемые процедуре merge, только что были отсортированы. Следовательно, мы имеем без­опасное поведение, не жертвуя при этом эффективностью.

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

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