Текст
                    К. Зиглер
/МЕТОДЫ
ПРОЕКТИРОВАНИЯ
ПРОГРАММНЫХ
СИСТЕМ


Programming System Methodologies Carol A. Ziegler University of Arkansas at Little Rock Prentice-Hall, Inc., Englewood Cliffs, N. J. 1983
К. Зиглер /ИЕТОЛЫ ПРОЕКТИРОВАНИЯ ПРОГРАММНЫХ СИСТЕМ Перевод с английского канд. техн, наук М. В. Сергиевского, канд. техн, наук А. В. Шалашова, А. В. Чукашова и А. Е. Кондратьева под редакцией д-ра техн, наук Я. А. Хетагурова Москва «Мир» 1985
ББК 32.973 3-59 УДК 681.142.2 Зиглер К. 3-59 Методы проектирования программных систем: Пер. е англ. — М.: Мир, 1985. — 328 с., ил. В книге американского специалиста по обработке данных анализируется про- цесс проектирования и рассматриваются методы проектирования программных си- стем. Большое внимание уделено этапу тестирования и оптимизации программ,, написанных на языке высокого уровня. Для системных программистов, инженеров-математиков и системотехников в области САПР. 2405000000—268 3 041(01)—85 163—85, Ч. 1 ББК 32.973 6Ф7.3 Редакция литературы по информатике и электронике Кэролл Зиглер МЕТОДЫ ПРОЕКТИРОВАНИЯ ПРОГРАММНЫХ СИСТЕМ Старший научный редактор И. М. Андреева. Младший научный редактор Н. И. Сивилева. Художественный редактор Н. М. Иванов. Технический редактор Л. П. Ермакова. Корректор В. И. Постнова. ИБ № 5309 Сдано в набор 25.09.84. Подписано к печати 14.02 85. Формат бОХЭО’/ю. Бумага типограф- ская № 2. Печать высокая. Гарнитура литературная Объем 10,25 бум. л. Усл. печ. л. 20,50. Усл. кр.-отт. 20,'50. Уч.-изд. л. 20,56. Изд. № 20/3468. Тираж 25 000 экз. Зак. 399. Цена 1 р. 80 к. ИЗДАТЕЛЬСТВО «МИР». 129820, ГСП, Москва, И-110, 1-й Рижский пер., 2. Московская типография № 11 Союзполиграфпрома при Государственном комитете СССР по делам издательств, полиграфии и книжной торговли. Москва, 113105, Нагатинская ул., д. 1. © 1983 by Prentice Hall, Inc. © Перевод на русский язык, ?Мир», 1985
ПРЕДИСЛОВИЕ РЕДАКТОРА ПЕРЕВОДА «Еще одно руководство по проектированию программных систем»,— может подумать читатель. С одной стороны, это дей- ствительно так. Только в издательстве «Мир» в последние годы вышло несколько книг, посвященных различным вопросам раз- работки программных систем: структурному программированию, верификации и тестированию программ. Среди них следует вы- делить несколько очень удачных1). Но дело в том, что данная монография принципиально отличается от всего того, что было написано до сих пор. В ней излагаются основы методологии проектирования программ, приводится максимально широкий спектр существующих методов и инструментов проектирования, причем охватываются все этапы создания прикладных прог- раммных систем, начиная с системного обследования и кончая тестированием и оптимизацией. Впервые совместно анализиру- ются три наиболее распространенных подхода к разработке программного обеспечения: проектирование сверху вниз, снизу вверх и расширение ядра. Хотя выбор метода зависит в основ- ном от характеристик проектируемой системы, показано, как можно на разных этапах использовать различные методы. Большой интерес, без сомнения, представляет заключитель- ная глава книги, где речь идет об оптимизации готовых прог- рамм. Автор подчеркивает, что оптимизация возможна на уров- не алгоритмов, программных секций и отдельных операторов, и показывает, в каких случаях и на каком уровне ее целесообраз- но проводить. Причем конкретные методы оптимизации иллюст- рируются большим количеством удачных примеров. Выделим еще один важный момент, на котором следует ос- тановиться,— это подготовка системной документации. К сожа- лению, в нашей стране этой важной проблеме до сих пор уде- ляется мало внимания. Например, пакеты прикладных программ сдаются в таком виде, что их эксплуатация и сопровождение чрезвычайно трудоемки. Поэтому предлагаемые практически во 1> Хьюз Дж., Мичтом Дж., Структурный подход к программированию.— М.: Мир, 1980. Тассел Д. ван, Стиль, разработка, эффективность, отладка и испытание программ. — М.: Мир, 1981.
6 Предисловие редактора перевода всех главах книги рекомендации относительно того, что и в ка- кой форме включать в документацию, имеют большое значение. В то же время хотелось бы отметить, что в книге встречают- ся нестрогие определения. Их особенно много в гл. 5 (понятие вычислимости, определение детерминированных и недетермини- рованных алгоритмов). Кроме того, автор использует большое число синонимов, т. е. одному и тому же понятию очень часто соответствует несколько терминов. Переводчикам, на мой взгляд, удалось устранить указанные выше недостатки. Не вызывает сомнений, что книга будет полезна широкому кругу специалистов, занимающихся обработкой данных. Особен- но важен ее материал для системных и прикладных програм- мистов; найдут книгу интересной также те, кто занимается си- стемным анализом. Хочется предупредить, что для чтения кни- ги потребуется знание по крайней мере одного языка программирования высокого уровня. Перевод книги выполнен канд. техн, наук А. В. Шалашовым (предисловие, гл. 1, 2, список терминов), канд. техн, наук М. В. Сергиевским (гл. 3, 8, 9), А. В. Чукашовым (гл. 4, 5, 6) и А. Е. Кондратьевым (гл. 7). Г_ Д-Р техн, наук, профессор Я. До Хетагуров
ПРЕДИСЛОВИЕ АВТОРА На протяжении нескольких последних лет в области прог- раммирования основное внимание было сосредоточено на вы- работке единого, стандартного подхода к проектированию и реализации программ для ЭВМ. В это же время в штатных расписаниях вычислительных центров стала появляться новая должность, получившая название «системный аналитик». Ее введение отражало объективную потребность в разделении функций между проектировщиками и программистами, подоб- но тому как ранее возникла необходимость отделить програм- мистов от операторов, занимавшихся эксплуатацией программ. В курсах программирования вопросам проектирования и стандартизации редко отводится значительное место, несмотря на их очевидную важность для создания эффективных прог- рамм, снабженных исчерпывающей документацией. В процессе первоначального обучения языкам программирования препода- ватели иногда знакомят студентов с принципами анализа и проектирования' применительно к назначению и присваиванию переменных. Однако чаще получается так, что студентам, изу- чающим языки программирования, приходится тем или иным способом самим приобретать навык составления небольших программ. До более сложных и объемистых программ дело, как правило, не доходит. Изложение методов проектирования на уровне вводного курса в основном сводится либо к разъяснению самой идеи программы'как совокупности подробных инструю ций, определяющих последовательность операций, выполняемых над данными, либо к обоснованию роли планирования в про- цессе проектирования. На этом уровне преподаватели вынужде- ны ограничиваться простейшими примерами и иллюстративны- ми материалами. После того как основные конструкции и особенности языка освоены, появляется возможность сосредоточиться на совершен- ствовании техники и стиля программирования. Целью данной книги является повышение уровня знаний студентов и других лиц, достаточно свободно владеющих одним или несколькими языками программирования, в области проектирования и реали- зации программ. При этом предполагается, что читатели хоро- шо знакомы с разнообразными типами информационных и уп-
s Предисловие автора равляющих структур: и таких, которые применяются в наибо- лее распространенных языках высокого уровня, и таких, как деревья и стеки, которые стали базовыми элементами многих разделов теории вычислительных машин. Эта книга в первую очередь ориентирована на студентов вы- числительных специальностей, а не на студентов-экономистов или специалистов, занимающихся прикладным программирова- нием. Тем не менее многие из приведенных в ней примеров за- имствованы из приложений, связанных с коммерческими опера- циями, а не с разработкой программного обеспечения ЭВМ или решением научно-технических задач. Такой выбор объясняется тем, что задачи коммерческого характера просты для восприя- тия и не требуют значительной математической подготовки. В то же время при их программной реализации возникают не менее сложные проблемы, чем в других прикладных задачах. Кроме того, необходимо учитывать, что в США бизнес и ком- мерция— это те сферы, в которых студент-выпускник сможет с большей вероятностью, чем в других, найти себе работу, хотя бы временную. В других примерах рассматриваются игровые и более традиционные задачи из области вычислительной техники. Данная книга рассчитана не на системных аналитиков, а на программистов, которые хотели бы отчетливее определить свое место в рамках процесса проектирования и разработки прог- рамм. В ней сделана попытка дать общий обзор этого процесса и представить некоторые из применяемых в данной сфере мето- дов, получивших широкое признание. Особое внимание в книге уделено таким специальным проблемам, как использование файлов, верификация данных, обработка исключительных со- стояний и устранение вычислительных ошибок. В первых трех главах рассматриваются наиболее общие проблемы, с которыми сталкиваются проектировщики прог- раммной системы, например: что должна делать система? ка- кие ресурсы для нее необходимы? В следующих четырех главах обсуждаются некоторые детали построения систем: какие тре- бования предъявляются к программному обеспечению ЭВМ? каким образом должно осуществляться управление информа- ционными потоками и процессами переработки данных? как должны выполняться вычисления? Последние две главы посвя- щены вопросам качества функционирования систем: какую точ- ность результатов обеспечивают системы и сколь эффективно их применение? Структуру книги можно представить также по-иному, рас- сматривая ее содержание как последовательное описание эта- пов создания программной системы. В гл. 1 обсуждаются спо- собы формализации прикладной задачи. Гл. 2—4 посвящены разработке программ и данных, гл. 5—7 — их реализации, гл. 8 и 9 — испытаниям и оптимизации программ. В книге отдельно
Предисловие автора 9 не исследуется завершающая и наиболее продолжительная фа- за существования системы в качестве готового коммерческого продукта, оснащенного средствами сопровождения. Однако при- менение этих средств с целью внесения тех или иных усовер- шенствований включает этапы разработки, испытаний и опти- мизации, о которых идет речь в книге. По всей книге отмечает- ся важность тщательной подготовки системной документации. Итак, предлагаемая книга призвана заполнить пробел меж- ду изучением языков программирования и использованием этих языков в качестве инструмента при создании систем для реше- ния конкретных прикладных задач. Некоторые части книги тре- буют от читателя определенной математической подготовки, при чтении других он должен иметь представление о внутренних механизмах действия отдельных операторов языка программи- рования. Поскольку книга рассчитана на программистов, вла- деющих различными языками, в примерах использованы Бэй- сик, Фортран, Кобол и ПЛ/1, а в качестве целевого языка — псевдокод, подобный применяемому в языке Паскаль. Как учебное пособие книгу можно рекомендовать студентам младших курсов, знакомых с несколькими языками программи- рования и прослушавших вводный курс по структурам данных. Кэрол А. Зиглер
Глава 1 ВВЕДЕНИЕ Наиболее важное свойство хорошей программы состоит в том, что она работает, причем не просто делает что-то, но имен- но то, что было задумано. Иными словами, она дает правильные результаты в ответ на правильные исходные данные. Не менее важно и то, что хорошая программа сама способна отличать верные данные от неверных. Благодаря этому она не станет вы- давать внешне вполне правдоподобные результаты, если исход- ные данные на самом деле неправильны. Качество программного обеспечения играет важную роль, поскольку большинство программ и программных систем, раз- рабатываемых с целью сбыта (например, компиляторы, тексто- вые редакторы, программы сбора учетной информации, пакеты программ статистической обработки), рассчитаны на длительное использование. Как правило, программное обеспечение оказы- вается более долговечным, чем аппаратное. Поэтому почти на каждой вычислительной установке можно найти программы, написанные еще для ЭВМ предыдущих поколений. Создание хорошей программы является весьма трудоемкой задачей, и предприятия вынуждены вкладывать большие сред- ства в разработку прикладного программного обеспечения. С учетом того, что требования к программам со временем пре- терпевают изменения, модификация уже существующих прог- рамм часто обходится дешевле, чем создание новых. Фактически программы приходится постоянно корректировать, поскольку время от времени в них обнаруживаются ранее не замеченные ошибки. Кроме того, изменяется также операционная среда, в которой они функционируют. Практически не встретишь боль- шую программу, не содержащую никаких ошибок, или оконча- тельно сложившуюся версию программы. Необходимость модификации может быть вызвана сменой оборудования. Если корректировка программ требует времени, новые устройства целесообразно эксплуатировать в режиме эму- ляции, при котором имитируется работа прежних устройств, благодаря чему могут использоваться старые варианты прог- рамм. Модификация программ может потребоваться также в тех случаях, когда вносятся изменения в программное обеспе- чение ЭВМ. В частности, несмотря на все усилия, направленные
Введение 11 на стандартизацию языковых средств, до сих пор трудно найти два компилятора, которые работали бы с абсолютно одинако- выми версиями языка или давали бы идентичные результаты при обработке одних и тех же конструкций. По мере того как внедряются новые типы ЭВМ, как разрабатываются более со- вершенные подходы к решению прикладных задач и созданию языков программирования, эволюционируют и традиционные, общепризнанные языки. Так, вслед за Фортраном I появился Фортран II, затем Фортран IV и наконец Фортран 77. Обычно различные версии языка совместимы по принципу «снизу вверх», однако и в этих условиях в программы почти всегда приходится вносить небольшие коррективы. Всякий раз, когда вводится очередной стандарт на синтаксис языка Кобол, список резервируемых идентификаторов пополняется новыми именами. В частности, такие имена данных, как TIME-OF-DAY (время суток) или SUM (сумма), которые программисты ранее могли употреблять по своему усмотрению, в позднейших версиях бы- ли включены в число зарезервированных слов. Модификация программ может быть вызвана и другими причинами, в том числе изменениями в обработке учетной ин- формации, появлением более эффективных алгоритмов, перехо- дом на новые стандарты. По некоторым оценкам, около 80%’ рабочего времени программистов-профессионалов уходит не на разработку программ, а на их модификацию. Таким образом, многие программы должны быть рассчита- ны на длительную эксплуатацию, на протяжении которой они могут неоднократно модифицироваться. Учитывая это, понятие «хорошая программа» следует трактовать в более широком смысле, чтобы оно включало и такие качества, которые способ- ствовали бы увеличению срока службы программ и их адапта- ции к изменяющимся условиям применения. 1.1. Качество программных систем Каждая программа, входящая в систему, должна отвечать таким требованиям, как правильность, точность, совместимость, надежность, универсальность, защищенность, полезность, эф- фективность, проверяемость и адаптируемость. Будем говорить, что программа является • правильной, если она функционирует в соответствии с техни- ческим заданием. Подразумевается, что техническое задание составлено в четкой форме, позволяющей однозначно судить о том, действительно ли программа отвечает перечисленным в нем требованиям; • точной, если выдаваемые ею числовые данные имеют допу-
12 Глава 1 стимые отклонения от аналогичных результатов, полученных с помощью идеальных математических зависимостей; • совместимой, если она работает должным образом не только автономно, но и как составная часть всей программной системы, осуществляющей обработку информации; • надежной, если она при всех условиях обеспечивает полную повторяемость результатов? Любой человек, имеющий опыт ра- боты с ЭВМ, может подтвердить, что в его практике еще не встречалось ни безукоризненно работающих машин, ни абсо- лютно надежного системного программного обеспечения. И, не- смотря на оптимистичность высказываний некоторых програм- мистов, то же самое можно сказать о прикладных программных системах. Впрочем, уровень их надежности может быть повы- шен за счет использования встроенных механизмов резервиро- вания и самоконтроля; • универсальной, если она правильно работает при любых до- пустимых вариантах исходных данных. В ходе разработки прог- рамм должны предусматриваться специальные средства защи- ты от ввода неправильных данных, обеспечивающие целостность системы; • защищенной, если она сохраняет работоспособность при воз- никновении сбоев. Это качество особенно важно для программ, предназначенных для решения задач в режиме реального вре- мени. В подобных приложениях отказ оборудования может пов- лечь катастрофические последствия — например, аварию ракеты или ядерного реактора. .Указанным свойством должны также обладать программы с большим временем выполнения, осуще- ствляющие обработку постоянно хранимых файлов; • полезной, если задача, которую она решает, представляет практическую ценность; • эффективной, если объем требуемых для ее работы ресурсов ЭВМ не превышает допустимого предела; • проверяемой, если ее качества могут быть продемонстриро- ваны на практике. Здесь подразумевается возможность провер- ки таких свойств программы, как правильность и универсаль- ность. Можно применить формальные математические методы, позволяющие установить, действительно ли программа удовлет- воряет техническим условиям и выдает достаточно точные ре- зультаты. Однако существуют и неформальные способы оценки качества программ, причем иной раз они оказываются более убедительными, чем формальные. Имеются в виду такие не- формальные приемы, как прогоны с остановами в контрольных точках, обсуждения результатов с заинтересованными пользо- вателями и др.; • адаптируемой, если она допускает быструю модификацию с целью приспособления к изменяющимся условиям функциони- рования. Адаптируемость в значительной степени зависит от
Введение 13 конструкции программы, от того, насколько квалифицированно юна составлена и полно снабжена документацией. В какой степени удовлетворяются перечисленные требова- ния, характеризующие качество программ, определяется знания- ми и мастерством разработчиков и программистов. Хотя прог- раммисты часто пишут небольшие процедуры для своих собст- венных нужд, все же большинство программ составляется для тех пользователей, которые не обладают должными навыками программирования. На любой вычислительной установке (кро- ме самых маломощных) пользователь, прикладной програм- мист, оператор ЭВМ, техник, отвечающий за ввод данных, си- стемный программист — это разные лица, и качество программ сказывается на деятельности каждого из них. Часто программы создаются коллективами программистов. Некоторые из них занимаются в основном разработкой, некото- рые — самим программированием, остальные — составлением документации и испытаниями программ. Если разработка алго- ритмов и программирование выполняются разными группами специалистов, за подготовку документации отвечают все группы. Машинные программы — это своего рода рабочие инстру- менты, и пользователь нуждается в определенных инструкциях по их применению и правильному обращению с ними. Подобные инструкции должны содержаться в документации, поставляемой вместе с программой. Документация — столь же обязательный продукт процесса программирования, сколь и сама программа. Если программа взаимодействует с ЭВМ непосредственно, связь се с людьми обеспечивается в основном с помощью докумен- тации. 1.1.1. Среда пользователей Программы редко применяются как самостоятельные едини- цы. Чаще всего они являются элементами более крупных ин- формационных систем, осуществляющих сбор, хранение и об- работку данных. Такие системы становятся неотъемлемой ча- стью механизма функционирования предприятий, на которых они эксплуатируются. Прямо или косвенно, они затрагивают деятельность множества людей. Чтобы это воздействие носило положительный характер, программы не просто должны быть полезными, надежными и эффективными, но должны явно об- наруживать эти качества для тех, кто с ними работает. Иными словами, как процесс проектирования программной системы, так и его конечный продукт должны быть ориентированы на нужды пользователей. Пользователи будут уверены в эффективности системы, если почувствуют, что в группе, занимающейся ее разработкой, при- слушиваются к их пожеланиям, если найдут, что форма вход-
14 Глава 1 ных данных и результатов удобна и близка к привычной, и, на- конец, если им будет продемонстрировано, что система долж- ным образом перерабатывает информацию, отобранную по их собственному усмотрению. Если пользователи принимают уча- стие в проекте на стадии разработки,.они лучше осведомлены о характеристиках системы и могут внести свою лепту в фор- мирование ее окончательного облика. Если же пользователи привлекаются на этапе испытаний, они получают возможность оценить качества системы еще до начала эксплуатации. Распо- лагая квалифицированно составленной, исчерпывающей доку- ментацией, пользователи смогут быть уверены, что система бу- дет работать с полной отдачей. ВОВЛЕКАЙТЕ ПОЛЬЗОВАТЕЛЕЙ В ПРОЦЕСС ПРОЕКТИРОВАНИЯ СИСТЕМЫ Пользователями программной системы могут быть служащие административных учреждений, для которых на ЭВМ подготав- ливаются отчеты и ведомости, или инженеры, выполняющие на машинах научно-технические расчеты. В число пользователей входят также работники из вспомогательного персонала, отве- чающие за ввод информации и заполнение бланков входных форм или контролирующие правильность и точность данных,, выдаваемых ЭВМ. Все лица, на том или ином этапе принимаю- щие участие в процессе обработки инфорк^ации, от ее сбора до> оформления результатов, еще на стадии проектирования систе- мы должны быть ознакомлены с теми ее особенностями, кото- рые должны приниматься в расчет в их практической деятель- ности. 1.1.2. Среда ЭВМ Программа неотделима от вычислительной среды, с которой взаимодействует. Она использует системные программные сред- ства, а те в свою очередь могут пользоваться ее информацией. Программа либо сама создает файлы, либо обрабатывает фай- лы, сформированные другими программами. Она должна быть построена таким образом, чтобы могла применяться в различ- ных приложениях и обходиться только имеющимися аппарат- ными ресурсами и средствами программирования. Процесс раз- работки программы в значительной степени зависит от наличия спёциализированных языков проектирования, каталогов данных, оптимизирующих компиляторов, генераторов тестовых задач. Существенно проще создать хорошую программу, располагая эффективными вспомогательными средствами. МАКСИМАЛЬНО ИСПОЛЬЗУЙТЕ СЕРВИСНЫЕ СРЕДСТВА АВТОМАТИЗАЦИИ ПРОЕКТИРОВАНИЯ
Введение 15 1.1.3. Среда заказчиков Обычно работа по составлению программ начинается в свя- зи с тем, что некоторая организация предлагает создать для нее программную прикладную систему. Официальному заклю- чению договора обычно предшествует выяснение реальной необ- ходимости в такой системе, оценка возможности ее разработки и примерного объема затрат, а также ожидаемого эффекта от ее внедрения. Несмотря на то что ЭВМ можно спроектировать и запрограммировать так, что она будет способна решить лю- бую задачу, которую пользователь сможет описать в виде фор- мальных правил, определяющих последовательность действий, применение вычислительных машин далеко не всегда оправдано. ЭВМ лучше, чем человек, справляется с трудоемкими задача- ми, требующими многократного повторения однотипных опера- ций, значительно быстрее и точнее выполняет арифметические вычисления. ЭВМ более эффективно осуществляет поиск и об- работку больших массивов информации, состоящих из однород- ных элементов. В то же время человек лучше, чем машина, мо- жет разобраться в том, что и как следует делать, и способен работать с неоднородной информацией. Всякое использование ЭВМ предполагает стандартизацию данных и способов обра- ботки. Эффективная реализация преимуществ ЭВМ возможна лишь в тех случаях, когда необходимо выполнять либо трудо- емкие вычисления, либо обработку больших объемов инфор- мации. После завершения этапа предварительных исследований со- ставляется список требований, предъявляемых к системе. В не- го должны быть включены результаты анализа обстановки, опи- сание выполняемых системой функций и ограничения, которые необходимо учитывать в процессе проектирования. Под обста- новкой в данном случае понимается совокупность условий, при которых предполагается эксплуатировать систему. К ним отно- сятся аппаратные и программные ресурсы, предоставляемые си- стеме, внешние условия ее функционирования, а также состав людей и работ, имеющих к ней отношение. Должны быть про- думаны изменения в текущей деятельности организации, об- условленные внедрением системы. Возможно, понадобится иная расстановка персонала, придется внести изменения в структуру выполняемых работ. Могут также потребоваться дополнитель- ные вычислительные мощности. Функциональные требования к системе содержат четкое опи- сание всего того, что она должна делать. Ограничениями в про- цессе проектирования являются директивные сроки завершения отдельных этапов, имеющиеся в наличии ресурсы, организацион- ные процедуры и мероприятия, обеспечивающие сохранность информации.
16 Глава 1 НЕОБХОДИМО ВСЕСТОРОННЕ АНАЛИЗИРОВАТЬ ЭФФЕКТЫ, СВЯЗАННЫЕ С ВНЕДРЕНИЕМ СИСТЕМЫ Организация-заказчик и группа разработчиков совместно со- ставляют официальный перечень спецификаций, а также дого- вор о порядке проведения проектных работ и приемке системы. Иногда процесс создания системы разбивается на два отдель- ных этапа, в которых участвуют различные группы специали- стов. Первая из них занимается собственно проектированием системы, вторая — ее программной реализацией. В таких случа- ях договоры заключаются с обеими группами, причем между указанными этапами должен быть предусмотрен определенный промежуток, выделяемый для анализа и обсуждения характери- стик системы. Подобный подход к проектированию можно проиллюстриро- вать на примере разработки языка Ада. В начале 70-х годов министерство обороны США объявило о создании нового языка, которым предполагалось заменить другие языки программиро- вания во всех приложениях, связанных с решением задач воен- ного характера. Еще до составления окончательного перечня функциональных требований и спецификаций было разработано несколько версий языка, которые анализировались и оценива- лись группой сторонних экспертов. Был объявлен конкурс на создание языка. Его победителем стала французская фирма Honeywell-Bull. После того как работа над языком была за- вершена, для его оценки вновь были приглашены сторонние эксперты. Много различных групп приняло участие в экспери- ментах по практической реализации некоторых наиболее нетра- диционных особенностей языка. Наконец, когда стало ясно, что Ада в целом отвечает предъявляемым требованиям, различные военные ведомства начали заключать контракты на приобрете- ние компиляторов языка. 1.2. Постановка задачи Первый шаг в проектировании прикладной программной си- стемы заключается в точном формулировании целей внедрения машинной обработки. Требования к системе еще не создают полной картины. В постановке задачи, разумеется, должны при- нять участие как представители организации-заказчика, так и те, кто будет заниматься проектированием системы. Необходи- мо, чтобы этот процесс был гибко организован и продолжался в течение достаточно длительного времени, поскольку на любом этапе разработки или внедрения могут вскрываться ранее не предусмотренные трудности, требующие внесения определенных изменений в проект. В то же время с целью упрощения разра- ботки в договор следует включить пункт, который запрещал бы
Введение 17 радикальный пересмотр требований на стадии реализации си- стемы. В этом же пункте могут быть оговорены условия внесе- ния несущественных изменений. ВСЕ ДОГОВОРЕННОСТИ должны ОФОРМЛЯТЬСЯ В ОФИЦИАЛЬНОМ ПОРЯДКЕ 1.2.1. Адаптация процедур ручной обработки Задача может заключаться либо в использовании ЭВМ для выполнения тех операций, которые ранее производились вруч- ную, либо во внедрении качественно новых способов обработки. Решение последней задачи требует ответа на вопросы, во мно- гом аналогичные тем, с которыми проектировщики сталкивают- ся при машинной реализации уже сложившихся процедур об- работки. В одних случаях эти ответы могут базироваться на решениях, принятых организацией-заказчиком. В других случа- ях их приходится искать, обсуждая проблемы с лицами, зани- мающимися в настоящее время выполнением указанных про- цедур. Если на ЭВМ предполагается реализовать некую ручную операцию, проектировщик системы обязан досконально выяс- нить, из каких конкретных,действий состоит эта операция. Эффективное решение даже такой простой задачи, как со- ставление на ЭВМ списка клиентов с учетом особенностей взаи- модействия пользователя и машины, должно основываться на принципах, принятых в современной деловой практике. Проек- тировщику системы предстоит получить ответы на ряд вопро- сов, в том числе: какого рода информация содержится в запи- сях о клиентах? кто предоставляет эту информацию и в какой форме? сколь долго предполагается ее хранить? какая файло- вая система будет применяться? имеются ли копии информации в других учреждениях? каким образом будет использоваться данная информация? кто является потребителем информации? как осуществляется корректировка информации? что предпола- гается делать с информацией о клиентах, переставших пода- вать заявки? как поступать с неполной информацией? что де- лать с информацией, в которой имеются ошибки? Полученные ответы будут содержать массу подробностей, которые должны учитываться в процессе работы над системой. В то же время эти ответы дают более полное представление о внутреннем механизме процедур обработки данных в организа- ции-заказчике. НЕОБХОДИМО ФИКСИРОВАТЬ ВСЕ, ЧТО СДЕЛАНО НА ТЕКУЩИЙ МОМЕНТ Прикладная система служит своего рода машинной мо- делью, отражающей процессы ручной обработки информации. Однако лишь на первый взгляд ситуация проста, на деле все 2-399 ИЙУЧЯАЯ БИБЛИОТЕК им. Горького МГУ
18 Глава 1 может оказаться значительно сложнее. В принципе можно счи- тать, что имеется всего один набор исходных данных. Допустим, это документы, хранящиеся в канцелярском шкафу. К сожале- нию, очень часто подобные наборы исходных данных содержат повторения, неточности, неясности, несоответствия и не облада- ют требуемой полнотой. Проектирование системы на базе ЭВМ включает такой обязательный элемент, как определение формы, в которой должна быть представлена информация, хранящаяся в шкафу. Кроме того, необходимо установить, какие варианты данных будут использоваться на различных этапах, начиная от сбора исходной информации и кончая выдачей результатов. Очевидно, формы, применяемые на этапах сбора данных, их корректировки или выборки с целью проведения той или иной обработки, могут отличаться друг от друга. Варианты данных должны охватывать все входящие в автоматизированную систе- му и исходящие из нее информационные потоки. Аналогично тому как процессы перемещения данных в ЭВМ имитируют операции с бумагами в учреждении, отдельные прог- раммные модули, работающие с этими данными, служат моде- лями деятельности служащих, обрабатывающих бумаги. Конеч- но, подобное сопоставление справедливо лишь в самых общих чертах. Внутренние механизмы обработки, используемые в ЭВМ, вовсе не обязательно должны воспроизводить методы, приме- няемые конторскими службами. Постановка задачи должна охватывать весь процесс сбора, хранения, использования и окончательного представления дан- ных. В ней должны также учитываться ограничения на время обработки, финансовые затраты и вычислительные ресурсы. Кроме того, в постановке задачи следует предусмотреть воз- можность последующей модернизации системы, например рас- ширения памяти для хранения большего числа записей, для включения в каждую запись дополнительной информации или для внесения изменений в отдельные элементы информации. Оценка будущих потребностей должна осуществляться совмест- но как проектировщиками системы, так и ее потенциальными пользователями, поскольку необходимо принимать компромис- сные решения, касающиеся соотношений между универсально- стью и эффективностью, гибкостью организации данных и стремлением обеспечить текущие потребности. ПЛАНИРУЙТЕ НАПРАВЛЕНИЯ МОДЕРНИЗАЦИИ 1.2.2. Определение основных элементов системы На первом этапе проектирования должны быть определены информационные потоки и взаимодействующие с ними процес- сы. Информационным потоком будем называть информацию, пе-
Введение ремещающуюся от одного узла обработки к другому. Такими узлами обработки могут быть машинные программы или рабо- чие места служащих, отвечающих за выполнение определенных операций. Большие массивы информации могут размещаться а архивах, таких, как канцелярские шкафы или накопители на магнитной ленте. Часть информации должна быть представле- на в форме, воспринимаемой ЭВМ; для остальных данных это не обязательно. Когда возникает потребность в информации, она извлекается из архива, включается в информационный по- ток, перемещаемый либо с помощью электронных средств, ли- бо вручную к узлу обработки, и в конечном итоге возвращается в архив, но уже в другом информационном потоке. В подобной укрупненной линейной модели обработки машинные программы выступают в качестве процессов, воздействующих на поступаю- щие к ним данные, преобразуя их или меняя направление их пе- ремещения. Эти процессы запускаются при появлении данных или возникновении условий, внешних по отношению к системе (например, приближение конца месяца). Для пользователя программы представляют собой черные ящики, присутствие ко- торых проявляется лищь в виде результатов их воздействия на данные. 1.3. Проектирование системы К основным стадиям развития программной системы отно- сятся следующие: постановка задачи, разработка, реализация,, испытания и эксплуатация системы. Эти этапы могут частично перекрываться. Так, формулиро- вание задачи не обязательно должно заканчиваться в тот мо- мент, когда начинается разработка программ и подготовка данных. Почти всегда продолжают возникать те или иные де- тали, требующие согласования с будущими пользователями. Переделка уже законченной системы, связанная с изменением внешних условий, обычно рассматривается как элемент сопро- вождения системы в ходе ее эксплуатации, хотя при этом при- ходится анализировать новые требования и вносить соответст- вующие изменения, осуществлять их реализацию и проводить испытания. Все это может происходить еще до того, как систе- ма будет принята к промышленной эксплуатации. Система должна проходить испытания как на этапах разработки и реа- лизации, так и в уже готовом виде. Вначале испытания сводят- ся к консультациям с будущими пользователями: подобные консультации позволяют четко сформулировать требования к системе. На этапе разработки путем тщательного анализа ре- зультатов решения тестовых задач производится детальная про- верка проектной документации. 2*
20 Глава 1 1.3.1. Структурный анализ Формирование окончательного проекта системы — чрезвы- чайно многообразный процесс, требующий от проектировщиков не только профессионального мастерства, но также выдержки и терпения. Умение работать с людьми для разработчика не ме- нее важно, чем знание вычислительной техники. Если в неавто- матизированных информационных системах допускается воз- никновение всякого рода особых ситуаций, автоматизирован- ная система действует во всех случаях стандартным образом. Поэтому при проектировании большое внимание приходится уделять выявлению и устранению частных деталей, не имеющих прямого отношения к делу, которые могут встретиться как в данных, так и в процедурах обработки, и, что немаловажно, убеждению защитников подобных деталей в том, что они не яв- ляются существенными. По мере того как проблемная ситуация проясняется и опре- деляется, процедуры сбора, хранения и переработки информа- ции, рассматриваемые вначале как нечто единое, постепенно расчленяются на отдельные элементы данных и действия, совер- шаемые над этими данными. Метод исследования, которое на- чинается с общего обзора системы и затем детализируется, приобретая иерархическую структуру со все большим числом уровней, принято называть структурным анализом. Требования к системе и ее. предполагаемые характеристики не могут слу- жить отправной точкой, поскольку помимо общего описания они содержат много ненужных деталей. Их можно рассматривать •скорее как цели и стандарты, к которым следует стремиться на всех стадиях проектирования. Верхний уровень структурного анализа представляет собой функциональное описание системы. Составление функционального описания системы — это обобще- ние всей информации, касающейся целей проекта. СЛЕДУЕТ СТРЕМИТЬСЯ К СОЗДАНИЮ ПОЛНОЙ КАРТИНЫ Расчленение системы на функциональные элементы додчи- няется вполне определенным правилам. Самое общее правило состоит в следующем: необходимо отделять то, что требуется сделать, от того, каким образом это можно сделать. Для иллю- страции в качестве примера рассмотрим, как в административ- ном совете округа ведется учет списков избирателей и наблю- дение за порядком голосования. Одна из задач здесь — регист- рация избирателей. Предположим, что для решения этой зада- чи, а также для ведения списков избирателей разрабатывается автоматизированная система на базе ЭВМ. Отдельные функцио- нальные элементы такой системы должны осуществлять регист- рацию новых избирателей, корректировать регистрационную ин- формацию в тех случаях, когда избиратели меняют место жи-
Введение . 21 Окружное административное управление Избиратель Заявление о регист- рации Админист- ративная схема округа Регистра- ционная форма Окружной отдел | здравоохранения । Скорректи- рованная регистраци- онная ин- формация Админист- ративная схема округа Скорректи- рованная егистраци- онная ин- формация Форма кор- ректировки данных Уведомле- ние о смерти Регистра- ционная форма Список зарегистри- рованных избирате- . лей |Избирательный । участок 1 I I I I I I I I I I I I I I 1 1 I 1 I ___________ I Список зарегистри- рованных избиоате- I Рис. 1.1. Потоки документов при составлении списков избирателей.
22 Глава 1 тельства или когда в округе вводится новое деление на избирательные участки, удалять из списков лиц, лишенных пра- ва голоса, выдавать демографические отчеты, подготавливать списки для избирательных участков. Следующий уровень анали- за может состоять в изучении потока документов, циркулирую- щих при существующей методике работы со списками избирате- лей. Для того чтобы отобразить перемещение бумаг между различными служащими и учреждениями, целесообразно ис- пользовать функциональные схемы, подобные изображенной на рис. 1.1. На ней треугольниками отмечена информация о реги- страции избирателей, которая хранится в административном управлении округа в двух различных формах. Первая форма обеспечивает быстрый доступ к данным, когда необходимо вне- сти изменения в списки избирателей. Вторая содержит списки по всем избирательным участкам, составленные в алфавитном порядке. Служащие административного управления занимаются, в частности, тем, что проверяют, не противоречат ли записи друг другу, учтены ли в обоих наборах данных новые границы избирательных участков, внесены ли в оба набора все измене- ния регистрационной информации. На рис. 1.1 показана лишь часть связей между элементами упомянутых форм и процедурами их обработки. Полный их пе- речень должен быть получен на следующем, более детальном уровне описания задачи. При исследовании процесса регистра- ции избирателей и разработке соответствующей автоматизиро- ванной системы решение всех вопросов, относящихся к ее ма- шинной реализации, можно отложить до последнего, наиболее детального этапа анализа. Схемы, отображающие потоки доку- ментов, помогают лучше понять структуру процесса обработки,., определить состав входной и выходной информации. Для анали- за логических связей между элементами данных и процедурами обработки отдельных фрагментов данных могут применяться схемы других типов. Так или иначе, процесс анализа проблемы исходит из функционального описания системы в целом, затеАм составляются функциональные описания ее отдельных частей,, после чего исследуются информационные потоки и, наконец, оп- ределяется структура данных. 1.3.2. Структурное проектирование Логические связи, существующие между различными эле- ментами данных, составляют основу всего процесса проектиро- вания. Были предложены различные методы построения этого процесса. В одних методах проектирование базируется на си- стемном анализе, согласно которому на первом этапе составля- ется общий обзор функционирования системы и перерабатывае- мых в ней данных, после чего система постепенно расчленяется
Введение 23 на все более мелкие составные части. Другие методы исходят из базовых элементов какого-то одного класса, будь то данные или процедуры обработки, и целиком строятся на их основе. В любом случае центральную роль играет исследование струк- туры и взаимосвязей, а не частные вопросы машинной реализа- ции. Начинается ли процесс проектирования с общего обзора системы или с анализа взаимодействия ее компонентов, он ве- дется на полуабстрактном уровне. Система проще адаптируется к внешним условиям, если ее структура в минимальной степени зависит от конкретного выбора технических средств или прог- раммного обеспечения. Более того, очень часто выбор тех или иных способов программной реализации может быть осу- ществлен jia достаточно поздних стадиях проектирования. ПРОЦЕСС ПРОЕКТИРОВАНИЯ ДОЛЖЕН БЫТЬ СТРУКТУРИРОВАН Проектирование становится более целенаправленным, если в его основе лежат зависимости между данными, присущие реша- емой проблеме, а не условия, диктуемые вычислительной сре- дой. Функциональные связи между программами могут быть определены еще до того, как начнется разработка соответству- ющих алгоритмов. На этапе проектирования вопросы реализа- ции решаются на абстрактном уровне с использованием диаг- рамм, таблиц, структурных схем и псевдокодов. Эта информа- ция обеспечивает возможность первоначальной проверки систе- мы. Если проект системы или программы разработан на доста- точно детальном уровне, допускающем моделирование основных процессов обработки данных, количество ошибок, возникающих на стадии реализации, резко снижается. Ошибки на этой стадии обходятся весьма дорого, поскольку к этому времени в проект вложено слишком много усилий. Гораздо проще вносить кор- рективы* в проект на этапе разработки, чем вновь возвращаться к нему уже после его завершения. Любая прикладная система, подобно рассмотренной выше автоматизированной системе регистрации избирателей, включа- ет не только программы, но также все данные и архивы дан- ных. Этапы исследования и разработки системы могут считать- ся завершенными лишь после того, как определены все перечис- ленные компоненты, вплоть до структуры отдельных информа- ционных единиц и наиболее элементарных процедур обработки. Нельзя говорить о завершенности проекта до тех пор, пока не составлено подробное описание взаимодействия различных ча- стей системы, не подготовлены тестовые задачи и не проведена предварительная проверка, на основании которой делается вы- вод о том, насколько система отвечает требованиям техническо- го задания.
24 Глава 1 1.3.3. Реализация и испытания Разработка программы и ее написание — это процессы, про- текающие при ограничениях, принципиально отличающихся друг от друга. Если в ходе разработки преследуется цель выполнить, требования, предъявляемые пользователем, то при написании, программ в качестве ограничений выступают требования, дикту- емые особенностями аппаратного и программного обеспечения,, а также практикой, сложившейся в данном вычислительном центре. Соотношение между разработкой и реализацией прог- раммы примерно такое же, как между проведением исследова- ния и составлением технического отчета. В идеале исследование должно быть полностью закончено и набросана схема отчета,, прежде чем можно будет приступить к его написанию. На прак- тике, разумеется, все это далеко не всегда осуществимо. Как правило, приходится неоднократно вносить изменения и возвра- щаться к началу процесса. Конечно, эффективное планирование облегчает последующую реализацию, но тем не менее всегда следует проявлять достаточную гибкость, чтобы проблемы, воз- никающие на этапе реализации, не приводили к коренному пересмотру проекта. ЗАРАНЕЕ ПЛАНИРУЙТЕ ПРОЦЕСС ПРОГРАММНОЙ РЕАЛИЗАЦИИ На этапе реализации кодирование модулей не вызывает осо- бых затруднений, если проект продуман достаточно тщательно. По мере написания программ модули испытываются сначала по отдельности, а затем во взаимодействии. РЕАЛИЗАЦИЯ ДОЛЖНА ПРОВОДИТЬСЯ ПО МОДУЛЬНОМУ ПРИНЦИПУ Подобно проектированию, процесс реализации необходимо структурировать. Для этого разработанную систему следует разделить на отдельные части, объединенные либо горизонталь- ными, либо вертикальными связями. Наиболее важные с точки зрения их функций модули следует программировать в первую очередь. В результате будет образована многоуровневая иерар- хия модулей и составлен сетевой график реализации, включаю- щий промежуточные этапы проверки взаимодействия модулей. В конечном итоге вся система в целом будет испытана и го- това к внедрению в промышленную эксплуатацию. На этой ста- дии организация, отвечающая за внедрение, должна продемон- стрировать будущим пользователям, что система функциониру- ет согласно требованиям технического задания. Однако до сих пор не встречалось еще системы, не содержащей никаких ошр- бок, а если таковая система и была, то никто не смог бы пору- читься за их отсутствие. Нетрудно доказать, что ошибки имеют-
Введение 25 <ся, но доказать, что их нет, практически невозможно. Для того чтобы выявить и исправить максимально большее число оши- бок, испытания программной системы должны планироваться -гак же тщательно, как и процесс ее реализации. ЗАРАНЕЕ ПЛАНИРУЙТЕ ИСПЫТАНИЯ СИСТЕМЫ 1.4. Вспомогательные средства проектирования С полным основанием можно утверждать, что ключом к по- вышению эффективности процесса программирования является планирование. Были разработаны различные вспомогательные средства, позволяющие упростить планирование всех этапов «проектирования. В некоторых вычислительных центрах приме- няются специализированные языки для подготовки технических заданий, которые помогают при разработке программ так же, как языки программирования — при их реализации. Основное назначение подобных языков состоит в том, чтобы информиро- вать проектировщика обо всех характеристиках, которые могут включаться в спецификации. Пока существуют лишь экспери- ментальные варианты таких языков, и широкого распростране- ния они еще не получили. ИСПОЛЬЗУЙТЕ ИМЕЮЩИЕСЯ ВСПОМОГАТЕЛЬНЫЕ СРЕДСТВА Опубликовано немало книг, в которых даются рекомендации по составлению технических заданий. Обычно эта информация приводится либо в виде графической схемы задания, либо в ви- де развернутого плана проекта, в котором более подробно рас- крывается содержание технического задания. 1.4.1. Графическая схема задания Графическая схема задания представляет собой схему, по- строенную по иерархическому приципу и охватывающую все во- просы, связанные с разработкой проекта. На рис. 1.2 приведен •один из возможных вариантов графической схемы задания на проектирование программной системы. Содержимое схемы по- дробно поясняется в разд. 1.4.2. Графическая схема детализиру- ется в тексте развернутого плана задания, а каждый из входя- щих в нее блоков подробно прорабатывается на более поздних стадиях проектирования. Графические схемы задания и развер- нутые планы проекта должны включаться в состав системной Документации. Каждая графическая схема задания должна сво- бодно умещаться на одной странице. Поэтому рекомендуется строить ее таким образом, чтобы она содержала не более трех основных блоков по горизонтали и пяти-шести связанных с ни- ми блоков по вертикали.
26 Глава 1 Рис. 1.2. Графическая схема задания для проекта системы. ШИРЕ ИСПОЛЬЗУЙТЕ ИЛЛЮСТРАТИВНЫЕ МАТЕРИАЛЫ Схему, приведенную на рис. 1.2, можно рассматривать как общий план проектирования системы. Именно таким представ- ляется процесс проектирования автоматизированной системы из- вне. От системы к системе графическая схема задания меняется незначительно. Основные отличия проявляются в деталях, со- держащихся в развернутом плане проекта. 1.4.2. Развернутый план проекта системы 1. Введение. Дается общая характеристика системы, в доста- точной степени подробная, чтобы будущий пользователь мог принять решение о том, отвечает ли система его требованиям. 1.1. Функции системы. Поясняется назначение прикладной системы, приводится перечень основных процедур и обрабаты- ваемых данных. 1.2. Сфера применения. Характеризуется круг пользователей» на которых ориентирована разрабатываемая система.
Введение 27 1.3. Сбор и корректировка данных. Описываются источники исходных данных, поступающих в систему, а также источники данных, используемых для корректировки. В этот пункт следу- ет включить планы и графики корректирования данных. В даль- нейшем информация используется как руководство при деталь- ной проработке программ корректировки данных. 1.4. Отчеты. Описываются формы, определяются периодич- ность и общее содержание отчетов, выдаваемых системой. Эта информация служит основой для последующей детальной про- работки программ генерации отчетов. 2. Вычислительная среда. Определяется минимальный состав оборудования, необходимого для нормального функционирова- ния системы. 2.1. Технические средства. Описывается конфигурация тех- нических средств, указывается требуемый объем оперативной памяти, определяются ограничения на сегментацию памяти, требования к внешним устройствам и т. д. 2.2. Программные средства. Указываются типы операцион- ных систем, используемые библиотеки стандартных программ, системы управления базами данных и т. д. 2.3. Режимы работы. Определяется возможность функциони- рования системы в условиях пакетного режима, интерактивно- го режима, режима реального времени или их комбинаций. 3. Связь с внешней средой. Описывается взаимодействие пользователей с системой. 3.1. Вход системы. Определяются форматы данных всех ти- пов, вводимых пользователями, а также внутренняя структура данных. Эта информация служит руководством при разработке бланков входных форм и подготовке данных. 3.2. Выход системы. Описываются форматы отчетов, сооб- щений и других выходных форм. Эта информация используется при составлении планов и подготовке данных. 3.3. Управляющие параметры. Перечисляются параметры, задаваемые при настройке системы на конкретную конфигура- цию технических и программных средств. 3.4. Рабочие инструкции. Дается общий обзор содержания инструкций, касающихся обращения с лентами, хранения бума- ги и т. д. Данная информация используется при составлении инструкций для обслуживающего персонала. 4. Качество системы. 4.1. Соблюдение стандартов и общепринятых обозначений. Указывается, в какой мере система соответствует стандартному варианту языка программирования и отвечает стандартам вер- сии, эксплуатируемой в данном вычислительном центре. Кроме того, определяется степень использования общеупотребительных сокращений и математических обозначений. Это позволяет оце- нить трудоемкость сопровождения системы.
28 Глава 1 4.2. Универсальность системы. Обсуждается уровень незави- симости системы от конкретных внешних условий, с учетом ко- торых она разрабатывается. Это характеризует сложность пе- ревода системы на другие вычислительные установки. 4.3. Надежность функционирования. Рассматриваются такие вопросы, как ожидаемое время наработки на отказ, способы корректировки ошибок, проверка достоверности информации, точность результатов, статистические характеристики всех мо- дулей, осуществляющих вероятностные расчеты, например гене- раторов псевдослучайных чисел. 4.4. Защита информации. Описываются средства, обеспечи- вающие сохранность данных и авторизацию доступа, использу- емые способы кодирования. 5. Документация по системе. 5.1. Пособия и руководства. Приводится перечень докумен- тации, прилагаемой к системе,— пособий, форм отчетности, ра- бочих описаний, системной и программной документации. 5.2. Спецификации программ. Дается общее функциональное описание отдельных программ, входящих в состав системы. Эта информация служит руководством при разработке программ. 5.3. Организация данных. Приводится общее описание взаи- модействия отдельных информационных потоков в системе. Эти сведения используются при разработке принципов организации данных. Документы, содержащие общее описание проекта, служат, с одной стороны, своеобразным эталоном, на основании которого осуществляется проверка законченной системы, с другой — ис- пользуются как руководства на этапах разработки, реализации и испытаний. 1.4.3. Организация процесса проектирования Графическая схема задания и развернутый план проекта оп- ределяют те качества, которыми должна обладать система. Они также указывают в общей форме основные направления проек- тирования. Однако эти документы не могут служить планом проектных работ. В процессе разработки и реализации системы решается широкий круг задач, в том числе такие задачи, как составление рабочих спецификаций, составление перечня характеристик, внешнее описание данных, внешнее описание программ, разработка архивов данных, разработка программных модулей, разработка тестовых задач, кодирование программных модулей, проверка программных модулей,
Введение 29» объединение программных модулей, испытание системы в целом, комплектация сопровождающей документации. Поскольку многие из перечисленных задач связаны друг с другом, возникает необходимость в планировании последова- тельности их решения. Для анализа распределения работ часто» применяются так называемые ПЕРТ-диаграммы. Эти диаграм- мы .оказываются полезными и при планировании распределе- Составление внешних описаний данных Подготовка тестовых данных S Рис. 1.3. ПЕРТ-диаграмма процесса проектирования системы. Начало работ Составление внешних описаний Процедур обработки Кодирование программных модулей Разработка архивов данных Испытания Отладка программ- программной ных модулей системы Окончание работ Разработка программных модулей ния ресурсов в прикладных системах. Данный вопрос подробно» обсуждается в гл. 2. ПЕРТ является прямой транскрипцией английского сокра- щения PERT, составленного из первых букв названия «prog- ram evaluation review technique» (методика анализа и коррек- тирования планов). ПЕРТ-диаграмма представляет собой граф» содержащий описания работ и событий и характеризующий про- цесс взаимодействия работ во времени. Ребра графа обознача- ют работы, его вершины — события. Обычно под событием по- нимается завершение одной работы и начало другой, причем вторая не может быть начата, прежде чем завершится первая. На рис. 1.3 приведен сокращенный вариант ПЕРТ-диаграммы_ описывающей процесс проектирования прикладной системы. Поставив в соответствие каждой работе ожидаемое время ее- выполнения, можно определить максимальный по продолжи- тельности путь от начальной до конечной вершины графа, ко- торый называют критическим путем проектирования системы. На этапах разработки и реализации могут применяться раз- нообразные методы организации-проектных работ. Они включа-
«30 Глава 1 ют создание ведущих групп, сквозной коллективный анализ проекта, свободное обсуждение программ. В ведущие группы входят специалисты по программирова- нию и лица из вспомогательного персонала, работающие сов- местно над проектом с самого начала и до окончательного его завершения. Один из них — главный программист — несет от- ветственность за разработку программы и координацию дея- тельности членов группы. Ему также предоставляется право вы- ступать от имени всей группы. Имея коллектив людей, тесно сотрудничающих между собой в течение определенного време- ни, можно более целенаправленно руководить ходом работ и ожидать более качественных результатов. Сквозной анализ проекта предпринимается с целью выявле- ния таких ошибок, как отсутствие спецификаций или неправиль- ное толкование имеющихся спецификаций, и проводится он на той стадии, когда исправление ошибок еще не вызывает особых затруднений. Специалисты, работавшие над индивидуальными заданиями, осуществляют совместный сквозной просмотр про- екта, используя специально подобранные тестовые наборы дан- ных. С помощью подобных просмотров спецификации отдельных блоков системы или описания их взаимодействия проверяются до того, как фактически начнется кодирование соответствующих программных модулей. Свободные обсуждения — это анализ текстов исходных мо- дулей, проводимый всей группой. Поскольку проект представ- ляет собой не механическую сумму результатов, полученных от- дельными программистами, а продукт их коллективной деятель- ности, то такие обсуждения позволяют выявлять логические ошибки, описки и несоответствия в спецификациях. 1.5. Системная документация Первым из документов, которые должны выпускаться в про- цессе проектирования, являются исходные требования к систе- ме, согласованные между будущими пользователями и систем- ным аналитиком. Многие части документации не имеют непо- средственного отношения к программистам. В нее входят инст- рукции для персонала, занимающегося подготовкой входных данных, описания процедур контроля для управляющего пер- сонала, описания тестовых процедур, а также рабочие инструк- ции для операторов. В ходе проектирования выпускается документация двух ти- пов— рабочая (промежуточная) и отчетная (окончательная). При разработке сложных проектов часто приходится выделять специального человека — секретаря проекта, который занимает- ся оформлением документов и сбором рабочей документации. Любой программист вспомнит немало случаев, когда он выбра-
Введение 31 сывал распечатки старых вариантов программы, а впоследст- вии оказывалось, что без них не обойтись. Секретарь проекта как раз и занимается тем, что подшивает все промежуточные распечатки, старые блок-схемы, спецификации, тестовые данные и тому подобные материалы, и хранит до тех пор, пока не за- вершатся окончательные испытания системы. Некоторые из ра- Рис. 1.4. Графическая схема задания на разработку системной документации. бочих документов в дальнейшем должны войти в состав отчет- ной документации. Примерный перечень отчетной документации по системе при- веден на рис. 1.4. Проектная документация служит основным- источником информации, на основании которой осуществляют- ся разработка системы, ее эксплуатация и обслуживание.-Ниже’ дается развернутый перечень системных документов. 1. Требования к системе. Составление требований к системе- не входит в задачу программистов. Они являются специалиста- ми по программированию, а не по гражданскому строительству, банковскому делу, численным методам или в любых других прикладных областях. В то же время составление требований к системе не может быть поручено и одному лишь системному аналитику. Они должны вырабатываться в соответствии с офи- циально оформленным договором между организацией-заказчи-
32 Глава 1 ком и организацией-исполнителем и выражать содержание это- го документа. 2. Проектная документация. Эта часть документации гото- вится системным аналитиком, который руководствуется сведе- ниями, полученными от программистов и будущих пользовате- лей. 2.1. Проект системы. К этим документам относятся графиче- ские схемы задания и развернутые планы проекта, охватываю- щие структуру и основные детали системы. 2.2. Подготовка данных. Этот раздел охватывает все уров- ни организации данных, которые будут использоваться в про- граммах, в том числе справочники данных, описания файлов, таблицы ссылок и т. д. 2.3. Разработка программ. В этот раздел входят описания иерархической структуры программ в системе, потоков инфор- мации между программами и организации взаимодействия про- грамм. Сюда же должна быть включена информация о внутрен- нем содержании программ, например описания алгоритмов, их блок-схемы или программы на псевдокоде. 3. Справочные пособия и руководства. Эти издания подго- тавливаются специалистами по технической документации, ко- торые используют информацию, полученную от системного ана- литика и программистов. 3.1. Руководство пользователя. В руководстве содержатся -общее описание системы и подробные сведения о ее примене- нии, а также разъясняются сообщения об ошибках. 3.2. Руководство по обслуживанию системы. В это руковод- ство включаются отдельные разделы проектной документации и документы по реализации системы, необходимые для глубокого ознакомления с организацией данных и функциями программ. 3.3. Руководство оператора. В руководстве содержатся опи- сания вычислительной установки и конфигурации устройств, а также инструкции по работе с программной системой. 4. Реализация системы. Данная часть документации отража- ет результаты деятельности группы программирования. 4.1. Символьный код. Это набор текстов самодокументиро- ванных программ, составленных в удобной для восприятия форме. 4.2. Информация, выдаваемая ЭВМ. Сюда входят таблицы перекрестных ссылок, карты загрузки, таблицы атрибутов, дан- ные о времени выполнения программ и любая другая машинная информация. 4.3. Тестовые прогоны программ. В эту часть документации включаются контрольные варианты входных данных и соответ- •ствующие результаты, получаемые в различных условиях. Ука- занная информация служит в качестве тестовой при проверке функционирования системы.
Введение 33 ХРАНИТЕ ВСЮ ПРОМЕЖУТОЧНУЮ ДОКУМЕНТАЦИЮ ВПЛОТЬ ДО ЗАВЕРШЕНИЯ РАБОТЫ НАД СИСТЕМОЙ После того как проектирование системы закончено, единст- венное назначение документации — обеспечить максимальную эффективность использования системы. Поскольку к системе имеют отношение различные группы людей, для каждой из них должны быть подготовлены определенные документы. В то вре- мя как для персонала, занимающегося сбором информации и вводом данных в ЭВМ, нужны лишь описания некоторых форм и инструкции по их заполнению, лицам, ответственным за об- служивание системы, необходимо иметь доступ к возможно большей части документов. Для этого персонала в документа- ции должны быть предусмотрены разделы, содержащие кон- спективную информацию, позволяющую быстро разобраться в том, как действует система в целом, или наметить план работ по модернизации системы. Комплект документации строится по модульному принципу, но отдельные ее части в значительной степени перекрывают друг друга. Желательно, чтобы сборник документов предварялся общим обзором их содержания. Целе- сообразно также включать в этот сборник предметный указа- тель и перечень ссылок. 1.6. Упражнения 1. Предположим, вам предстоит взять книгу в абонементе библиотеки. Рассмотрите эту процедуру, руководствуясь следующим перечнем вопросов: а) Сколько различных бланков приходится заполнять? б) Какого рода информация заносится в бланки? в) Кто работает с этими бланками? г) Какая дополнительная информация может понадобиться любому из участников этой процедуры? 2. Предположим, имеются теннисные корты, доступ на которые разрешен только по предварительным заявкам. а) Сколько различных категорий людей принимает участие в оформлении заявки? б) Какого типа информация запрашивается? в) Будут ли чем-нидубь отличаться процедуры оформления заказа, если корты принадлежат закрытому клубу? городскому управлению спортивными сооружениями? 3. Если библиотека (упр. 1) решит автоматизировать выдачу книг на дом, то на какие вопросы о проведении этой процедуры требуется получить ответы разработчику программ? Рассмотрите возможные варианты ответов и спосо- бы их учета при программной реализации. Составьте также подробные описа- ния процедур получения книги в библиотеке. 4. Пусть в помещении библиотеки (упр. 1) имеется один основной стол выдачи книг на дом и два выхода, где проверяют оформление взятых книг. Предложите возможный вариант реализации автоматизированной системы вы- дачи, включающий программные и технические средства. Как будет функцио- нировать система и не нужно ли будет внести какие-то изменения в сущест- вующую процедуру? Желательно, чтобы соображения по этим вопросам были изложены в форме развернутого плана проекта. 3—399
34 Глава 1 5. Если в административном управлении избирательного округа решат создать базу данных, в которую информация о регистрации избирателей могла бы вводиться по мере ее поступления (в режиме реального времени), то каю это отразилось бы на схеме потока документов, проведенной на рис. 1.1? Рас- смотрите изменения, затрагивающие избирателей, окружное административное управление, окружное управление здравоохранения, служащих избирательного участка. 6. Разработайте развернутый внешний план проекта системы оформления предварительных заявок на пользование теннисными кортами (упр. 2), при условии что корты принадлежат закрытому клубу и могут посещаться только его членами. Предусмотрите, чтобы плата за пользование включалась в еже- месячный счет, оплачиваемый членом клуба.
Глава 2 ПРОЕКТИРОВАНИЕ СИСТЕМ Программисты предпочитают мыслить категориями прог- рамм, а не категориями систем. Между тем только в самых про- стых случаях поставленную задачу наиболее успешно удается решить путем разработки одной программы. Например, с по- мощью отдельной программы можно получить численное реше- ние некоторой технической проблемы. Но уже для сопровожде- ния финансовой документации или управления вычислительны- ми ресурсами более эффективными оказываются системы, со- стоящие из нескольких программ. Число программ в системе зависит как от сложности, так и порядка поступления исходных данных. Для проведения вычислений с данными из входного набора простой структуры может быть достаточно и одной программы. Если же данные относятся к нескольким типам или элементы данных поступают из разных источников в различные моменты времени, может потребоваться ряд связанных друг с другом программ. 2.1. Определение основных компонентов системы Простейшая система включает один входной поток данных, один выходной поток данных и одну программу (содержащую, возможно, подпрограммы). Если элементы данных поступают из нескольких источников и обрабатываются не вручную, а с по- мощью ЭВМ, для различных источников должны быть преду- смотрены разные входные потоки. Если, кроме того, конечные результаты предполагается использовать несколькими способа- ми, необходимо иметь соответствующее число отдельных выход- ных потоков. Данные, необходимые в течение достаточно про- должительного времени, обычно отправляются на хранение в машинной форме, воспринимаемой только ЭВМ. Данные, харак- теризующие текущую ситуацию, могут подвергаться корректи- ровке. В частности, должны регулярно обновляться записи, содержащие информацию о деятельности предприятия. Измене- ния вносятся также в исходные программы, отлаживаемые в интерактивном режиме. Данные, получаемые в ходе длительно- го эксперимента, регулярно пополняются новыми записями. Во всех перечисленных случаях система должна объединять по 3»
36 Глава 2 меньшей мере три программы, выполняющие следующие основ- ные функции: а) запоминание данных; б) корректировку данных; в) использование хранящихся данных. Если система состоит из нескольких программ, в ней долж- но циркулировать несколько различных потоков данных. Для каждой программы могут быть определены два потока дан- ных— входной и выходной. Если программа предназначена для корректировки данных, она имеет два входных потока. Первый из них содержит данные, подлежащие корректировке, второй — новую информацию. Если выходной поток направляется не на печатающее устройство или экран дисплея, должен быть пред- усмотрен дополнительный выходной поток, в котором отражался бы процесс выполнения программы, отмечалось бы ее успешное или аварийное завершение, а также фиксировались бы любые отклонения от нормы. Если основной выход содержит только данные, представленные в машинной форме, оператору или пользователю, работающему с системой, должна быть предо- ставлена оперативная информация о ходе программы. Кроме того, программа может выдавать резервные дубликаты данных, результаты контрольных проверок и другую вторичную инфор- мацию в машинном коде. На рис. 2.1 приведена упрощенная структура системы сопро- вождения данных. Процессы, осуществляющие преобразование данных, представлены на ней кружками, а потоки данных, цир- кулирующие между процессами,— стрелками. Каждый процесс реализуется в виде одной или нескольких программ. Длительное хранение данных показано штриховой линией, которая связыва- ет потоки, содержащие два главных списка. Часть других потоков может передаваться на временное хранение. Если из- менения данных фиксируются ЭВМ и подвергаются предвари- тельной обработке с целью их упорядочения и устранения оши- бок, система должна содержать дополнительные программы и потоки данных, кроме перечисленных выше. Структуру, близкую к описанной, имеют многие автоматизированные системы управ- ления и ведения документации. Представленная на рис. 2.1 схема прежде всего отображает порядок прохождения данных через систему. Хотя о прикладных системах обычно принято судить как о наборах программ, сами данные имеют более важное значение, чем компоненты, осуще- ствляющие их переработку. Если программа повреждена, ее можно перезаписать. С восстановлением данных дело обстоит значительно сложнее. По этой причине приходится периодически производить копирование данных, а иногда и хранить записи обо всех проведенных корректировках. Эти операции требуют введения в систему дополнительных выходных потоков и соот-
Изменения Рис. 2.1. Упрощенная структура системы сопровождения данных. Рис. 2.2. Структура усложненной системы сопровождения данных. Рис. 2.3. Структура системы, предназначенной для выполнения двухшаговых заданий.
1 38 Глава 2 ветственно программ, которые их формируют. Структура систе- мы сопровождения данных с учетом указанных дополнений при- ведена на рис. 2.2. Вне среды, приспособленной для его использования, набор данных не представляет никакой ценности, однако в рамках та- кой среды именно набор данных приобретает основное значение. Это утверждение справедливо и по отношению к приложениям, связанным с научными расчетами или с разработкой программ- ного обеспечения ЭВМ. Например, программа может осу- ществлять статистический анализ данных, извлекаемых из раз- личных наборов, выполняя сложные действия, которые невоз- ; можно воспроизвести вручную, однако без данных даже самая | изощренная программа теряет всякий смысл. Очевидно, целесо- образно рассматривать прикладную систему как некоторую со- вокупность данных и операций, определенных на этих данных, , а не просто как набор программ, взаимодействующих между собой посредством обмена данными. Программы — это всего лишь рабочие инструменты, в то время как данные — это своего ' рода исходное сырье, из которого вырабатывается конечный ! продукт. Студенты, которым предстоит выполнить на первый | взгляд элементарную операцию — прогнать на ЭВМ свои прог- раммы, на самом деле сталкиваются с ситуацией столь же ' сложной, что и описанная выше. Действительно, студенческая । программа, написанная на языке высокого уровня — например, на Коболе, Фортране или ПЛ/1,— сдается на машину вместе с ? данными для этой программы. Таким образом, возникают два входных потока, содержащих исходный код и данные. Процесс i обработки состоит по меньшей мере из двух этапов. Сначала обрабатывается исходный код, затем обрабатываются данные. При этом можно выделить несколько выходных потоков: । листинг исходного кода и либо сообщения об ошибках, либо ре- зультаты вычислений (часто то и другое). Хотя все необходи- ; мые действия могут быть выполнены одной программой, напри- i мер интерпретатором, обилие входных и выходных потоков ча- ще всего вынуждает использовать несколько программ ( (рис. 2.3). Стандартные ситуации типа «компилируем много раз — вы- . полняем несколько раз» со студенческими программами или ‘ «компилируем один раз — выполняем много раз» с программа- | ми, составленными профессионалами, становятся возможными потому, что языковые трансляторы вырабатывают объектные j коды. Если же эти коды стандартизованы, исходные коды раз- | личных типов могут транслироваться в единый объектный код и за счет этого одни и те же управляющие программы шага вы- полнения могут применяться при работе не с одним, а с не- I сколькими языками высокого уровня. В частности, благодаря ; тому что трансляция ведется на один машинный язык, можно i
Проектирование систем 39 на одной и той же машине выполнять программы как на Кобо- ле, так и на Фортране. Встречаются также ситуации противопо- ложного характера, когда для работы с одним объектным ко- дом, но на машинах различных типов используются разные наборы управляющих программ шага выполнения, как это дела- ется, например, с P-кодом в языке Паскаль. Рис. 2.4. Структура системы для работы с языками высокого уровня. В большинстве ЭВМ машинный язык является одновременно языком объектных кодов, вырабатываемых любыми языковыми трансляторами. Иногда вместо перевода непосредственно на машинный язык трансляторы выдают промежуточный код на языке Ассемблера. Одно из преимуществ такого подхода состо- ит в том, что программы на языке Ассемблера сравнительно лег- ко поддаются отладке. Применяя различные варианты Ассемб- лера, программу можно выполнять на несколько отличающихся Друг от друга машинах, либо использовать одни ЭВМ в качест- ве эмуляторов ЭВМ других типов. Структура подобной системы приведена на рис. 2.4, где показано также, как объектный код Дополняется стандартными программами, уже имеющимися в системной библиотеке. Возможность такого объединения обеспе-
40 Глава 2 чивается за счет разделения этапов компоновки и выполнения программ. В тех случаях, когда прикладная система формируется из отдельных программ, иногда необходимо проверить работу всех этих программ во взаимодействии. В некоторых языках высоко- го уровня предусмотрены средства объединения отдельных мо- дулей под управлением специальных программ-препроцессоров. Однако и не имея таких средств, можно выполнять программы в рамках единого задания, используя программу, написанную на языке управления заданиями. Процесс проектирования системы включает определение различных потоков данных (входных, промежуточных, выход- ных), формализацию процедур обработки, выполняемых над этими потоками, выбор методов обработки потоков, последова- тельных или параллельных, и, наконец, разработку отдельных программ, способов хранения данных и потоков данных. При использовании мультипрограммных операционных систем необ- ходимо, кроме того, установить, должны ли сами процедуры обработки осуществляться последовательно, или некоторые из них допускают параллельное выполнение. 2.1.1. Определение потоков данных Потоки данных в системе определяются согласно следующим правилам: а) Каждому источнику данных соответствует один входной поток. б) Если имеется совокупность наборов данных, получаемых из нескольких источников, эти наборы распределяются по груп- пам обрабатываемых совместно потоков данных. в) Если не все потоки данных подвергаются обработке одно- временно, процесс обработки делится на этапы, в каждом из ко- торых участвует группа совместно обрабатываемых потоков. Кроме того, должны существовать внутренние потоки данных, связывающие последовательные этапы. г) Для каждого этапа обработки в системе выделяется ос- новной выходной поток, содержащий результаты обработки, и дополнительный поток для выдачи оперативных отчетов, сооб- щений об ошибках и другой вспомогательной информации. ДЛЯ РЕАЛИЗАЦИИ ОТДЕЛЬНЫХ ФУНКЦИИ ЦЕЛЕСООБРАЗНО ИСПОЛЬЗОВАТЬ ОТДЕЛЬНЫЕ ПРОГРАММЫ 2.1.2. Определение процессов После того как определены потоки данных, необходимо оп- ределить процессы, оперирующие этими потоками: а) Если потоки данных обрабатываются порознь, для каждо- го из них требуется отдельный процесс.
Проектирование систем 41 б) Если некоторые системные функции должны выполняться в разное время или чаще, чем другие, они реализуются в виде отдельных процессов. в) Если некоторые из промежуточных потоков данных необ- ходимо сохранять с целью их последующего использования, должны быть предусмотрены: процесс, с помощью которого осу- ществляется их запоминание, процесс для сопровождения таких потоков (если потребуется корректировка) и процесс для поиска и обработки данных. Определив всю совокупность процессов, следует выяснить, нет ли ранее написанных программ, которые могли бы выпол- нять часть необходимых функций. Так, для работы с проекти- руемым компилятором можно использовать уже имеющийся загрузчик, а для работы с прикладной программой подготовки отчетной документации, обрабатывающей отсортированные файлы,— стандартную программу сортировки. Не исключено также, что имеются и некоторые из требуемых входных вели- чин — либо представленные в машинной форме, либо подготов- ленные для ввода с терминала. ИСПОЛЬЗУЙТЕ ВЕСЬ ИМЕЮЩИЙСЯ В РАСПОРЯЖЕНИИ МАТЕРИАЛ 2.1.3. Данные и их носители После того как процессы и потоки данных определены, не- обходимо связать с процессами конкретные программы, а с по- токами данных — конкретные их носители. Желательно, чтобы те и другие были описаны в структурированной форме. Данные в системе могут циркулировать в виде дискретных единиц, та- ких, как записи, либо в виде непрерывного потока, если обра- ботка производится в реальном времени. В качестве источников или приемников данных служат перфокарты, магнитные ленты и диски, терминалы или периферийные устройства других типов. Доступ к данным, хранящимся в ЭВМ, осуществляется либо непосредственно, либо с применением процедур поиска. Данные могут быть структурированы и не структурированы, упорядоче- ны и не упорядочены. В одних случаях программы доступа не- обходимо писать заново, в других случаях можно воспользо- ваться существующими. Данные, хранящиеся на перфокартах или магнитных лентах, обрабатываются только последовательно, что обусловлено фи- зической природой носителей. Данные на перфокартах нельзя использовать повторно в автоматическом режиме, без участия человека. Данные, записанные на ленте, допускают многократ- ное использование одной и той же программой, хотя такой спо- соб обработки неэффективен. Данные, передаваемые на такие
42 Глава 2 устройства, как перфоратор, принтер или накопитель на магнит- ной ленте, также должны обрабатываться последовательно. По- сле того как пересылка данных завершена, их замена или вы- борка весьма затруднительна. Поэтому в тех случаях, когда программа нуждается в многократном доступе к данным, по- следние должны быть отправлены либо на временное, либо на постоянное хранение в устройство массовой памяти. В целом структура потока данных определяется их логической организа- цией и физическими особенностями носителя, на котором они хранятся. Очень важной является проблема планирования доступа к данным. В мультипрограммных системах одни и те же данные одновременно могут принадлежать к нескольким потокам. Если операционная система, а также состав технических средств ЭВМ обеспечивают одновременный доступ к данным со сторо- ны нескольких пользователей, технические требования к при- кладной системе могут содержать определенные ограничения на доступ. Если ЭВМ позволяет в каждый момент времени об- ращаться к данным только одному процессу, может возникнуть необходимость в разработке средств планирования процессов и коллективного доступа к данным. Пользователи, работающие с общими данными, могут применять как одни и те же, так и раз- ные программы. Они могут реализовать различные процессы, например выборочное считывание данных или их корректиров- ку. В тех случаях, когда одновременно нескольким пользовате- лям разрешается выполнять одинаковые операции, они либо должны иметь индивидуальные копии программы и применять их по очереди, либо работать с одной программой, оформленной в виде реентерабельного модуля, в котором предусмотрены от- дельные области данных для каждого пользователя. Вопрос о том, предоставляются ли разным пользователям отдельные ко- пии или одна общая программа, решается прежде всего исходя из возможностей операционной системы ЭВМ, однако он тесно связан также со структурой прикладной системы. 2.2. Методы разработки данных Для демонстрации связей, существующих между отдельными компонентами системы, используются различные графические схемы. Некоторые из них, такие, как граф-диаграммы, отобра- жают главным образом прохождение потоков данных между процессами. Другие, в частности функциональные, схемы выде- ляют моменты, связанные с хранением данных и используемы- ми для этого носителями. Существуют также схемы, в которых основное внимание уделяется взаимодействию процессов.
Проектирование систем 43 2.2.1. Граф-диаграммы Рис. 2.1—2.4, иллюстрирующие структуры прикладных си- стем, представляют собой граф-диаграммы, иногда называемые также графами потоков данных. Каждый кружок на такой диа- грамме отображает некоторое преобразование данных. Потоки данных отмечаются стрелками. Этот тип схем можно использо- вать как на системном уровне для описания внешних входов и выходов программ, так и при проектировании самих программ для описания перемещений данных между отдельными модуля- ми. Физические носители данных на схемах не указываются. На рис. 2.5 приведена граф-диаграмма системы, предназна- ченной для ведения документации в небольших торговых пред- приятиях. В системе применяется терминал на базе микро- ЭВМ, устанавливаемый непосредственно на рабочем месте про- давца. Информация о совершенных продажах сначала заносит- ся во внешнюю память, а затем используется при корректиров- ке записей товарных запасов, записей счетов, предъявляемых к оплате, и файла общей бухгалтерской ведомости, а также при выдаче на печать бланка продаж. Система состоит из набора программ, обрабатывающих входные данные различных типов, и программ, осуществляющих ведение файлов и выдачу отче- тов. Основные программы системы обслуживают файлы товар- ных запасов, общей бухгалтерской ведомости, платежеспособ- ных счетов и счетов, подлежащих оплате. Эти же программы выводят на печать информацию из файлов. Сбор входной ин- формации и выдача данных в виде специальных форм, таких, как бланки продаж и чеки на оплату, производятся отдельны- ми, небольшими по размеру программами. Для пересылки дан- ных между программами используются временные файлы. Си- стема состоит из отдельных, но связанных между собой элемен- тов. Некоторые из них требуют машинной обработки, другие обрабатываются вручную, в зависимости от организации дело- производства. 2.2.2. Диаграммы Варнье—Орра На диаграмме Варнье — Орра в иерархической структуре системы выделяются ее элементарные составные части, которые снабжаются контурными изображениями носителей информа- ции. На рис. 2.6 подобная диаграмма построена для системы сопровождения данных. Сначала система разделяется на ряд отдельных процессов. На следующем уровне иерархии указыва- ются потоки данных для каждого процесса. Затем перечисля- ются наборы данных и наконец — соответствующие носители информации. Последние обозначаются с помощью стандартных условных изображений, применяемых на функциональных схе-
Счет- фактура Главный к файл \ товарных t запасов Главный файл > товарных / запасов повторных заказах к оплате Чеки файл .Общая сводка Балансовый отчет Подробный отчет Итоговый отчет Сводка прибылей и убытков \ Главный , . \ бухгалтерской \ ведомости Обработка записей продаж Запись в общую бухгалтерскую ведомость Главный файл счетов к оплате товарных запасов Обработка оплаченных счетов Печать чеков Сообщение о продаже Запись в общую ведомость счет Обработка записей закупок Главный чфайл платеже способ- ных счетов Обработ- ка записей платеже- способных счетов Сообщение об изменении товарных запасов Сообщение о счете к оплате Отчет о Подробный отчет Отчет о поступлении счетов-фактур Оформление счетов клиентов Сводка поступлений наличных средств Бланк Главный файл бухгалтерской / ведомости Обработка терской ведомости Обработка записей товарных запасов Сообщение о продаже продаж Главный файл платеже-/ способ- / пых счетов Сообщение о Платежеспособном счете Оплаченный Сообщение, об изменений Отчет о динамике продаж Текущий бухгалтерский отчет Сводка продаж бухгалтерскую бухгал- Сообщение о закупке Глав- ный файл счетов к оплате Сводка закупок Подробный отчет Отчет о поступ- лении счетов- фактур Сводка выплат наличными Рис. 2.5. Структура системы для ведения документации в небольшом торго- вом предприятии.
Проектирование систем 45 мах. Направления потоков данных отмечаются стрелками, про- веденными между наборами данных и физическими носителями информации. Наборы данных, используемые одновременно в не- скольких процессах, связаны между собой и имеют одинаковые имена. Диаграмма приведенного типа является обобщением ме- Вход Исходные Создание файла данные Выход Вход Оперативный отчет Главный „список Главный список Система сопровождения « данных Корректировка, файла Выход Изменения .данных Оперативный' отчет Главный ^список Вход Главный список Использование, файла Выход Отчет Рис. 2.6. Диаграмма Варнье — Орра для системы сопровождения данных. тода фрагментации программ Варнье — Орра, который приме- няется главным образом при разработке программ. Более подробно этот метод рассматривается в последующих главах. 2.2.3. Функциональные схемы Функциональная схема системы состоит из одного или не- скольких прямоугольных блоков, содержащих названия прог- рамм. Эти блоки соединяются входящими в них стрелками с источниками и исходящими из них стрелками — с приемника- ми данных. Источники и приемники изображаются в виде бло- ков, очертания которых напоминают определенные физические носители информации. В каждом блоке записано имя програм- мы или набора данных; иногда оно дополняется информацией, раскрывающей назначение блока. Основное внимание в схемах этого типа уделяется описанию потоков данных в системе и ис- пользуемых наборов данных.
46 Глава 2 Некоторые из стандартных символических обозначений, при- меняемых в функциональных схемах, приведены на рис. 2.7. Среди них можно выделить три различные разновидности сим- волов. Блоками в виде прямоугольника, изображающего про- цесс, и трапеции, изображающей ручную операцию, отмечаются Процесс Печатный документ Перфокарта Магнитная лента Пакет перфокарт Дисплей Память прямого доступа Магнитный диск Оперативная память Носитель произвольного типа Ручная операция Линия передачи данных Рис. 2.7. Стандартные условные обозначения на функциональных схемах. определенные действия, совершаемые над данными. Линии свя- зи передачи данных указывают потоки данных. Все остальные символы обозначают различные физические носители информа- ции. При построении функциональных схем внешние носители, такие, как перфокарты или бумага для печати, помещаются по бокам листа. Вдоль линий передачи должны чередоваться про- цессы и наборы данных. Процессы могут сообщаться друг с другом только с помощью наборов, и всякая пересылка данных из одного набора в другой должна осуществляться соответст- вующим процессом.
Проектирование систем 47 На рис. 2.8 приведены функциональные схемы фрагмента системы сопровождения файлов. Рис. 2,8, а иллюстрирует систе- му с файлами на магнитной ленте, в которой корректирующие данные вводятся с перфокарт. Выбор магнитной ленты в каче- стве носителя информации приводит к необходимости введения Рис. 2.8. Структуры систем корректировки главного файла. нового главного файла. После завершения записи всей необхо- димой информации новый главный файл начинает эксплуатиро- ваться вместо прежнегд главного файла. Штриховая линия на схеме не обозначает поток данных, но просто указывает, что оба файла логически представляют собой одно и то же. На рис. 2.8,6 главный файл хранится в устройстве массовой памя- ти, а ввод корректирующей информации производится с клави- атуры терминала. Поскольку экран дисплея является лишь средством кратковременной визуализации, для получения доку- ментальной отчетности о проведенных корректировках система Дополнена регистрационным (контрольным) файлом.
48 Глава 2 2.3. Методы разработки средств управления Все рассмотренные выше типы схем — граф-диаграммы, диа- граммы Варнье — Орра, функциональные схемы — рассчитаны на описание потоков данных в программно-управляемых систе- мах, в которых только программы могут инициализировать или прекращать генерацию потоков данных. Однако в приложениях, для которых характерна работа в режиме реального времени, некоторые из системных функций управляются не столько прог- раммами, сколько самими данными, т. е. в таких системах не процессы являются причиной, вызывающей некоторые переме- щения данных, но, напротив, данные приводят в действие или заставляют прекращаться определенные процессы. Так, регу- лярная выдача месячного отчета инициируется программными средствами, в то время как корректировка файла в интерактив- ном режиме происходит потому, что пользователь терминала имеет данные, которые он желает ввести с клавиатуры. Если часть системы управляется данными, поступление заявок на ис- пользование программ и информационных массивов может быть предсказано лишь в вероятностном смысле. В одно и то же время в активном состоянии могут находиться несколько про- цессов. Эти процессы в свою очередь могут одновременно предпринимать попытки обращения к одним и тем же наборам данных. На схемах, иллюстрирующих структуру системы, долж- ны отражаться любые ситуации, когда возникает необходимость в синхронизации процессов. Для этого можно либо расширить набор обозначений, применяемых при составлении функцио- нальных схем, либо использовать специальные схемы, напри- мер ПЕРТ-диаграммы. 2.3.1. Функциональные схемы, учитывающие синхронизацию процессов На функциональных схемах можно отобразить координацию программ во времени, включив в число символов специальный синхронизирующий интерфейс, помещаемый между программа- ми и наборами данных. На рис. 2.9 показан вариант системы сопровождения файлов, дополненной средствами интерактивного доступа к данным. К наборам данных нельзя обращаться в процессе их объединения или корректировки. Запрещается так- же выполнять корректировку наборов в то время, когда с ними ведется работа другого типа. Это предполагает, что программа, обратившаяся к файлу, после получения требуемой записи должна определенным образом известить, что файл свободен. Операции доступа к наборам данных необходимо координиро- вать. Эту функцию выполняет синхронизирующий интерфейс между программами и данными, изображенный на схеме двой-
Проектирование систем 49 ними горизонтальными линиями. Программа выборки начинает выполняться только в том случае, когда имеется запрос на вы- борку и главный файл свободен. В главном файле должен быть предусмотрен механизм блокировки, который позволяет активи- ровать либо тот, либо другой выходной поток, но не оба одно- Рис. 2.9. Система сопровождения файлов е синхронизацией операций доступа. временно. Кроме того, ни один из выходных потоков не должен инициироваться до тех пор, пока не завершится процедура вво- да данных в файл. Этот тип координации выполнения операций с файлом на схеме не показан. Имена файлов и программ да- ны прописными буквами в верхней части соответствующих бло- ков. Для процессов или наборов данных, представленных по- добными блоками в системной документации, как правило, имеются подробные описания. 4-399
50 Глава 2 Приведенная система сопровождения файлов и интерактив- ного доступа к данным является примером одного из возмож- ных решений ставшей уже классической проблемы чтения-запи- си. Эта проблема заключается в такой организации процедур считывания и записи, которая обеспечивала бы доступ с целью выборки данных одновременно нескольким процессам, но огра- ничивала бы запись в файл таким образом, чтобы в каждый момент времени в активном состоянии мог находиться только один процесс записи. Все механизмы, отвечающие за выполне- ние процедур параллельной обработки запросов на выборку и последовательной обработки запросов на корректировку дан- ных, должны быть реализованы в соответствующих программах выборки и корректировки. В качестве меры защиты все операции считывания данных фиксируются в регистрационном файле. Это делается в тех случаях, когда не исключены попытки получения неавторизо- ванного доступа к данным. Иногда удается идентифицировать подобные операции и проследить их путь вплоть до источника запроса. Фиксируются также все проведенные корректировки данных: с одной стороны, для того, чтобы иметь отчетность об их выполнении, с другой — для упрощения операций по восста- новлению системы после ее отказа. На приведенной схеме не предусмотрено копирование файлов. Однако, если значительная часть обмена информацией проводится в интерактивном режи- ме, весьма желательно, чтобы имелись дубликаты файлов. Системы описанного типа характерны не только для прило- жений, связанных с управлением предприятиями и делопроиз- водством. Они могут применяться для работ с базами данных, а также для управления совместным использованием различных системных ресурсов. Задачи разделения ресурсов возникают в разнообразных приложениях — например, в системах програм- мирования или при обработке слов в реальном времени. В муль- типрограммных операционных системах и системах управления файлами также необходимо регулировать доступ и координиро- вать использование данных, процессов и устройств. 2.3.2. ПЕРТ-диаграммы Из функциональной схемы на рис. 2.9 следует, что програм- мы QUERY и UPDATE начинают выполняться только в том случае, когда свободен главный файл и имеется запрос, введен- ный с терминала. Однако на схеме нельзя показать, что доступ к главному файлу не может быть получен, пока не завершено его формирование. Для этого удобно использовать ПЕРТ-диа- грамму. Диаграммы этого типа введены в гл. 1 в качестве сред- ства описания процесса проектирования прикладных систем. Их можно применять также для иллюстрации порядка взаимодей-
Проектирование систем 51 ствия программ в интерактивных системах. На рис. 2.10 приве- дена ПЕРТ-диаграмма для системы сопровождения файлов. На этой диаграмме отсутствует завершающее событие, имеющееся, например, на рис. 1.3, поскольку работа системы не прекраща- ется в том смысле, в каком заканчивается выполнение програм- мы или проекта. Система прекращает свое функционирование, когда хранящиеся в ней данные больше не нужны и программы перестают эксплуатироваться. На ПЕРТ-диаграмме не указываются наборы или потоки данных. Она отображает связи по управлению, существующие Рис. 2.10. ПЕРТ-диаграмма интерактивной системы сопровождения файлов. в системе, а также координацию выполняемых действий. Каж- дая стрелка соответствует определенной операции, а каждый кружок — событию, под которым понимается завершение одной или нескольких операций и переход к другим. По содержанию эти символы прямо противоположны аналогичным обозначени- ям на граф-диаграммах. Событие 1 показывает, что файл сво- боден для использования. Подобное состояние возникает после завершения операций по созданию файла, корректировке файла или после выдачи данных в ответ на запрос. Ввод данных с тер- минала на диаграмме не изображен. Запрос на выборку или проведение корректировки удовлетворяется, если система нахо- дится в состоянии 1. Он реализуется путем выполнения дейст- вий, приводящих систему в состояния 2 или 3. Последние обо- значают прием требования и переход к операциям обмена дан- ными между файлом и терминалом. Как и на функциональных схемах, учитывающих синхронизацию, на ПЕРТ-диаграмме нель- зя показать, что корректировка файла и выборка данных несо- вместимы во времени или что параллельно•могут выполняться несколько операций выборки, а операции корректировки — не могут. Однако в большинстве случаев действия, инициируемые после возникновения общего для них события, могут выполнять- 4*
52 Глава 2 ся параллельно. Так, например, запись в регистрационный файл происходит одновременно с обработкой запроса на выборку данных. 2.3.3. Сети Петри Диаграммы, называемые сетями Петри, используются в ка- честве моделей, которые описывают движение потоков данных в сетях, допускающих частичное или полное переключение по- токов из одних магистралей в другие. Такая ситуация характер- на для интерактивной системы корректировки — выборки дан- ных, в которой данные могут проходить либо через программу выборки, либо через программу корректировки, одновременная работа которых не допускается. В то же время от программ вы- борки или корректировки параллельно могут исходить несколь- ко выходных потоков. Сети Петри позволяют исследовать как потоки данных, так и динамику передач управления в системе. Для этого строится несколько схем, отражающих последова- Рис. 2.11. Сеть Петри для интерактивной системы сопровождения файлов. Отчет об перациях выборки Отчет о корректи- ровках тельные состояния сети, из которых видно, как происходит пе- ремещение точек управления вдоль потоков данных. Следую- щие друг за другом изображения сети Петри отличаются лишь расположением указанных точек. На рис. 2.11 приведена сеть Петри для системы сопровожде- ния файлов, соответствующая моменту, когда получен запрос на корректирование данных, а главный файл свободен. Кружки на диаграммах этого типа называются позициями (они изобра- жают наборы данных), а вертикальные линии — переходами (отмечают процессы). Жирная точка в кружке (рис. 2.12) ука-
Проектирование систем 53 зывает, что соответствующий набор в данный момент активен. Переходы действуют подобно вентилям. Когда все наборы, из которых в процесс поступают данные, становятся активны, вен- тиль открывается, в результате чего возбуждаются выходящие из него потоки данных. Это изображается путем перемещения жирных точек из входных во все выходные позиции. Если про- изошли описанные действия, говорят, что переход сработал. Запрет на параллельное выполнение операций корректировки и выборки реализуется в виде соглашения, согласно которому пе- реходы не могут срабатывать одновременно. Невозможность сосуществования операций корректировки и выборки приводит к тому, что главный файл остается недоступным все время, пока жирная точка остается в позиции главного файла. Когда выбор- ка или корректировка завершаются, жирные точки перемеща- ются в выходные позиции и файл вновь становится доступным для использования. Сеть Петри отображает последовательное чередование со- бытий, возникновение которых определяется доступностью дан- ных. В течение очередного интервала времени, следующего за тем, которому соответствует диаграмма на рис. 2.11, в активном состоянии будут находиться два файла — главный и регистраци- онный. Стрелка, идущая от процесса, подавшего запрос, назад а. Создание файла б. Ожидание в. Одновременное поступление запросов на выборку и корректировку д. Регистрация проведенной корректировки Рис. 2.12. Последовательные состояния сети Петри.
54 Глава 2 Входы Процедуры обработки Выходы UPDATE 1 .Корректиру- | ) ющие данные у а) Ключ записи 6) Код операции в) Новое значение 1.Проверка правиль- ности сообщения 2 Файл CUSTOMER а) Ключ записи б)Запись 2 Проверка правиль- ности записи из главного файла 3 Корректировка запи- си из главного файла 4. Сообщение в регист- рационный файл 1 .Файл CUSTOMER а) Ключ записи б) Запись 2 Регистрацион- ный файл а) Старая за- пись б)Корректиру- ющие данные в) Новая за- пись Рис. 2.13. Схема HIPO для программы корректировки файла CUSTOMER, к главному файлу, показывает, что файл освобождается и к не- му вновь разрешен доступ. Такие связи необходимы для возвра- та жирных точек в позицию, представляющую главный файл. На рис. 2.12 показаны последовательные состояния сети Пет- ри, соответствующие этапам первоначального формирования файла, затем его корректировки и, наконец, этапам выборки из него данных. Считается, что операция корректировки имеет при- оритет перед операцией выборки. На сети Петри можно также отметить невозможность одновременного удовлетворения не- скольких запросов на проведение корректировок. Для этого до- статочно поставить в соответствие каждому запросу отдельный переход. 2.3.4. Схемы HIPO После составления общего наброска проекта системы следу- ющим этапом является более детальная проработка способов взаимодействия ее отдельных элементов. Для этой цели целе- сообразно использовать схемы HIPO. Название схем образова- но из первых букв английского словосочетания hierarchy-input- processing-output (иерархическое описание входов-обработки- выходов). Употребление здесь слова «иерархическое» связано с тем, что данные схемы применяются в сочетании с графически- ми схемами заданий для отдельных программ, построенными
Проектирование систем 55 по иерархическому принципу. На рис. 2.13 представлена схема HIPO для программы корректировки главного файла. На этой схеме указаны потоки данных и перечислены содержащиеся в них элементарные данные. Определены также основные части описываемого процесса и связанные с ними элементы входных и выходных данных. Использование схем HIPO характерно для той стадии про- ектирования, когда системные аналитики уже могут приступать к разработке программ и данных. Эти схемы, определяя основ- ные функции каждой программы и перечень основных элемен- тов данных, не конкретизируют способы организации данных, иерархическую структуру подпрограмм и выбор алгоритмов об- работки. На этапе разработки программ схемы HIPO могут применяться в качестве средства описания функций, реализуе- мых программой, и циркулирующих внутри нее потоков данных. Их можно рассматривать как промежуточный шаг перед со- ставлением спецификаций программ и выбором способов орга- низации данных (блоки 5.2 и 5.3 на графической схеме задания, приведенной на рис. 1.2). При разработке внешнего плана про- екта программы и информационные структуры не детализиру- ются, указываются лишь связанные с ними потоки и наборы данных. Именно эти сведения и содержатся в схемах HIPO. 2.4. Проектная документация В графической схеме задания на разработку документации по системе, изображенной на рис. 1.4, проектная документация представлена блоком 2. На этапе проектирования, которому со- ответствует данная схема, проектная документация должна описывать компоненты системы прежде всего с точки зрения их организации. Различные типы схем и диаграмм, обсуждавшиеся в данной главе, следует рассматривать не только как элементы методики проектирования и средства формализации этого про- цесса, но и как составную часть проектной документации, даю- щую наглядное представление о структуре системы. Организа- ционные аспекты проекта воспринимаются значительно легче в графическом исполнении, чем в виде чисто словесного описания, хотя это отнюдь не означает, что графические материалы вооб- ще не нуждаются в словесных пояснениях. Проектная документация должна содержать следующее: • перечень важнейших функций, реализуемых системой; • определения потоков данных; • графические описания процессов передачи данных и уп- равления; • перечень носителей информации, используемых для хране- ния всех наборов данных; • описания средств взаимодействия с вычислительной сре-
56 Глава 2 дой, включая списки уже имеющихся наборов данных и прог* рамм; • перечень имен, подлежащих разработке наборов данных и программ; при их выборе должны учитываться функциональное назначение программ и отражаться особенности данных, содер- жащихся в наборах; • требования к синхронизации выполняемых операций с учетом доступности данных; • предложения по разработке системных средств защиты; • перечень контрольных примеров для проверки всех эле- ментов системы с приведением ожидаемых результатов. Значительная часть перечисленных сведений может быть оформлена в виде разнообразных диаграмм и схем типа пред- ставленных в этой главе. В качестве дополнительного описа- тельного материала можно включить список информационных структур и программ — с указанием разделов документации и снабженный перекрестными ссылками. Такой список позволит читателю получить подробную информацию, которую нельзя по- черпнуть из отдельной диаграммы. Следует отметить, что рассмотренная часть полного пакета документации по системе служит отправным материалом для обсуждения внутренних деталей проекта, а также разработки программ и данных. 2.5. Упражнения 1. Начертите граф-диаграмму для библиотечной системы регистрации вы- даваемой литературы (упр. 1 в гл. 1). 2. Предположим, что в системе оформления предварительных заявок на пользование теннисными кортами (упр. 2 в гл. 1) используется база данных, работающая в интерактивном режиме. Начертите функциональную схему этой системы, а также схемы HIPO для всех входящих в нее программ. 3. Просмотрите набор операторов языка управления заданиями, необхо- димый для трансляции и выполнения программы на языке высокого уровня, стандартном для вычислительной установки, которой вы пользуетесь. Начер- тите граф-диаграмму потоков данных и процессов, участвующих в задании. 4. Начертите функциональную схему, на которой были бы показаны про- цессы, временные наборы данных и постоянные наборы данных, описываемые в операторах управления заданиями, которые используются при трансляции и выполнении программы на языке высокого уровня, стандартном для вашей вычислительной установки. 5. Начертите функциональную схему системы ведения документации в не- большом торговом предприятии, структура которой приведена на рис. 2.5. 6. Начертите схемы HIPO для всех программ системы, рассмотренной в предыдущем упражнении. 7. Начертите ПЕРТ-диаграмму или схему Петри для программ спулинга периферийной ЭВМ, которые должны принимать данные, поступающие с двух терминалов, и временно заносить их иа диск, ожидая того момента, когда освободится процессор. Когда процессор освобождается, указанные програм- мы передают порцию данных в процессор, затем вновь временно записывают полученный результат на диск и хранят его там до освобождения терминала, пославшего данные. 8. Разработайте проект произвольной системы, которая включала бы не менее четырех программ. Составьте всю необходимую документацию.
Глава 3 МЕТОДЫ ОРГАНИЗАЦИИ ДАННЫХ Часто при обработке информации на ЭВМ используется та- кая же организация данных, как и при ручной обработке. В на- учных расчетах наиболее важным элементом данных является отдельное измерение. Данные представляют собой неструктури- рованные группы связанных друг с другом измерений. В эконо- мике самым важным элементом данных является запись. В Ко- боле обработка информации осуществляется способом, приня- тым для экономических приложений: открывается файл, читается запись, используется из нее информация и, наконец, закры- вается файл. Фортран — язык для научных расчетов, поэтому он ориентирован на обработку индивидуальных элементов дан- ных (обычно чисел). Языки ПЛ/1 и Паскаль имеют соответст- вующие средства для работы в обеих названных выше обла- стях. Разработка баз данных предусматривает совершенно другой подход к организации данных. Базы данных похожи на библио- теки каталогизированной и упорядоченной информации. В них широко используются перекрестные ссылки, дающие возмож- ность организовать доступ к данным на основе их атрибутов и взаимных связей. На уровне аппаратных средств и в операционных системах данные представляются в форме, отличной от той, которая лег- ко воспринимается пользователем. Кроме того, они могут быть по-другому организованы. Скалярные величины обычно перфо- рируются на одной и той же карте, даже если они и не связаны Друг с другом. Это позволяет уменьшить число перфокарт. Не- зависимые записи могут быть упорядочены для того, чтобы ускорить к ним доступ, причем этот порядок не определяется какими-то характеристиками данных, а вводится только для Удобства. К записям, имеющим ключи для организации пря- мого доступа, можно обратиться с помощью системного прог- раммного обеспечения, использующего методы просмотра таб- лиц и последовательного поиска. 3.1. Типы данных Элементы данных обычно разделяют на две основные груп- пы: скаляры и структуры. К скалярам относятся флаги, коды, числа и слова; к структурам — массивы, таблицы, списки, стеки,
58 Глава 3 множества и записи1). Структуры формируются из скалярных данных, сгруппированных в соответствии с определенными пра- вилами. Отметим, что блоки, файлы и базы данных должны обязательно состоять из структурированных элементов. Внеш- ние для программной системы данные также формируются из структурированных элементов. В то время как некоторые языки программирования снабже- ны средствами для формирования различных данных, другие позволяют только явно задать определенную структуру. В Форт- ране, чтобы одинаково оНисать три массива, необходимо одно и то же описание повторить для каждого: DIMENSION А (10, 20), В (10, 20), С (10, 20) (Пример 3.1) Аналогично в Коболе определяются два идентичных структури- рованных списка имен и адресов: (Пример 3.2) 03 A-PERSON OCCURS 100 TIMES. 05 A-NAME PIC X(20). 05 A-ADDR PIC X(20). 01 B. 03 B-PERSON OCCURS 100 TIMES. 05 B-NAME PIC X(20). 05 B ADDR PIC X(20). В Паскале имеются как средства для задания правил форми- рования структур, так и средства для непосредственного опи- сания данных: (Пример 3.3) type TEN—BY_TWENTY = array(10,20) of real; var A.B.C : TEN—BY_TWENTY Запись на Паскале может быть определена следующим обра- зом: type CUSTOMER = record (Пример 3.4) NAME: character-string; ADDRESS: character—string; TELEPHONE: numeric__string; ACCOUNT-BALANCE: PAYMENT-DATE: real; integer end *> В отечественной литературе их часто называют структурами. — Прим, перед.
Методы организации данных 59 С помощью такого рода утверждений устанавливается тип дан- ных. Объявление переменной позволяет задать конкретный пример, относящийся к этому типу. Любой язык с динамическим распределением памяти имеет средства для задания правил объявления сложных структур. Часто невозможно заранее установить размер структуры данных. В большинстве языков программирования нельзя за- давать динамически изменяемые структуры данных. Програм- мисту необходимо указывать наибольшее число экземпляров и максимальный размер каждой структуры. Существует несколь- ко формальных методов описания, которые следует использо- вать для определения динамически изменяемых структур. В дальнейшем эти описания могут быть преобразованы в прог- раммные сегменты. То, что неформально, можно записать в виде семья =отец, мать, ребенок.... формально записывается следующим образом: семья ->отец, мать, (ребенок)+ Знак «плюс» показывает, что в семье должен быть по крайней мере один ребенок. Если, определяя понятие семьи, учитывать вариант, когда детей в семье нет, имеем семья ->отец, мать, (ребенок)* Семья может быть также определена рекурсивно следующим образом: семья ->отец, мать, дети дети -»-ребенок | ребенок, дети (Пример 3.5) Вертикальная черта трактуется как «или». Нотации такого ви- да часто используются для определения различных типов дан- ных в языках программирования. Имеются в виду числовые кон- станты и арифметические выражения: действ_знач->- (знак | Л) ((цифра) *. (цифра) +1 (цифра) +. (цифра) *) выражение-»-выражение(4-1 —) терм | терм терм->терм (* |/) операнд | операнд (Пример 3.6) операнд->действ_перем | действ-знач Действительное значение определяется как знаковая или беззнаковая последовательность цифр, содержащая десятичную точку. Выражение и терм могут быть определены с помощью повторяющихся конструкций, но предпочтение отдается рекур- сии, поскольку ее легче использовать для анализа арифметиче- ских выражений.
60 Глава 3 В языках программирования данные, для описания которых нужны конструкции повторения или рекурсии, можно реализо- вать, используя массивы или указатели: (Пример 3.7) type FAMILY = record FATHER: PERSON; MOTHER: PERSON; CHILDREN: array[1. .3] of PERSON end или (Пример 3.8) type CHILDREN = record CHILD: PERSON; OTHER_CHILDREN: t CHILDREN end ' FAMILY = record FATHER: PERSON; MOTHER: PERSON; OFFSPRING: CHILDREN end Если, как в примере 3.7, использовать для реализации массивы, нужно задать максимально допустимое число детей в семье. Поскольку большинство семей имеет только двух детей, нецеле- сообразно отводить место для хранения информации о десяти или двадцати детях. Применяя указатели, можно добиться, что- бы размер отводимого пространства точно зависел от числа де- тей в семье. Правда, определенные ресурсы необходимо потра- тить на организацию процесса размещения памяти. Удобно использовать указатели для реализации структур данных типа деревьев. Они также позволяют наиболее естественным образом описывать отношения между элементами в базах данных. Способы описания или изображения сложных структур дан- ных, принятые в человеческой практике, часто отличаются от способов их описания для использования в ЭВМ. Программисты настолько привыкли к работе с массивами, что иногда забыва- ют, что структура массива обычно не отражает структуру дан- ных, хранящихся в нем. При использовании массива подразу- мевается, что, во-первых, известно число его элементов, во-вто- рых, все элементы принадлежат одному и тому же типу и име- ют один и тот же размер и, наконец, в-третьих, доступ к эле- ментам массива организуется в соответствии с их положениями в массиве. Реальные множества данных редко бывают однотип-
Методы организации данных 61 ними. Порядок использования данных может оказаться несущественным или может определяться значениями данных. Таким образом, можно сделать вывод о том, что языки прог- раммирования накладывают искусственные ограничения на данные. 3.2. Уровни организации данных Программист, проектировщик и пользователь имеют свои собственные, отличающиеся друг от друга взгляды на органи- зацию данных. В соответствии с этим могут быть выделены три уровня организации данных: • Логическая организация данных: проектный уровень. • Представление данных: уровень языка реализации. • Физическая организация данных: машинный уровень. Логическая организация данных отражает взгляд пользо- вателя на данные. В ее основе лежат требования пользователя и внутренне присущие данным связи. Это наиболее важный уровень абстракции, используемый при представлении данных, поскольку именно требования пользователей определяют облик проектируемой системы. Если на этапе проектирования системы удачно выбрана логическая организация данных, изменения си- стемных требований, не приводящие к модификации логической структуры данных, не повлекут за собой реорганизации на более низких уровнях представления данных. Только на логическом уровне могут применяться формаль- ные методы описания динамически изменяющихся структур. Никакая дополнительная информация о членах семьи не изме- нит общую логическую структуру семьи: семья =отец, мать, ребенок... отец =имя, возраст, профессия Мать =имя, возраст, девичья фамилия, профессия ребенок =имя, возраст, пол В этом случае новые данные о членах семьи не нарушают ее глобальной организации, но могут привести к изменениям в представлении информации. Описание данных на языке программирования относится к Уровню представления данных. Отношение между данными за- даются в виде, характерном для конкретного языка. На этом Уровне оперируют массивами и указателями. На языке Кобол Данные о семье и отношения между ними могут быть представ- лены только в том случае, если точно задать число детей в
62 Глава 3 семье и размеры каждого элемента: (Пример 3.9) 01 FAMILY. 03 FATHER. PIC X{20). PIC 99. PIC X(15). 05 FATHERS-NAME 05 FATHERS-AGE 05 FATHERS-OCCUPATION 03 MOTHER. 05 MOTHERS-NAME PIC X(20). 05 MOTHERS-AGE PIC 99. 05 MAIDEN-NAME PJC X{15). 05 MOTHERS-OCCUPATION PIC X(15). 03 CHILDREN. 05 CHILD OCCURS 30 TIMES. 07 CHILDS-NAME PIC X(20). 07 CHILDS-AGE PIC 99. 07 CHILDS-SEX PIC X. В дальнейшем информация о представлении данных может быть распределена по отдельным программным модулям, при- чем можно использовать как внешнюю, так и внутреннюю фор- мы представления данных. Под внешним представлением пони- мается взгляд на данные со стороны других программ, т. е. представление на уровне потоков данных. При внешнем пред- ставлении главным является определение возможных путей до- ступа. Внутреннее представление — это представление в виде внутренних областей хранения данных, т. е. структура данных может быть во внешнем представлении стеком, а во внутрен- нем— массивом или связанным списком. Предположим, что данные о покупателе включают номер счета, имя, адрес, даты предыдущих расходов, платежи и счета. Внешней формой их представления (возможно, в базе данных) являются отдельные агрегаты данных о личности покупателя и о его платежах. Вну- треннее представление может состоять из таблиц, полей при- знаков и указателей. Уровень физической организации связан с системным прог- раммным обеспечением. На этом уровне приходится опериро- вать с границами слов, размерами полей, двоичными кодами и физическими записями. Большинство средств системного прог- раммного обеспечения дают возможность программисту осуще- ствить выбор из весьма узкого круга способов представления данных, которые имеют более или менее ясную физическую ор- ганизацию. Агрегаты данных, хранящихся в быстродействую- щей памяти, могут быть представлены массивами или стеками. К записям файла можно обращаться в последовательном или произвольном порядке, используя ключи различных типов.
Методы организации данных 65 Б языках программирования существуют возможности блокиро- вания и буферизации, а также управления доступом к еще меньшим единицам информации. Так, в ПЛ/1 различные типы операторов GET и PUT позволяют программисту явно или по умолчанию проводить форматирование упорядоченных или не- упорядоченных данных. Проблема проектирования структуры данных заключается, во-первых, в определении способа их логической организации и, во-вторых, в поиске пути выражения этой организации на уров- не внешних представлений, допускаемых системой и языком программирования. Отметим, что системное программное обес- печение управляет потоком данных между программами и внешними устройствами, а языки программирования управляют обменом данными между программами. К сожалению, ввод и вывод являются нестандартными опе- рациями, и поэтому системный аналитик и программист долж- ны хорошо знать не только возможности языка программирова- ния, но и разбираться в работе системного программного обес- печения. Проектирование и реализация должны быть проведе- ны так, чтобы изменение требований пользователя, а также мо- дификация аппаратного и программного обеспечения как можно- в меньшей степени влияли на проектируемую программную си- стему. Это влияние можно свести к минимуму путем изоляции операций системного интерфейса и ввода-вывода. Системное программное обеспечение позволяет изолировать реальные процедуры ввода-вывода от программиста. Таким об- разом, программы в принципе не меняются при изменении внешних устройств. Программист в свою очередь должен огра- дить пользователя от изменений в системном программном обеспечении. Пользователь не должен также заботиться о- структурах и размерах файлов. Аналогично в прикладных си- стемах не должны использоваться внутренние представления данных вне отдельных модулей. Программы доступа, которые служат для размещения, поиска и изменения данных, должны обеспечивать контакт с данными только на уровне внешнего- представления. Это означает, что процесс перехода от уровня логической организации к уровню представлений должен вклю- чать проектирование не только структур данных, но и прог- раммных модулей. Причем при проектировании структур дан- ных используется принцип изоляции информации. Доступ к хранящейся в базе данных информации о семьях должен начинаться с идентификации конкретной семьи, и только после этого можно обратиться к данным о каком-то ее члене. Доступ осуществляется с помощью заранее определенных свя- зей и атрибутов, которые могут отражать или не отражать ре- альную физическую организацию базы данных. Предположим, что нужно узнать имя старшей дочери. Пользователь, какой-
64 Глава 3 нибудь человек или программа, не должны иметь право про- сматривать весь список имен, отыскивая дочерей и сравнивая их возраст. Программа пользователя не должна быть связана с методом хранения данных. Когда для хранения информации используется база данных, СУБД выбирает пути доступа в со- ответствии с начальным определением базы данных. Осущест- вляя запрос, необходимо придерживаться правил, установлен- ных при реализации базы данных. Если информация содержится не в базе данных, а в файло- вой системе, то, для того чтобы могли быть использованы новые формы запросов, необходимо либо изменить существующие программы, либо создать новые. Организация новых путей до- ступа, отличных от предусмотренных при первоначальной орга- низации, может привести к дополнительной обработке данных. СЛЕДУЕТ ПРОДУМЫВАТЬ ПУТИ ДОСТУПА Тщательное определение путей доступа и явное описание структур данных только в некоторых модулях следует произво- дить по крайней мере по двум причинам. Во-первых, пользова- тель не должен иметь дела с не относящимися к его задаче во- просами хранения данных. Во-вторых, как можно меньше моду- лей должно зависеть от организации хранения данных. Если организация данных изолирована, изменение их представления повлечет за собой незначительные изменения текста программы. Поэтому необходимо стараться размещать структуру данных только в одном программном модуле. Кроме того, это является одной из причин, из-за которой следует избегать использовать общие области в Фортране и глобальные переменные в ПЛ/1. определяйте структуры данных КАК МОЖНО В МЕНЬШЕМ ЧИСЛЕ МОДУЛЕЙ Наличие постоянных файлов приводит к дополнительным проблемам. В системах баз данных постоянные файлы относятся к внутренним структурам, и доступы к ним осуществляются только с помощью администратора базы данных, т. е. структу- ра постоянных файлов в базах данных изолирована. В других системах нельзя ограничиться разрешением доступа к ним толь- ко одной программе. Наилучшим выходом является размещение описания файла в небольшом числе программных модулей. Это позволяет минимизировать возможные изменения программ. Если в каждой программе, которая работает с файлом, доступ к нему обеспечивает один модуль, можно рекомендовать хра- нить описание файла в библиотеке и во всех программах ис- пользовать для доступа один и тот же модуль или по крайней мере одно и то же описание файла.
Методы организации данных 65 3.3. Уровень логической организации данных Логическая организация данных — это, по существу, пред- ставление данных пользователем. Индивидуальные данные должны быть сгруппированы в соответствии с их связями, а также со связями, отражающими способы их использования. В обычных библиотеках информация разделяется на классы и подклассы. В библиотеке конгресса США индекс QA использу- ется для книг по математике и вычислительной технике. Книги, имеющие индексы от QA76.5 до QA76.9, относятся к подклас- су языков программирования и операционных систем класса вычислительной техники. Последние буквы и цифры индекса идентифицируют автора. Такая иерархическая система удобна для хранения книг на полках, но не приспособлена для опреде- ления их местонахождения. Руководством для пользователя служат предметный и авторский каталоги с массой перекрест- ных ссылок. Как и в библиотеке, данные в ЭВМ могут быть упорядоче- ны с помощью ключей, которые являются элементами данных. Для организации данных можно использовать уровни иерархий или многоаспектный доступ и перекрестные ссылки. Для опи- сания информации в базах данных чаще всего используются иерархические или реляционные модели, причем реляционная модель ближе к логической структуре данных. Обычно службы, проводящие подписку на журналы, хранят информацию о каждом подписчике и о том, на что он подписал- ся, т. е. информация сразу разделяется на два класса. Если данные используются для оповещения подписчиков об истечении сроков подписки и для направления почтой номеров журналов, они должны быть организованы так, чтобы можно было осуще- ствить доступ не только по дате окончания подписки, но даже и по адресу так же просто, как и по фамилии подписчика или номеру подписки. Считается, что названия журналов и инфор- мация, касающаяся почтовой метки журнала, входят в состав Данных. Предполагается, что человек может подписаться более чем на один журнал. На уровне логической организации описа- ние данных может меняться, поскольку службы, проводящие подписку, естественно, не захотят вводить ограничение на число журналов, на которые может подписаться человек. Информа- ция, необходимая для почтовой метки, состоит из фамилии и ад- реса подписчика, номера счета, кода регионального цент- ра распределения и срока окончания подписки. Номер счета может быть связан либо с фамилией подписчика, либо с номе- ром подписки. Также нужно уметь связывать каждую почтовую метку с соответствующим журналом. Предположим, что номера счетов относятся к подписчикам, а не к номерам подписки. Если имеется достаточно памяти и почтовые метки наносятся в раз- 5-399
66 Глава 3 ное время на разные журналы, информация о каждом журнале’ может обрабатываться независимо и задача упрощается. Прав- да, появится избыточность, заключающаяся в хранении в не- скольких местах одних и тех же фамилий и адресов. Но если данные хранить на лентах и редко использовать, то проблем не возникает. Отметим, что изменение адреса подписчика приведет к необходимости вносить изменения во все относящиеся к нему! подписки. Тогда структуру данных можно описать следующим^ образом: идентификатор журнала (Пример 3.10). информация о подписке номер счета (ключ) фамилия подписчика адрес подписчика код округа срок окончания подписки Информация такого рода должна повторяться для каждой под- писки. Если информация содержится в одном файле или в базе: данных, предназначенной для непосредственного доступа, избы- точность весьма нежелательна. Устранение избыточности и группирование данных в соответствии с их действительными; связями приводят к следующей структуре: информация о подписчике (Пример 3.11) номер счета (ключ) фамилия подписчика адрес подписчика код округа информация о подписке название журнала срок окончания подписки информация о подписке название журнала срок окончания подписки можно С помощью трех процедур нормализации .любые данные к единой форме. Первая нормальная форма (1НФ) позволяет описать данные без повторяющихся групп. Это осуществляется с помощью выделения части структуры, ко- торая повторяется в другой структуре. Если информация о под- писке представлена так, как описано выше, ни одно из этил представлений не является первой нормальной формой, по- скольку либо информация о подписчике может появиться в за- писях, связанных с несколькими журналами, либо однотипная информация о нескольких подписках может встретиться в запи- си, связанной с одним подписчиком. В первом случае налицс избыточность, которая затрудняет, например, изменение адрес* подписчика. Во втором — не ясно, что делать с информацией ( подписчике, если сроки действия всех подписок истекли. Про
Методы организации данных 67 цесс преобразования к первой нормальной форме сводится к за- даче построения двух типов структур, связанных перекрестны- ми ссылками: информация о подписчике (Пример 3.12) номер счета (ключ) фамилия подписчика адрес подписчика код округа информация о подписке код подписки (ключ) номер счета название журнала срок окончания подписки В качестве ключа для информации о подписчике использует- ся номер его счета. Так как несколько подписчиков могут иметь одну и ту же фамилию, использовать ее как ключ нельзя. Совместно фамилия и адрес, вероятно, уникальны, но адреса до- вольно часто меняются, и их изменение не должно приводить к закрытию одного счета и открытию другого. Код подписки сле- дует дополнить, чтобы снабдить информацию о подписке уникальными идентификаторами. Записи о подписке и подписчи- ке должны быть связаны друг с другом либо с помощью указа- телей, либо с помощью составного ключа, содержащего номер счета и код подписки. Ключ — это уникальный идентификатор каждого экземпляра записи. Изменение ключа должно приво- дить к удалению одного экземпляра записи и созданию другого. Тип отношения подписчик — подписка есть многие-к-многим. Структура данных относится ко второй нормальной форме (2НФ), если она, во-первых, относится к первой нормальной форме и, во-вторых, неключевые элементы функционально пол- но зависят от ключа. Это означает, что ключ требуется для оп- ределения других элементов, и, если ключ является составным, ни один из других элементов не может быть определен на осно- вании знания только части ключа. Приведение структуры к 2НФ осуществляется путем удаления элементов данных, ко- торые зависят только от части ключа, и формирования из них и из частичных ключей отдельной структуры. Структура подписчик — подписка относится ко второй нор- мальной форме, поскольку, с одной стороны, никакая информа- ция о подписчике не может быть получена без указания номера счета и, с другой — никакая информация о подписке не может 'быть получена без указания кода подписки. Код подписки явля- ется единственным составным ключом, и срок окончания под- писки не может быть определен только из номера счета или только из названия журнала. Если фамилию подписчика и его адрес использовать как ключ к информации о подписчике, структура не будет принадлежать к 2НФ, так как код округа можно определить только на основании знания адреса. 5*
68 Глава 3 Структура данных относится к третьей нормальной форме (ЗНФ), если не существует неключевых элементов, которые функционально зависят от других неключевых элементов. Если же такой элемент существует, он непосредственно связан с клю- чом. Перевод из 2НФ в ЗНФ осуществляется путем удаления функционально зависимых элементов и формирования из них и из элементов, которые их определяют, отдельной структуры. Данные о подписчике не относятся к ЗНФ, поскольку код окру- га зависит от адреса и может быть определен только на основа- нии адреса. Для того чтобы привести данные о подписке и под- писчиках к ЗНФ, необходимо сформировать новую структуру для адреса и кода округа: (Пример 3.13) информация о подписчике номер счета (ключ) фамилия подписчика адрес подписчика информация о подписке код подписки (ключ) номер счета название журнала срок окончания подписки местонахождение подписчика адрес (ключ) код округа Большинство систем управления файлами и СУБД не пред- назначено для проведения нормализации данных. Логическая структура нормализованных данных должна быть удобна для перехода к более аппаратно-ориентированной форме, определя- емой требованиями программного обеспечения ЭВМ. То, что ло- гическая структура данных может сильно отличаться от их фи- зической структуры, было показано на примере реляционных баз данных, в которых данные связаны друг с другом чисто ло- гически. Однако большинство баз данных имеют иерархическую, а не реляционную организацию. При реализации структуры дан- ных являются элементами иерархической организации, а не связанными друг с другом схемами нормализованных данных. Служба, проводящая подписку, хранит свои многочисленные данные в файлах. Их необходимо организовать так, чтобы мож- но было легко осуществить многоаспектный доступ. Для того ; чтобы внести изменения в данные о подписке, необходимо знать ; код подписки; чтобы изменить фамилию или адрес подписчика, ' необходим только номер его счета; чтобы послать сведения, ка- сающиеся окончания срока подписки, надо получить информа- . цию как о подписке, так и о подписчике. Поэтому дата оконча- , ния подписки должна играть роль вторичного, хотя и не уни- кального, ключа. Снабжение журналов почтовыми метками, не- обходимыми для их рассылки, потребует информации сразу из <
Методы организации данных 69 всех трех структур, причем доступ будет осуществляться по на- званию журнала. Несколько методов позволяют организовать доступ по вторичным ключам: метод, использующий инвертиро- ванные файлы, и метод формирования цепочек записей. Для описания логической структуры данных применяется несколько формальных нотаций. В функциональной нотации используются ключи и явно вы- делены связи между структурами данных: подписчик (номер счета) = фамилия, адрес подписка (номер счета, название журнала) = срок окончания подписки местонахождение (адрес) =код округа Рис. 3.1. Диаграмма отношений, существующих между данными о подписке. Это связывает логические структуры с традиционными метода- ми хранения наборов данных. На рис. 3.1 показана диаграмма отношений, существующих между данными о подписке. Она в большей степени, чем функ- циональная нотация, пригодна для организации хранения ин- формации в базе данных. Каждая запись на диаграмме, связан- ная ребром с другой записью, содержит ключ или часть ключа, позволяющего организовать доступ к этой второй записи. Ребра с двумя стрелками используются для обозначения отношений типа «один-к-многим», ребра с одной стрелкой — для обозначе- ния отношений типа «один-к-одному». Петли отражают группи- рование информации о подписке, во-первых, с помощью назва- ния журнала и, во-вторых, с помощью срока окончания подпис- ки. Диаграмма не содержит информации о конкретной реализа- ции. Показаны только структуры данных и пути доступа к ним. Выбранное представление данных обеспечивает возможность доступа с помощью ключей и связей, существующих между Данными. В заключение приведем правила, которые следует применять для нормализации данных: а) необходимо образовывать новые структуры из повторяю- щихся групп элементов;
70 Глава 3 б) если неключевой элемент зависит только от части ключа, его необходимо перевести в отдельную структуру, ключом кото- рой служит часть первоначального ключа; в) если неключевой элемент полностью определяется другим неключевым элементом, его надо перевести в отдельную струк- туру, ключом которой служит второй неключевой элемент. РАЗРАБАТЫВАЙТЕ ЛОГИЧЕСКУЮ СТРУКТУРУ ДАННЫХ 3.4. Представление данных Рассмотрим представление данных, относящихся к приклад- ной области на системном уровне. Оно определяется взглядом на данные из точки, находящейся вне программной среды, т. е. либо со стороны пользователя, либо со стороны системы. Внеш- нее представление данных — это способ организации входных и выходных данных. Одни данные содержатся в системе, другие — поступают на ее вход, третьи — предназначены для вывода и передачи пользователям. Внутреннее представление данных — это представление данных внутри программ, доступное только программисту. 3.4.1. Внешнее представление Данные, доступ к которым осуществляется только с по- мощью ЭВМ, хранятся в закодированном виде. Они не нужда- ются в редактировании и преобразовании в символьную форму. На анализ потоков данных в системе и проектирование внешних входов и выходов влияют различные физические характеристи- ки устройств. Организация данных на внешних носителях опре- деляется размерами перфокарты и экрана, длиной строки АЦПУ, объемом и характеристиками дисковой памяти. Все это относится к физической организации. Внешнее представление элементов, составляющих отчет о подписке на журналы, вклю- чает описания их характеристик, заголовков, итоговых характе- ристик и зависимостей между такими объектами, как названия округов и данные о подписчиках. Основное внимание уделяется организации логических элементов, а не требованиям, опреде- ленным аппаратными средствами. Такие факторы, как блокирование записей, размер дорожки и способ организации хранения информации на дисках, связа- ны с работой операционной системы, и, до тех пор пока эффек- тивность не станет основным стимулом, эти факторы не будут оказывать большого влияния на внешнее представление дан- ных. Особенно тщательно описываются атрибуты данных, по- скольку это важно для любого языка программирования. На- пример, выделяются объекты, являющиеся строками символов,
Методы организации данных 71 десятичными и двоичными числами. Также необходимо уметь определять, какие программы обрабатывают те или иные дан- ные. Обычно для этих целей используются различные типы диаграмм и таблиц. Для того чтобы показать, как происходит обмен данными между программами и областями хранения данных, использу- ются схемы и таблицы потоков данных. Надо стремиться к то- му, чтобы потоки содержали минимально необходимое количе- ство данных. Внутреннее представление данных и их физиче- ская организация не должны влиять на внешнее представление. Например, если программа сопровождения файла, содержащего информацию о подписчиках журналов, идентифицирует подпис- > * Номер подписки Срок окончания подписки Номер счета / Файлы \ [с данными! 1о подпис-J^ Хииках/Д^ вь|й адрес Новый срок окончания подписки Новый код округа Новая подписка ^/програм-Д [ма коррек-1 < — —Хтировки / Номер счета V V Новый адрес ------Номер подписки Название нового журнала Продление подписки Код корректировки / Коррек-Л ^(тирующий \ файл . Рис. 3.2. Потоки данных, возникающие при корректировке главного файла. чиков с помощью номера счета и не предназначена для измене- ния их фамилий, фамилия подписчика не должна входить в поток данных, идущий к программе корректировки или от нее. Процесс корректировки, схема потоков данных которого приве- дена на рис. 3.2, предусматривает изменение адресов и сроков окончания подписок, а также добавление информации о новых подписках. Схемы такого вида объединяют индивидуальные элементы данных в потоки, идущие к областям хранения данных и прог- раммам. На уровне внешнего представления эти элементы либо считаются независимыми (скалярными), либо входят в состав записей или каких-то других языковых структур. Не все эле- менты данных обязательно должны быть представлены в каж- дом корректирующем сообщении. Так, в нем обычно присутст- вует либо новый адрес подписчика, либо новая дата окончания подписки. Изменения информации о подписке могут затрагивать либо только одну из трех логических структур данных, либо все три сразу. Использование одной и той же вершины на схеме для дан- ных всех трех типов не является достаточным основанием для помещения данных в один и тот же файл. Но с другой стороны, тот факт, что используются три логические структуры, не явля- ется основанием для создания трех различных файлов. Решение о том, как хранить данные, зависит и от логической организа- ции, и от распределения элементов данных по потокам. Физиче-
72 Глава 3 <ски данные могут быть организованы в виде нескольких файлов, и может быть предусмотрена возможность передачи сразу не- скольких записей от каждого файла. Обычно из-за ограничен- ных возможностей системного программного обеспечения прог- рамма корректировки может работать только с одной записью каждого файла- Она может работать также с дополнительной информацией. Если программа не использует дополнительную информацию, знания о том, как эта информация организована, ей не нужны. Нужно знать только то, что необходимо для мо- дификации файла. ОПИСЫВАЙТЕ ТОЛЬКО НЕОБХОДИМЫЕ ДАННЫЕ Потоки данных, изображенные на схеме (рис. 3.2), могут также быть описаны с помощью таблиц потоков данных (табл. 3.1). Каждая таблица соответствует одному потоку дан- ных. Таблицы потоков данных могут быть связаны не только не- посредственно с самими потоками данных, но и с программами или областями хранения данных. Для составления таких таблиц Таблица 3.1. Таблицы потоков данных а) Поток данных от главного файла к программе корректировки Элемент данных Пояснение Комментарии CUST-ACCT-NO MAG-ACCT-NO MAG-EXP-DATE Номер подписчика Номер подписки Срок окончания под- Требуется > Требуется только для писки продления подписки б) Поток данных от корректирующего файла к программе корректировки Элемент данных Пояснение -« Комментарии ACCT-NO NEW-ADDR SUBSCRIP-NO Номер подписчика Новый адрес Номер подписки Требуется Необязателен Требуется только для изменений подписки SUBSCRIP-EXT NEW-SUBSCRIP TRANS-CODE Дата продления Новый журнал Код корректировки Необязателен > Требуется в) Поток данных Элемент данных от программы корректировки к главному файлу Пояснение Комментарии NEW-ADDR NEW-MAG-ACCT-NO NEW-REG-CODE NEW-EXP-DATE Новый адрес Новый номер подпис- ки Новый региональный код Новый срок оконча- ния Необязателен > Требуется, если есть NEW-ADDR Требуется, если есть NEW-MAG-ACCT-NO
Методы организации данных 73 Таблица 3.2. Таблицы потоков данных для процессов обработки и областей хранения а) Таблица потоков данных для главных файлов к Элемент данных Пояснение Источник1) Назначение CUST-ACCT-NO Номер подпис- СОЗДАНИЕ КОРРЕКТИ- CUST-NAME чика Фамилия под- > РОВКА, АД- РЕСАЦИЯ, ПРОВЕРКА СЧЕТОВ АДРЕСАЦИЯ, CUST-ADDR писчика Адрес подпис- » ПРОВЕРКА СЧЕТОВ АДРЕСАЦИЯ, NEW-ADDR чика КОРРЕКТИ- ПРОВЕРКА ADDR-REG-CODE Код округа РОВКА СОЗДАНИЕ СЧЕТОВ АДРЕСАЦИЯ , NEW-REG-CODE MAG-EXP-DATE Срок окончания КОРРЕКТИ- РОВКА СОЗДАНИЕ КОРРЕКТИ- NEW-EXP-DATE подписки КОРРЕКТИ- РОВКА, MAG-ACCT-NO Номер подписки РОВКА СОЗДАНИЕ ПРОВЕРКА СЧЕТОВ КОРРЕКТИ- NEW-MAG-ACCT- NO КОРРЕКТИ- РОВКА, РОВКА ПРОВЕРКА СЧЕТОВ б) Таблица потоков данных для процесса корректировки Входное имя Источник2) Выходное имя Назначение CUST-ACCT-NO Файл подписчи- ACCT-NO ков Корректирую- щий файл То же Файл подписчи- SUBSCRIP-NO MAG-ACCT-NO 1 NEW-SUBSCRIP NEW-ADDR SUBSCRIP-EXP MAG-EXP-DATE ков Корректирую- NEW-MAG-ACCT-NO щий файл То же NEW-ADDR > Файл подписчи- NEW-EXP-DATE Файл подписчи* ков То же » ] TRANS-CODE ков Корректирую- NEW-REG-CODE щий файл > ’) Под источником в данном случае автор, по-видимому, понимает процесс, в резуль* тате которого образуется элемент данных. — Прим, перед. 2) Здесь под источником подразумевается место хранения элемента данных. — Прим, пер ев. ______________________________________________________________ нужна информация об источнике каждого элемента данных и о том, как он используется. В табл. 3.2 указаны источник и на- значение каждого элемента данных. Дополнительная информа- ция может быть добавлена, как только это окажется нужным.
74 Глава 3 Таблица потоков данных, в которой представлены все дан- ные, циркулирующие между отдельными частями системы, на- зывается словарем данных. В словаре данных обычно содержит- ся информация о каждом элементе данных. Табл. 3.3 представ- ляет собой словарь данных, используемый программами кор- ректировки, рассылки счетов и адресации в системе подписки чна журналы. В словаре присутствуют области хранения данных, а не потоки данных, и не приведены детали физической органи- зации хранения агрегатов данных. Например, не указано, име- ют ли записи фиксированную или переменную длину, сблоки- рованы ли, снабжены ли ключами, распределены ли по отдель- ным файлам. В словаре данных должны быть предусмотрены следующие записи: имя элемента данных с пояснениями; диапазон возмож- ных значений с пояснениями, например F, М, U (женщина, мужчина, не определено); тип элемента данных; отношения между данными; источник данных; использование данных; не- обходимая степень защиты; данные о том, является ли элемент обязательным или необязательным и при каких условиях. Обычно возникает конфликт между способами использова- ния данных и их логической структурой. Это касается не только программы адресации, формирующей адресные метки, и прог- раммы корректировки, имеющей доступ по всем структурам, но относится также к программе проверки счетов, которая форми- рует извещения об окончании сроков подписки, и к программе адресации, осуществляющей доступ к структурам с помощью элементов, а не главных ключей. Для задания представления индивидуальных элементов данных в словарь вводится инфор- мация об их типах, длинах и диапазонах возможных значений. Представление групп данных зависит от конкретной СУБД или от системы управления файлами. Необходимо искать компро- миссы между временем доступа, занимаемой памятью и часто- той использования данных при согласовании логической органи- зации и представления данных. Так как издания журналов бу- дут пересылаться подписчикам значительно чаще, чем извеще- ния об окончании срока подписки, и, кроме того, модификация информации о подписке будет осуществляться сравнительно редко, то данные на физическом уровне должны быть организо- ваны так, чтобы наиболее эффективно работала программа ад- ресации. 3.4.2. Эргономические факторы Данные, являющиеся внешними по отношению к системе (например, входные и выходные данные), должны быть пред- ставлены в форме, удобной прежде всего для восприятия чело- веком. Как входные, так и выходные данные должны легко чи-
Таблица 3.3, Словарь данных Для подписки на журналы Имя, дополнительные имена, пояснение Тип Диапазон Цель использо- вания Где исполь- зуется Источник От чего зависит ACC_.NO CUST-ACCT-NO Номер подписчика Числовой 10 000—59 999 КОРРЕКТИ- РОВКА Ключ подписчи- ка СОЗДАНИЕ — CUST-NAME Фамилия подписчика Символьный — АДРЕСАЦИЯ, ПРОВЕРКА СЧЕТОВ — СОЗДАНИЕ Номер подпис- чика CUST-ADDR NEW-ADDR Адрес подписчика Символьно-чис- ловой — АДРЕСАЦИЯ, ПРОВЕРКА СЧЕТОВ — СОЗДАНИЕ, КОРРЕКТИ- РОВКА Номер подпис- чика (и код сообщения) ADDR-REG-CODE NEW-REG-CODE Код округа Символьный Е, S, М, W АДРЕСАЦИЯ Ключ издания СОЗДАНИЕ, КОРРЕКТИ- РОВКА Адрес подпис- чика MAG-ACCT-NO NEW-MAG-ACCT-NO Номер подписки Символьный COMPLIFE АСТ-СОМР АДРЕСАЦИЯ, ПРОВЕРКА СЧЕТОВ Ключ подписки СОЗДАНИЕ, КОРРЕКТИ- РОВКА Номер подпис- чика (и код сообщения) MAG-EXP-DATE NEW-MAG-EXP-DATE Срок окончания подпис- ки Символьно-чис- ловой JAN 70-DEC 90 КОРРЕКТИ- РОВКА, ПРОВЕРКА СЧЕТОВ Ключ счета СОЗДАНИЕ, КОРРЕКТИ- РОВКА Идентификатор подписки SUBSCRIP-NO NEW-SCRIP Новый журнал Символьно-чис- ловой Номер счета+ -1-название журнала — Ключ подписки СОЗДАНИЕ, КОРРЕКТИ- РОВКА — SUBSCRIP-EXP Дата продления Числовой 1, 2, 3 КОРРЕКТИ- РОВКА —— КОРРЕКТИ- РОВКА Идентификатор подписки (и код сообще- ния) TRANS-CODE Код корректировки Символьный А, Т, М КОРРЕКТИ- РОВКА Ключ сообщения КОРРЕКТИ- РОВКА —
76 Глава 3 таться, не требуя для этого специальных знаний о форматах, кодах и представлении данных. В процессе проектирования этой « части внешнего представления данных важны как общая орга- 1 низация данных, так и конкретные детали представления. Ор- ганизация зависит от системных спецификаций и требований пользователей. Детали зависят от языка реализации, но прежде всего определяются стилем, вкусом и возможностями адаптации к условиям, возникающим в процессе использования системы. Формат ввода числовых данных должен включать десятич- ную точку и стоящие слева пробелы. Выводимые числовые дан- • ные должны быть отредактированы и снабжены специальными | метками так, чтобы можно было легко узнать, являются ли они | денежными единицами или результатами измерений физических I величин. Предположим, что необходимо использовать коды. ’ Тогда мнемонические сокращения предпочтительнее числовых кодов (М и F лучше, чем 1 и 2). Если нужно применить аббре- виатуру, следует отдавать предпочтение стандартным сокраще- ниям (М, Т, W, TH, F, SA, SU лучше, чем М, Т, W, Н, F, S, U). Если применяется нестандартная аббревиатура или код, Поль- зователю часто будет нужна подсказка, поясняющая смысл со- общения. Соответствующие формы должны быть предусмотрены ' для представления как входных, так и выходных данных. Входные данные должны легко восприниматься человеком. Их следует вводить в полном соответствии с организацией. При вводе необходимо использовать разделители. Таблицы должны вводиться по строкам. Границы строк должны приходиться на интервалы между данными. Ошибки ввода, заключающиеся в пропуске символа или появлении лишнего символа, можно лег- ко обнаружить еще до ввода данных в ЭВМ, если данные пред- ставлены в форме, удобной для восприятия. Системы ввода дан- ных, работающие в режиме on-line, выдают пользователю вво- димую информацию, давая ему таким образом возможность ис- править допущенные ошибки до передачи данных в ЭВМ. Для ввода с перфокарт и с клавиатуры на диск в системах ввода должны быть предусмотрены специальные форматы. ВХОДНЫЕ ДАННЫЕ ДОЛЖНЫ БЫТЬ УДОБНЫ ДЛЯ ВОСПРИЯТИЯ Выходные данные обычно выводятся в форме, которая в эко- номике получила название «отчеты», или в форме, которая в диалоговых системах называется ответами на запросы. Какое бы количество данных ни выводилось, их надо разместить в со- - ответствии с заданными форматами, озаглавить, датировать и снабдить специальными метками. Числовые данные должны быть отредактированы и снабжены специальными метками. Не- числовые сообщения следует выводить вместе с пояснениями, касающимися используемых в них кодов и сокращений. Каж- дая страница должна быть пронумерована, озаглавлена и да* .
Методы организации данных 77 тирована. К результатам, возможно содержащим ошибку, необ- ходимо привлекать внимание пользователя. Для этого можно использовать флажки, например три звездочки (***). ВЫХОДНЫЕ ДАННЫЕ ДОЛЖНЫ БЫТЬ УДОБНЫ ДЛЯ ВОСПРИЯТИЯ Отчеты обычно разбиваются на страницы и часто содержат управляющие поля — элементы данных, используемые для груп- пирования данных. Изменение значения этого элемента приво- дит к временному прерыванию процесса обработки, называемо- му управляющим прерыванием. В отчетах службы подписки на журналы информация о подписке группируется по округам. Для каждого округа можно определить различные статистические характеристики, касающиеся подписки. Такая же информация может быть в дальнейшем получена в каждом округе для каж- дого журнала. Коды округов и названия журналов будут слу- жить управляющими полями. Внутри управляющих групп раз- личные характеристики, вероятно, следует расположить в соот- ветствии с алфавитным, числовым или некоторым другим порядком. Разделение на страницы относится к физической орга- низации данных, а группирование в соответствии с управляю- щими полями — к представлению данных. Внешнее представление данных может быть описано или изображено с помощью графической схемы. Используя коды округов и названия журналов в качестве управляющих полей, можно представить отчет о подписке в следующем виде: вазвание отчета (Пример 3.14) название округа название журнала характеристика подписки итоги подписки на журнал название журнала характеристики подписки итоги подписки на журнал итоги подписки по округу .название округа итоги подписки по округу общие итоги. Графическая схема для приведенного выше описания дана на рис. 3.3. Графические схемы такого типа получили название «иерархические схемы данных». Звездочки на схеме обознача- ют, что блок может повторяться несколько раз. Введение нового управляющего поля приводит к появлению еще одного уровня иерархии. Каждый уровень схемы связан с одной управляющей группой. Весь отчет делится на части, соответствующие различ- ным округам. Каждая часть, соответствующая какому-то окру- гу, в свою очередь делится на части, соответствующие различ-
78 Глава 3 ним журналам. Заголовки отчетов, округов и журналов должны включать значение управляющего поля для определенной груп- пы. Итоговые характеристики иногда не нужны. На рис. 3.4 показана физическая организация данных, со- ставляющих отчет. Она учитывает такие характеристики печа- Рис. 3.3. Иерархическая схема отчета (логическая структура). тающего устройства, как длина строки и размер страницы, и связана с содержанием отчета только в том случае, если рас- пределение по страницам зависит от управляющих прерываний. Кроме того, ширина полос и расположение информации зависит от характеристик как отчета, так и печатающего устройства. Заголовки и итоги, относящиеся к страницам, должны включать Рис. 3.4. Иерархическая схема множества страниц, составляющих отчет (фи- зическая структура).
Методы организации данных 79 номера страниц, дату, идентификатор отчета, легенды и, воз- можно, заголовки столбцов и итоговые результаты. Иерархиче- ские схемы, приведенные на рис. 3.3 и 3.4, предназначены для описания одного и того же отчета, но находятся в таком отно- шении друг к другу, что непосредственно сопоставить их нельзя. Диаграмма логических отношений между данными, графическая схема внешнего представления данных и графическая схема фи- зической организации являются тремя способами представления данных, относящимися к трем различным точкам зрения на данные. 3.4.3. Внутреннее представление Представление данных в программах зависит от применяе- мого языка программирования. Наиболее часто используемыми типами данных являются скаляры, одномерные массивы и за- писи. Большинство языков программирования позволяет рабо- тать со всеми этими типами данных. Часто наиболее подходящими являются структуры данных типа множеств, связанных списков или таблиц. В принципе все подписчики на журналы составляют множество, так же как и относящиеся к одному подписчику журналы. Множество пред- ставляет собой совокупность элементов. В отличие от массива или последовательного файла элементы множества не упорядо- чены. Не существует причины, по которой информация о под- писке должна быть упорядочена каким-то вполне определенным образом. Правда, необходимо так представить данные, чтобы можно было всегда быстро получать нужную информацию. Так как языки программирования позволяют работать с различны- ми типами данных, задачей программиста является выбор такой структуры данных, которая наиболее удобна для представления информации. Для целей ввода-вывода скалярные элементы данных следу- ет рассматривать либо как независимые элементы, либо как элементы записей. В зависимости от возможностей языка прог- раммирования данные могут быть либо строками символов, ли- бо десятичными или двоичными числами; числа, в свою очередь, могут быть представлены в форме с фиксированной или плава- ющей точкой. Конкретная форма представления данных и их размеры зависят от конкретной ЭВМ. Десятичные числа с фик- сированной точкой — основной тип числовых данных Кобола. В Фортране числа представляются в двоичной форме с фикси- рованной или плавающей точкой (целые, вещественные). В Бей- сике используются двоичные числа с плавающей точкой. ПЛ/1 позволяет задавать произвольное количество цифр в десятич- ном или двоичном числе либо с плавающей, либо с фиксирован- ной точкой. Но представление чисел в ПЛ/1 зависит еще и от
so Глава 3 ЭВМ, поэтому реально программисту предоставляется меньше возможностей, чем предусмотрено в общем описании языка. В программе или в области размещения данных они обычно объединены в массивы, структуры или более сложные агрегаты, которые часто непосредственно не предусмотрены в языках программирования. Существующие механизмы доступа, работа- ющие со структурами и массивами, изолированы от программи- ста. В том случае, когда разрабатываются новые сложные типы данных, расширяющие возможности языка, механизмы доступа к ним также следует изолировать. Обычно для реализации сложных типов данных можно ис- пользовать несколько способов. Например, стек можно реализо- вать в виде массива переменной длины, находящегося внутри массива с фиксированными границами, или в виде связанного списка, управляемого указателями. Для того чтобы упростить внесение изменений в эти типы данных, их следует поместить в отдельные программные модули. Если для внутреннего пред- ставления данных удобно использовать стек или множество, программист может создать специальное расширение языка, включающее этот тип данных, причем обращаться к нему мож- но только путем вызова подпрограмм. Остальные модули прог- раммной системы не зависят от способа реализации этого ново- го типа данных. Независимо от того, как реализован стек, процедуры, рабо- тающие с ним, должны уметь помещать элементы в стек, выби- рать элементы из стека и проверять, не пуст ли стек. Для того чтобы можно было работать с новым типом данных, необходи- мы специальные операции, например: NEW (стек) создание пустого стека PUSH (стек, элемент данных) помещение элемента в стек POP (стек) выборка элемента из вершины стека EMPTY (стек) проверка, пуст ли стек; результатом является значение «истина», если стек пуст, и «ложь» в противном случае NEW и PUSH — базовые операции, используемые при работе со стеком. Первая создает стек, а вторая помещает в него элемен- ты. POP — процедура, характерная только для работы со сте- ком. Она может быть определена с помощью двух базовых про- межуточных операций ТОР и REMOVE, которые позволяют вы- делить две основные функции, реализуемые POP, но не опреде- ляют способов организации доступа к структуре из других мо- дулей: POP (стек) = ТОР (стек); REMOVE (стек) (Пример 3.15) TOP (PUSH (стек, элемент данных)) = элемент данных TOP (NEW (стек)) — ошибка REMOVE (PUSH (стек, элемент данных) )= стек REMOVE (NEW (стек)) — ошибка
Методы организации данных 81 Определим с помощью базовых операций функцию EMPTY: EMPTY (PUSH (стек, элемент данных))—ложь EMPTY (NEW (стек))—истина Согласно приведенным выше определениям, POP выбирает вер- шину стека и удаляет ее из стека. Вершина стека описывается следующим образом: если стек- не пуст, вершина представляет собой последний помещенный в стек элемент; в противном слу- чае стек не имеет вершины. Операция REMOVE дает в резуль- тате стек без элемента, помещенного в него последним. К толь- ко что созданному стеку операцию REMOVE применять нельзя. Для определения пустого стека используется понятие нового* (или только что созданного) стека: пустой стек не содержит ни одного элемента. Любая реализация стека должна удовлетворять этим опре- делениям. Важно, что стек определяется как способ хранения данных, которые подчиняются определенным правилам, дейст- вующим при обращении к данным, а не как массив с указате- лями, которые передвигаются по определенным правилам. В языке Ада существует несколько механизмов, позволяю- щих изолировать детали реализации. Используя их, стек можно определить следующим образом: ' aeneric (type ITEM) (Пример 3.16> package STACKS is ' restricted type STACK is private; procedure PUSH(I: in ITEM; S: in out STACK); procedure POP(I: out ITEM; S: in out STACK); procedure CREATE(S: out STACK); ' - function EMPTY(S: in STACK) return BOOLEAN; private type STACK is , , , (определение стека) end; package body procedure PUSH (I: in ITEM; S: in out STACK) is . , . (onределение.процедуры) end PUSH; . . . (другие определения процедуры) end STACKS; Пакет (package) представляет собой программный модуль, со- стоящий из двух частей. Первая часть содержит информацию, необходимую для использования пакета. В нее входят имена процедур PUSH, POP, CREATE, EMPTY и списки их парамет- ров. Приватный тип STACK может использоваться только в этом пакете и только с определенными операциями. Другой па- б-399
«2 Глава 3 раметр ITEM является параметром настройки. Его тип опреде- ляется в момент создания стека, и все элементы стека должны относиться к этому типу данных. Поэтому описанный пакет может использоваться для работы со стеками, предназначенны- ми для хранения данных разных типов. Формальное определение любого сложного типа данных до- пускает несколько различных реализаций. Но новый тип дан- ных можно определить, ориентируясь только на способы обра- щения к нему. Для определения множества можно использовать базовые операции NEW (множество) ADD (множество, ключ, элемент) и дополнительные средства обращения RETRIEVE (множество, ключ) EMPTY (множество) IN (множество, ключ) В математике определение множества как совокупности элемен- тов подверглось за последнее время незначительным изменени- ям. Считается, что множество состоит не только из известных элементов — ключей, но также из элементов другого типа, свя- занных с ключами. В вычислительной технике идея определе- ния информации по ключу используется значительно чаще, чем просто поиск уже известного ключа. Дополнительные процеду- ры для работы с множествами определяются через базовые опе- рации следующим образом: пгтп1гиг/мгШ/ ч (Пример 3.17) RETRIEVE(NEW(множество), ключ) — ошибка к г г ' RETR IEVE(ADD (множество, ключ1, элемент данных), ключ 2) = if ключ 1 = ключ 2 then элемент данных.. else ЙЕТР1ЕУЕ(множество, ключ 2) EMPTY(NEW (множество)) = истина EMPTY(ADD (множество, ключ, элемент данных)) = ложь IN(NEW(множество), ключ) = ложь 1М(А00(множество,ключ 1,элемент данных), ключ 2) = if ключ 1 = ключ 2 then истина else 1М(множество, ключ2) Состав стандартных операций над множествами может быть расширен. Введем, например, операцию определения мощности множества SIZE (NEW (множество))=0 SIZE (ADD (множество, ключ, элемент данных)) = 1+ SIZE (множество)
Методы организации данных 83 3.5. Физическая организация данных На логическом уровне данные могут быть упорядоченными или неупорядоченными, однородными или неоднородными, со- держать ключи или не содержать. Результаты физических изме- рений считаются упорядоченными, если они делаются в строга определенные промежутки времени и если время является од- ной из используемых для анализа переменных, но явно не вхо- дит в состав данных. Данные можно упорядочить и с помощью» других измеряемых величин, таких, как расстояние. Данные- считаются неупорядоченными, если та величина, в соответствии со значениями которой они группируются, не является хотя бы. неявно частью данных. Упорядоченность предполагает неявное задание меры, достаточной для сравнения значений перемен- ной; эта мера в случае необходимости может быть выражена явно, т. е. на логическом уровне данные будут неупорядоченны- ми, если порядок вводится только из соображений удобства до- ступа к отдельным элементам данных и подсчета общего коли- чества данных. Наиболее адекватной формой представления не- упорядоченных данных является множество. Удобной формой, представления упорядоченных данных может быть очередь, стек,, список или массив. Результаты физических измерений обычно неоднородны, по- скольку либо одни величины измеряются в несколько раз чаще, чем другие, либо единицы измерений различны, либо множество данных содержит элементы разных типов. Записи экономиче- ской информации также неоднородны, поскольку они состоят из символьных и числовых полей разной длины. Внутренние и внешние формы представления однородных и неоднородных данных могут быть одними и теми же, если в языке программи- рования предусмотрено либо динамическое распределение па- мяти, либо памятью может управлять программист. На уровне- физической организации совокупность данных должна быть од- нородной. Если никогда не потребуется идентифицировать отдельные- элементы данных только на основании их порядка, считается,, что данные на логическом уровне не содержат ключей. В. про- тивном случае можно считать, что данные снабжены ключами. Характеристика, используемая для идентификации (ключ), яв- ляется подполем данных. Иногда целесообразно иметь несколь- ко ключей для того, чтобы обеспечить многоаспектный доступ. Ключи могут служить основой выбора внутреннего представле- ния данных. От них может зависеть также физическая органи- зация данных. Одни множества данных являются структурированными, Другие--неструктурированными. Когда множество данных не- 6»
«4 Глава 3 имеет характерной структуры, его элементы все же можно сгруппировать для удобства работы с ними. Это имеет место, когда результаты нескольких измерений помещаются на одну перфокарту, когда блокируются записи или когда несколько корректирующих сообщений вводятся вместе с терминального устройства. Если элементы данных связаны друг с другом, но принадлежат разным типам, их можно объединить либо в таб- лицы, либо в структуры (как в случае с информацией о подпис- чиках). Такие агрегаты данных в свою очередь объединяются в •еще большие агрегаты. Файл может состоять из строк таблич- ной информации или из набора структурированных записей. Анализ потоков данных приводит как к декомпозиции потоков данных на повторяющиеся компоненты, состоящие из отдель- ных элементов данных, так и к объединению элементов данных в агрегаты с целью формирования на их основе структуриро- ванных областей хранения данных. Области хранения данных содержат одновременно несколько экземпляров различных агрегатов данных. Хотя агрегаты дан- ных могут быть логически неоднородными, ограничения, накла- дываемые языками программирования и устройствами ввода- вывода, требуют, чтобы области хранения данных были одно- родными на уровне агрегатов. В общем случае данные должны быть упорядочены либо с помощью ключей, либо с помощью искусственно введенных правил. Выбор физической организации данных позволяет определить, являются ли данные упорядо- ченными и требуются ли для их организации ключи. 3.5.1. Упорядоченность/неупорядоченность Неупорядоченные данные формально можно описать с по- мощью множества, в котором существует правило, позволяющее определить, принадлежит элемент множеству или нет, и системы . доступа, которая организует доступ к нужному элементу. Эле- менты множества могут либо иметь ключи, либо сами быть клю- чами. Если с элементами множества необходимо выполнять ка- кие-то операции, требуется система доступа, которая просмат- ривает элементы множества последовательно или параллельно без учета какого-либо предварительно определенного порядка. , Так как к элементам множества необходимо постоянно обра- щаться, множества обычно реализуются в виде одномерных массивов или в виде файлов с последовательным доступом. Прямой доступ осуществляется с помощью выбора структуры файла, допускающего как прямой, так и последовательный до- ступ. Файлы и массивы могут состоять из упорядоченных и неупо- рядоченных данных. Фирма IBM вместо файла использует тер- >
Методы организации данных 85 Мин «набор данных»*), для того чтобы подчеркнуть, что он час- то состоит из логически неупорядоченных агрегатов данных. Если элементы множества не снабжены ключами, их можно хранить в массиве или в последовательно организованном фай- ле. Если ключи предусмотрены, именно они служат основой ор- ганизации системы хранения. Данные, которые необходимо упо- рядочить несколькими способами, используя различные ключи, могут храниться в базе данных, в многомерном массиве, в фай- ле со связанными с помощью ключей элементами или в системе инвертированных файлов. 3.5.2. Однородность/неоднородность Неоднородные элементы данных отличаются друг от друга типом, организацией или длиной или состоят из частей, отлича- ющихся друг от друга типом, организацией или длиной. На фи- зическом уровне с неоднородными данными можно работать, если провести стандартизацию отличающихся друг от друга ча- стей данных, т. е. необходимо унифицировать типы, размеры и все то, что связано с представ- лением данных в ЭВМ. Это может быть сделано с помо- щью введения единого для всех данных формата, содер- жащего пустые поля; стан- дартизации длины и использо- вания самоопределяемых дан- ных; объединения элементов различной длины в строку по- стоянной длины и использова- ния нескольких описаний, то есть если в записи о событии, происшедшем в определенное время в определенном месте, необходимо изменить место или время, то корректирую- щие данные можно описать различными способами (рис. 3.5). На рис. 3.5, а для описа- ния любой корректирующей информации используется по- ле одной и той же структуры. Запись ID Время____________Место 01275 | 13681 | 36604 | | SAN FRANCISCO а | 01275 | 13681~| | 36604 | SAN FRANCISCO ~] б Рис. 3.5. Физическая организация неоднородных данных. а — единый формат; б — самоопределяемые данные; в — различные описания. ’> В отечественной литературе принято различать понятия «набор данных» и «файл». Набор данных — это совокупность данных, расположенных на внеш- нем носителе информации; файл — абстрактный объект, служащий для пред- ставления набора данных в конкретной программе. Автор для этих двух поня- тий использует один термин — файл. — Прим, перев.
86 Глава 3 Новое значение вводится в определенную часть поля, а другая его часть остается пустой. На рис. 3.5, б для значений корректи- рующих данных используется одно и то же поле. О том, что именно модифицируется, можно узнать либо с помощью кода сообщения, либо проанализировав входную информацию (если она символьная, изменяется место, а если числовая, изменяет- ся время). На рис. 3.5,в корректирующая информация состоит из двух частей: первая содержит идентификатор записи и код сообщения, вторая — новые данные. Так как тип новых данных однозначно определяется с помощью кода сообщения, разные входные данные могут и не иметь одинаковых описаний. 3.5.3. Наличие ключей/отсутствие ключей Все хранимые данные должны быть доступны. Элементы данных, не имеющие ключей, должны размещаться последова- тельно в массиве или в последовательном файле, чтобы к ним можно было легко получить доступ. Данные, снабженные клю- чами, в зависимости от относительной частоты последователь- ных и случайных обращений к ним могут размещаться либо по- следовательно друг за другом, либо по некоторому методу хэширования. Если данные размещаются друг за другом, они могут быть либо отсортированы (во-первых, когда упорядоче- ны, во-вторых, когда доступ к ним осуществляется с помощью индивидуальных ключей), либо не отсортированы. Если поря- док не имеет значения, сортировать данные не обязательно. Если элементы данных размещены по определенному методу хэширования и, кроме того, возможен случай последовательного обращения к ним, необходимо дополнить специальными кодами неиспользуемые промежутки массива или файла или связать вместе информационные области памяти, вводя тем самым упо- Таблица 3.4. Формирование массива данных с помощью ключей а) Последовательно- смежное размещение Ключ Данные б) Метод хэширования Ключ Данные в) Метод хэширования и использование указа- телей Ключ Данные Указатель 127 0 215 241 241 io 241 482 482 04 369 553 553 . . . 397 0 ... ... . . . 482 215 215 02 553 0 ... ... ... 127 127 06 397 397 03 369 369 09
Методы организации данных 87 рядоченность. В табл. 3.4 приведено несколько разных способов хранения данных в массиве. К элементам данных, приведенным в столбце а, можно обращаться либо последовательно, либо ис- пользуя метод двоичного поиска. В столбце б последняя цифра ключа используется алгоритмом хэширования, а свободные об- ласти памяти заполняются нулями. Элементы в столбце б не являются упорядоченными. Отметим, что нуль был бы непри- емлемым заполнителем для столбца а. Оставшуюся часть таб- лицы столбца а следует заполнять данными, распознаваемыми быстрее любого числа, которое может использоваться в качест- ве ключа. К данным, размещенным в столбце в, можно органи- зовать доступ либо в последовательном, либо в произвольном порядке. Последовательный доступ следует начинать с элемента восьмой строки, а для обращения к другим элементам исполь- зовать указатели. Для определения позиции элемента данных в массиве в алгоритмах хэширования часто используются либо определенные цифры, входящие в ключ, либо остаток от деле- ния ключа на определенное число, например на размер табли- цы. Когда хэширование для двух ключей дает одно и то же значение (например, для чисел 127 и 397 в табл. 3.4), конфликт можно устранить, разместив второе значение в области памяти, первоначально выделенной для первого значения. 3.5.4. Физическая организация внешних данных Физическая организация внешних данных зависит от типа используемых устройств ввода-вывода. В режиме непосредст- венного доступа к системе информация, предназначенная для ввода или вывода, представляет собой поток данных. Каждое сообщение, которое адресовано пользователю, сидящему за терминалом, или считывается с экрана терминала, представляет собой одиночный элемент данных. Последний может быть либо простым элементом, либо целой структурой данных, либо сним- ком с экрана терминала. Для хранения данных в терминале ис- пользуется буфер. Он не имеет средств структуризации данных и сохраняет структуру посылаемых данных. Способ буфериза- ции, например использование двойной буферизации или цирку- лярных буферов, изолируется системой от пользователей. При решении большинства экономических задач приходится иметь‘дело с файлами. Информация о служащих, клиентах, то- варах, а также различные сообщения обычно хранятся в фай- лах. Кроме файлов для хранения исходных данных и промежу- точных результатов в экономических приложениях используются вспомогательные файлы, которые по сути являются последо- вательными и хранятся либо на магнитной ленте, либо на диске. Место хранения определяется требованиями, существующими в конкретной организации. На ленте хранятся копии всех важных
88 Глава 3 дисковых файлов. Они обычно используются для восстановле- ния утерянной или испорченной информации, но могут также служить для хранения важной информации в течение довольно долгого времени. Если часто используемый главный файл со- держит записи с редко запрашиваемыми данными, может быть создан файл, содержащий данные о частоте использования каждой записи, и на основании информации, хранящейся в этом файле, из главного файла могут быть исключены опреде- ленные записи. Файлы, накапливающие сведения о работе с другими файлами, создаются только в тех организациях, где это является важным. Нет никаких оснований держать записи о клиентах, не ведущих больше никаких дел с фирмой. Но в ме- дицинских учреждениях данные о пациентах должны храниться в течение нескольких лет. В учебных организациях также не- обходимо достаточно долго сохранять личные дела студентов, уже закончивших обучение. Файлы, предназначенные для регистрации изменений, долж- ны содержать информацию о всех тех модификациях главного файла, которые проводились. Для каждой записи, которая была изменена, необходимо сохранять данные о ее начальном состоя- нии, изменениях и конечном состоянии. Файлы, в которых реги- стрируются изменения, кроме того, можно использовать совме- стно с контрольными точками для повторного запуска програм- мы корректировки файлов, выполнение которой было прервано из-за системного сбоя. В областях, не связанных с экономикой, для хранения дан- ных также используются файлы. Каждый раз, когда собрано большое количество данных, их надо где-то хранить. Файлы нужны для размещения в них экспериментальных данных, по- лучаемых учеными и инженерами, статистических данных, со- бираемых социологами, и текстовых данных, которые исследу- ются специалистами гуманитарных наук. Файлы можно исполь- зовать просто для хранения собранной информации до тех пор, пока она необходима; их можно использовать для хранения данных, которые вырабатываются ЭВМ, но которых слишком много для того, чтобы держать их в оперативной памяти. Кро- ме того, в файлах можно хранить данные, к которым необходи- мо периодически обращаться. Операционные системы используют файлы для временного хранения входных и выходных данных. Специальные файлы предназначены для размещения компиляторов, ассемблеров и загрузчиков. Программы пользователей могут храниться в фай- лах либо временно, либо постоянно. Файлы учета и регистра- ции содержат информацию о работе ЭВМ. Для особенно цен- ных пакетов дисков создаются файлы-дубликаты. Для решения на ЭВМ практически всех задач в той или иной степени необходимы файлы. С файлами, как и со всеми
Методы организации данных 89 остальными структурами данных, могут выполняться различ- яые операции. Файлы можно создавать, инициировать, модифи- цировать, переводить в пассивное состояние и уничтожать, файл модифицируется, если к нему добавляется новая инфор- мация, или часть его информации уничтожается, или некоторая информация, содержащаяся в нем, изменяется. В качестве ин- формации могут выступать совокупности символов или строк, сообщения, записи и страницы текста. Во многих системах дан- ные группируются в блоки фиксированной длины, называемые физическими записями. Эти блоки могут состоять из одной или нескольких логических записей. В зависимости от возможностей операционной системы записи и файлы могут иметь перемен- ную длину (и тогда необходимая память будет выделяться ди- намически) либо их длина ограничена заранее определенной величиной. В языках ПЛ/1, Паскаль и Бейсик имеются операторы, ко- торые позволяют рассматривать файл как непрерывный поток байтов или символов, игнорируя размеры реально существую- щих записей и границы блоков. В программах, использующих потокоориентированный ввод-вывод, преобразование из сим- вольной формы и в символьную форму производится автомати- чески. Программист же управляет вводом-выводом с помощью форматов и объявлений. Для непосредственно вводимых с терминала данных можно использовать как потокоориентированную передачу, так и пе- редачу блоками. В первом случае за один раз передается толь- ко один символ и их группирование производится средствами операционной системы только в пункте назначения. Из символов формируются изображения на экране дисплея, или они группи- руются так, чтобы удобнее было хранить их в массовой памяти. Во втором случае символы накапливаются в специальном буфере и передаются целыми группами. Управление передачей осуществляется с помощью ключа конец-строки, конец-сообще- ния или конец-страницы и начинается с согласования первых символов строки, сообщения или страницы. Независимо от того, состоят ли файлы из интерактивных блоков или из записей, операции ввода-вывода позволяют создавать, инициировать, выбирать, модифицировать, переводить в пассивное состояние, сохранять и уничтожать файлы. Текстовые редакторы, предназ- наченные для работы в режиме непосредственного доступа, поз- воляют манипулировать символами, строками, сообщениями или страницами. Системы управления файлами и массовой па- мятью ориентированы на работу с данными, разделенными на отдельные записи. Два метода доступа к данным используются при потокоори- ентированной передаче и при передаче блоками. Если символы, строки, страницы или записи файла обрабатываются поодиноч-
90 Глава 3 ке, одна за другой, используется последовательный доступ. Ес- ли необходимы только определенные части файла, используется прямой доступ. Прямой доступ применяется также в системах редактирования текстов, предназначенных для работы в интер- активном режиме, когда редактор определяет местонахождение части текста или строки, начинающейся с заданной последова- тельности символов. В системах, использующих массовую па- мять, может запрашиваться любая запись, имеющая ключ, при- чем ключ может быть частью записи. Процедуры, которые осуществляют доступ к информации, содержащейся в файлах, используют следующие элементарные операции: • определение начала текущего элемента данных; • определение позиции следующего элемента; • определение позиции заданного элемента; • считывание элемента из заранее определенной позиции файла; • запись элемента в заранее определенную позицию файла; . • проверку доступности элемента. Определение позиции следующего элемента возможно толь- ко для файла, который содержит упорядоченную по какому-то признаку информацию. Эта операция является основой как по- следовательного, так и потокоориентированного ввода-вывода. Проверка доступности элемента связана с возможностью опре- деления границ файла и с возможностью распознания элемента по его позиции, содержимому определенного поля или по ключу. Определение местонахождения заданного элемента возможно, если информация в файле снабжена ключами и можно автома- тически осуществить поиск нужной последовательности симво- лов. Ключи физических файлов не обязательно должны быть логическими ключами. Данные упорядочиваются и снабжаются ключами для удобства организации доступа к файлу даже тог- да, когда они логически не упорядочены и не имеют ключей. В операционных системах наиболее часто используются по- следовательная, основанная на относительной нумерации, и индексная организации файлов. Существуют также различные варианты этих типов организаций. Кроме того, различные спо- собы описания структуры файлов и различные методы доступа к ним распространены в разных языках программирования и в разных операционных системах. 3.5.5. Последовательные файлы Последовательный файл — это файл, в котором записи фи- зически следуют одна за другой и доступны именно в таком по- рядке. В последовательном файле необходимо выделить первую запись, которая не имеет предшественницы, и последнюю, на-
Методы организации данных 91 зываемую обычно концом файла. В языках программирования для работы с последовательными файлами предусмотрены сле- дующие средства: Язык програм- мирования Функции Фортран Кобол ПЛ/1 Инициирование файла — OPEN OPEN Определение позиции первой записи REWIND OPEN OPEN Определение позиции предшествующей записи BACKSPACE — Проверка конца фай- ла END AT END ON ENDFILE Получение текущей записи READ READ READ Замена текущей запи- си WRITE REWRITE REWRITE Размещение новой за- писи WRITE WRITE WRITE Размещение послед- ней записи ENDFILE CLOSE CLOSE Закрытие файла — CLOSE CLOSE Такие устройства, как устройства считывания с перфокарт, АЦПУ, магнитофоны, работают только с последовательными файлами, поскольку из-за своих физических особенностей они могут обрабатывать информацию лишь последовательно. К со- жалению, имеются существенные различия как между аппарат- ными средствами, так и между системами программного обес- печения, которые предназначены для работы с последователь- ными файлами. Например, одна система программного обеспече- ния может определять позицию записи до ее считывания или размещения, а другая система считывает или размещает за- пись, а затем- определяет позицию следующей по порядку запи- си. Использование одной магнитной ленты не позволяет про- вести корректировку расположенного на ней файла; для осуще- ствления корректировки нужна вторая лента, на которую будут переписываться измененные записи. Дисковые файлы можно корректировать. В то время как на диск или ленту можно за- писывать и читать информацию с помощью одной программы, Для работы с устройством ввода с перфокарт и выводом на АЦПУ одной программы мало. ДЛЯ МОДИФИКАЦИИ ФАЙЛОВ НА МАГНИТНЫХ ЛЕНТАХ ИСПОЛЬЗУЙТЕ КОПИИ ЭТИХ ФАЙЛОВ В одних языках запись конец-файла размещается перед вы- полнением команды перехода к началу файла, но только в том
92 Глава 3 случае, если последним оператором, работавшим с файлами, был WRITE (а не READ). В других языках запись конец-фай- ла всегда размещается перед выполнением перехода к первой записи. И наконец, в некоторых языках принято размещать, запись конец-файла тогда, когда файл, открытый как выходной, закрывается. В одних случаях запись конец-файла размещается програм- мистом, в других — автоматически, но всегда в конце области памяти, предназначенной для хранения данных, а не после по- следней записи файла. Для устранения трудностей, возникаю- щих при обработке последовательных файлов, весь файл дол- жен быть просмотрен целиком, даже если не все данные, хра- нящиеся в нем, необходимы. Использование оператора DATA в Бейсике приводит к созданию последовательного файла, пред- назначенного только для чтения. В этот файл помещается по- ток данных ввода, состоящий из отдельных элементов данных, а не из символов или записей. В Паскале при формировании потока данных, помещаемых в последовательный файл, исполь- зуются данные как в числовой, так и в символьной формах. В том случае, если элементы, составляющие файл, малы (на- пример, символы и числа), в него включаются дополнительные символы. Эти символы, выполняющие, например, функцию ин- дикации конца строки, распознаются так же, как и запись ко* нец-файла. Для более эффективной работы с последовательными пото- ковыми файлами часто требуются дополнительные средства. Например, чтобы изменить информацию в файле, надо предо- ставить возможность легко передвигать указатель символа или элемента вперед или назад на заранее заданное число позиций. Паскаль позволяет это делать, так как использует программ- ные средства, предназначенные для редактирования текстов. В ПЛ/1, Бейсике и Фортране не разрешается использовать потокоориентированную передачу для модификации файлов. В этих языках нужно создавать новый файл. Изменение дан- ных в файле можно провести непосредственно, если использо- вать средства ввода-вывода, предназначенные как для работы с потоками данных, так и с записями. 3.5.6. Файлы с относительной нумерацией записей Обычно система управления файлами обеспечивает нумера- цию записей, начиная с нуля или с единицы, или дает возмож- ность пользователю самостоятельно определять номер первой записи. Задав начальную точку, можно организовать доступ! к записям, начиная с первой или с любой другой записи. Воз- можен также прямой доступ к записям. Для этого нужно толь- ко задать номер необходимой записи. Файлы с относительной
Методы организации данных 93» нумерацией весьма эффективны при последовательном доступе и не очень удобны для прямого доступа. Часто доступ, который пользователь принимает за прямой, в действительности явля- ется последовательным поиском, проводимым с помощью пред- назначенного для обслуживания дисков программного обеспе- чения. Задача состоит в следующем: чтобы сразу найти запись, ЭВМ должна знать адрес той области памяти, где эта запись- расположена. Запись можно найти или с помощью последова- тельного поиска, или путем определения ее позиции, если из- вестны длины всех записей, или с помощью справочника. Для доступа с помощью справочника, в свою очередь, требуется либо найти нужный номер в справочнике, либо вычислить адрес нужной позиции в нем. Поскольку номера записей являются упорядоченными и согласуются с последовательностью хране- ния данных, наиболее эффективно при создании файла исполь- зовать для ввода, уже упорядоченные данные. Если система предусматривает автоматическую вставку пустых записей для неиспользуемых номеров, не все номера следует- использовать- в качестве ключей. Данные можно добавлять только в конец файла; замена одной записи на другую возможна, если записи имеют одинако- вые размеры и номера. Если в файл с относительной нумера* цией необходимо периодически добавлять и удалять записи^ его структуру необходимо часто перестраивать. 3.5.7. Индексированные файлы Индексированные файлы создаются в основном для органи* зации прямого доступа. Элементы в таких файлах хранятся последовательно в соответствии с ключами доступа. Элемента- ми файлов обычно являются записи, но в системах редактиро- вания текстов в качестве элементов могут выступать строки или страницы. Последовательность элементов разбивается на подпоследовательности или сегменты подходящих размеров. Такое разбиение используется специальными программами си- стемы управления файлами. В процессе создания файла для элементов строится словарь индексов или справочник. Так как записи размещаются в памяти в соответствии со значениями их ключей, в справочнике удобно хранить только ключ послед- ней записи каждого сегмента. Когда размеры файла достаточ- но велики, справочник разбивается на части, для каждой из ко- торых создается свой справочник. Этот процесс показан на рис. 3.6. Для размещения новых записей и ключей предусмотре- ны свободные области памяти. Системы редактирования текстов используют в качестве ключей номера строк; однако для индексированных файлов ключи не обязательно должны быть числовыми. Они могут
94 Глава 3 быть номерами строк, номерами счетов или уникальными иден- тификаторами любого другого типа, например фамилией или адресом. Справочники имеют древовидную структуру, каждый уровень которой состоит из .последовательного списка ключей. Для организации доступа требуется, чтобы поиск по дереву дополнялся процедурами линейного поиска — просмотром глав- ного справочника с целью определения необходимого справоч- ника более низкого уровня, просмотром этого нового справоч- ника и т. д., до тех пор. пока не будет найден нужный набор Рис. 3.6. Организация индексированного файла. записей. Эти записи просматриваются одна, за другой, пока не будет определена искомая запись. При разработке первых вариантов структуры индексирован- ных файлов1’ предусматривалась возможность их использова- ния для последовательного доступа. Поэтому справочники име- ли несколько иную структуру. Тогда в случае добавления к файлу новых записей последние помещались в конец области памяти, первоначально выделенной группе записей. Следует отметить, что при этом не было предусмотрено средств для автоматического изменения структуры файла, которое в прин- ципе необходимо при переполнении первоначально выделенных областей хранения. В современных системах управления фай- лами предусмотрено помещение новых записей в места, полно- стью соответствующие их ключам. Когда выделенная для групп *> Автор имеет в виду индексно-последовательные файлы. — Прим, перев.
Методы организации данных 95 записей область памяти исчерпана, структура файла претер- певает изменения: группа записей, относимая ранее к одному сегменту, разделяется на две и соответствующим образом изме- няются справочники. Если файл регулярно корректируется, при- чем информация удаляется так же часто, как и добавляется, и близки статистические распределения ключей удаляемых и добавляемых записей, рассмотренная выше организация весьма эффективна. Если файл постоянно пополняется записями в слу- чайном порядке, необходимо часто менять его структуру. С точки зрения программиста, индексированные файлы не зависят от аппаратного обеспечения. Программы управления файлами определенным образом подбирают величины сегмен- тов записей и размеры областей словарей, чтобы минимизиро- вать сумму перемещений головок записи-считывания и макси- мально использовать пространство дорожек. Сегментирование индексно-последовательных и других ранних версий индексиро- ванных файлов зависело от характеристик внешних устройств- и проводилось программистом. Выбор оптимальной структуры файла с виртуально-последовательной организацией осущест- вляется автоматически. В индексированных файлах организация последовательного* доступа усложняется, поскольку связь между записями осу- ществляется с помощью иерархической системы справочников.. Если записи в каждом сегменте упорядочены, последователь- ный доступ для индексированных файлов дает немного худшие результаты, чем для чисто последовательных файлов. 3.5.8. Обработка файлов Физическая организация файлов более или менее понятна программисту. Он также знает, что метод доступа оказывает влияние на эффективность программы. Но, выбирая подходя- щий тип файла и метод доступа, программист не должен забо- титься о деталях организации файла, поскольку это осущест- вляется автоматически средствами языка программирования. Запись можно добавить в файл с помощью оператора WRITE; ее можно модифицировать, применив сразу за опера- тором READ оператор REWRITE. Если запись добавляется в. файл с помощью последовательного метода доступа, она долж- на либо заменить уже использованную запись, либо быть до- бавлена в конец файла. При использовании прямого доступа запись автоматически занимает свободное место, соответствую- щее ее ключу. Записи, подвергшиеся корректировке, автомати- чески занимают свое прежнее место. При проведении корректи- ровки с помощью методов последовательного и прямого досту- па записи в корректирующем файле упорядочиваются так же, как в главном файле. В этом случае корректировку разумнее
96 Глава 3 •выполнять в пакетном режиме, а не в режиме непосредствен- ного доступа к системе. Это позволяет избежать повторных обращений в тех случаях, когда несколько запросов относится ’ к одной записи или к записям, расположенным в одном блоке. Если работа ведется в режиме непосредственного доступа, как в вопросо-ответных системах, следует использовать метод прямого доступа. Запись, которую надо уничтожить, в действительности не может быть удалена из файла до тех пор, пока файл не будет модернизирован. Ключ этой записи можно приписать другой записи, и тогда эта запись займет место первой. Запись можно особым образом пометить и оставить на прежнем месте. Обыч- но первый байт записи остается свободным и заполняется в случае удаления записи. Пустые записи маркируются аналогич- но. Записи, которые помечены таким образом, пропускаются при обработке файла, а их место можно использовать для хра- нения других записей. Программист может сгруппировать записи файла так, что количество обращений к диску станет меньше, а следовательно, уменьшится и время обработки запроса. Это осуществляется - путем объединения нескольких логических записей в одну фи- зическую. Блокирование не оказывает влияния на логические связи, существующие между данными. При блокировании за- писей учитывается оптимальный размер сегмента, объединяю-' щего группу записей с близкими ключами (разд. 3.5.7), и отыс- кивается приемлемый компромисс между временем доступа и требуемой памятью. Наиболее эффективно использовать дис- ковую память можно только в том случае, когда удается за один раз считывать или записывать информацию полностью на всю дорожку. Но для этого потребуется достаточно большой буфер, в котором будет храниться информация, помещаемая на дорожке. Кроме того, это сразу сделает блокирование зави- симым от физических характеристик дисковой памяти. Чтобы устранить эту зависимость, в блок помещают целое число ло- гических записей и оставляют свободное пространство для учетной информации. Блокирование может иногда проводиться программистом. Если в конкретной системе управления файла- ми не предусмотрено блокирование, программист может ис- пользовать массивы и группировать записи явно. Если записи снабжены ключами, для доступа к блоку следует использовать максимальный ключ записи, принадлежащей блоку. Блокиро-. вание программистом записей индексированного файла вруч- ную может потребовать от него разработки процедур доступа к справочникам или изменения размеров сегментов записей. В том случае, когда записи не сблокированы, большой объ- ем внешней памяти расходуется впустую. Если файл распола- гается на магнитной ленте, за время, необходимое для тормо-
Методы организации данных 97 жения и разгона между двумя обращениями, будет пропущен участок ленты, на котором могло бы поместиться 400—800 сим- волов. Если файл располагается на диске, размеры потерь на межблоковые промежутки будут зависеть от того, соответству- ют ли размеры блока, физическим характеристикам файла. Для каждого типа дисков существуют таблицы, в которых указаны оптимальные размеры блоков. В системах с виртуальной па- мятью размещение данных по страницам производится авто- матически'. 3.5.9. Выбор организации файлов Если данные и программы представлены в форме, понятной только для ЭВМ, их можно разместить на диске или на ленте. Правда, магнитную ленту можно переносить, а диск — нельзя. Информация, записанная на магнитную ленту на одной ЭВМ, может быть считана на другой. Если лента достаточно мала, ее можно хранить на полке или пересылать по почте. Если под руками нет небольших съемных дисков, магнитная лента наи- более удобна для хранения редко используемой информации. В том случае, когда данные или программы используются часто, дисковая память предпочтительнее памяти на лентах. Для установки лент на магнитофон требуется оператор, а для диска, работающего в режиме on-line, оператор не требуется. Кроме того, время доступа к информации на диске меньше времени доступа к информации на ленте. Файлы, размещаемые на дисках, можно организовать по-разному, используя как по- следовательный, так и прямой доступ. В отличие от ленты, за- писи на дисках могут быть модифицированы без перестройки всего файла. Некоторые операционные системы не содержат программ для блокирования записей последовательных файлов. Часто встречаются задачи, для решения которых наиболее удобна по- следовательная организация файла. Если файл стабилен, или, что то же самое, не подвергается частым перестройкам (т. е. общее число обращений к нему существенно больше числа за- просов, связанных с добавлением или уничтожением записей),- если записи и запросы могут быть упорядочены и, наконец, если каждый раз, когда файл обрабатывается, используются многие его записи, без сомнения, нужно создавать последова- тельный файл. Эта ситуация характерна для таких приложе- ний, как обработка контрольных счетов, ведомостей об успевае- мости студентов и отчетов о товарных запасах. Но есть приложения (например, получение информации из личных дел студентов и изменение в режиме непосредственного Доступа содержимого банковских расчетных книжек), которые 7-399
98 Глава 3 связаны с организацией доступа к случайно выбранным запи- сям файла с достаточно стабильной структурой. В данном слу- чае последовательная организация была бы неэффективной. Наиболее целесообразно для этих целей использовать либо ин- дексно-последовательный файл, либо файл с относительной ну- мерацией. Если все-таки выбирается последовательный доступ, аргументы в его пользу должны быть весомее аргументов в пользу прямого доступа. Если обработка ведомостей об успе- ваемости студентов, проводимая регулярно для всех студентов, важнее, чем внесение в ведомости локальных изменений и за- просов, касающихся личных дел отдельных студентов, предпоч- тительнее последовательная организация файлов. Кроме того, все сообщения, касающиеся отдельных записей файла, можно временно задержать, отсортировать и в дальнейшем эффектив- но работать с последовательным файлом. Если регулярное по- явление новых курсов и отказ от старых, а также запросы, ка- сающиеся личных дел отдельных студентов, преобладают над требованиями последовательной обработки, предпочтительнее прямая организация файлов. В системах резервирования авиационных билетов и в систе- мах регистрации пациентов в больницах данные хранятся в файлах, имеющих весьма нестабильную структуру. Данные за- прашиваются непрерывно и в случайном порядке; необходимо часто удалять и добавлять записи. Поэтому удобно использо- вать файлы с прямым доступом. Первоначально эти файлы должны содержать большое число пустых записей. Если можно присваивать новые ключи старым записям, пригодны также файлы с относительной нумерацией записей и файлы с индекс- но-последовательной организацией. Если повторное присваива- ние ключей запрещено, индексно-последовательный файл будет быстро переполняться и время доступа к нему значительно уве- личится. При работе с индексно-последовательными файлами и файлами с относительной нумерацией необходимо учитывать следующие факторы: тип ключей, диапазон изменения значений ключей, возможность повторного назначения ключей записям, стабильность структуры файла. 3.6. Документирование данных В документацию, относящуюся к организации данных, вхо- дят схемы и диаграммы, характеризующие отношения между элементами потоков данных и программами, между элемента- ми потоков данных'и областями хранения данных и различные уровни структурирования данных. Функциональные схемы используются для изображения по- токов внешних по отношению к программам данных, а иерар- хические схемы модулей—для изображения внутренних по-
Методы организации данных 99 токов данных. Сведения о данных, которые не локализованы в каком-то одном модуле, а участвуют в обмене информацией между модулями или программами (такие, как сведения о дан- ных, хранящихся в разделяемых областях памяти, о списках параметров и о файлах), представляются в форме таблиц по- токов данных, словарей данных и схем организации данных. Схемы организации данных .могут иметь форму графических схем, или графов отношений между данными. Описание всех уровней объединения данных в агрегаты также должно найти отражение в документации. Следует упомянуть в документации о методах хранения данных. Если в системе есть файлы, их описание должно включать информацию о файловых метках, а в остальном они описываются так же, как разделяемые обла- сти памяти и параметры. Словарь данных, типа представленного в табл. 3.3, не заме- нит полное описание данных в системе, поскольку он предназ- начен для описания только представления данных и не отра- жает их логическую и физическую организацию. Информация о логической и физической организации обязательно должна быть включена в документацию. Кроме словаря данных в до- кументацию должны входить: • схема и форма организации внешних данных; • указание тех компонентов аппаратного и программного обеспечений системы, которые являются источниками данных; • определение тех частей системы, которые обращаются к данным; • определение тех частей системы, которые изменяют дан- ные; • описание всех уровней организации сложных структур данных; • описание памяти, необходимой для размещения данных, включая размер файла, блокирование, метки и т. п. В общесистемную документацию обязательно должна быть включена документация, в которой содержится информация, во-первых, о потоках данных, циркулирующих между функцио- нальными блоками и областями хранения данных, во-вторых* о данных, которыми обмениваются программы и области хра- нения (эту информацию также удобно представлять в виде сло- варя данных), и, в-третьих, об используемых ресурсах. Про- граммная документация также будет содержать* информацию, относящуюся к документации данных, а именно описание пото- ков данных между программными модулями. Документация модулей будет, в свою очередь, включать аналогичную инфор- мацию, связанную с элементами данных, определенными внут- ри модулей. 7*
100 Глава 3 3.7. Упражнения 1. Используя язык, в котором есть указатели и средства для определения различных типов данных (например ПЛ/1, Паскаль или Ада), опишите струк- туру, приведенную на рис. 3.1. 2. Определите логическую организацию данных для системы каталогиза- ции книг в библиотеке из упр. 1 гл. 1. 3. Определите логическую организацию данных для системы регистрация заявок на пользование теннисными кортами из упр. 2 гл. 1. 4. Разработайте словарь данных для системы каталогизации книг (см» упр. 1 гл. 1 и упр. 2). 5. Разработайте словарь данных для системы регистрации заявок на поль- зование теннисными кортами (см. упр. 2 гл. 1 и упр. 3). 6. Чем отличается последовательный файл от очереди? Какие операции можно выполнять с файлом и с очередью? Дайте формальные определения последовательного файла и очереди. 7. Чем отличается файл с прямым доступом от множества? Какие опера- ции можно выполнять с файлом и множеством? Дайте формальные определе- ния файла с прямым доступом и множества. 8. Предположим, что в процессе регистрации избирателя клерк смотри? на карту и вводит в ЭВМ номер квартала, где живет избиратель. Предполо- жим также, что, когда границы избирательного округа изменяются, данные о новых границах вводятся в ЭВМ, причем каждая точка изгиба границы из- бирательного округа задается путем указания номера квартала. Квартал всег- да целиком принадлежит одному округу. Разработайте структуру данных, ко- торую можно использовать для определения принадлежности квартала окру- гу, когда границы округов меняются. 9. Предположим, что всем студентам университета присвоены номера*. Каталог семестровых курсов содержит список курсов, упорядоченный по ка- федрам и номерам курсов. Каждый студент должен посещать от 1 до 10 кур* сов. Количество студентов, желающих прослушать один и тот же курс, может быть любым. Для того чтобы прослушать определенный курс, студент должен зарегистрироваться. Разработайте описания структур данных для этой задачи на логическом уровне и на уровне представления данных, используя любой язык программирования с блочной структурой. Кроме того, опишите предпо- лагаемую физическую организацию данных.
Глава 4 ПРОЕКТИРОВАНИЕ ПРОГРАММ Процессы проектирования всей системы и отдельных про- грамм во многом подобны друг другу. Но в отличие от системы каждая программа имеет единственное функциональное наз- начение и не может быть разбита на части, используемые в раз- личные моменты времени. Сходство программы с системой за- ключается в наличии внешних и внутренних потоков данных, областей хранения данных, а также в возможности разделения ее на независимые модули, которые можно разрабатывать и на- страивать отдельно. В последние десять лет общее признание получил модуль- ный принцип построения программ. Под модуляризацией пони- мается разделение программы на части по некоторым уста- новленным правилам. Этими частями могут быть программные секции (в Коболе), внутренние процедуры (в ПЛ/1 и Паскале) или внешние процедуры (в Фортране, Коболе и ПЛ/1). При- кладные программы чаще всего слишком сложны, чтобы быть написанными как единое целое или разработанными одним программистом. Поэтому сложность программ долгов время вызывала трудности при проектировании систем и программно- го обеспечения. Программы стали слишком велики, чтобы их можно было представить во всех подробностях как единое це- лое и поместить в памяти ЭВМ целиком. Когда для размещения больших программ в па(мяти маши- ны стали применяться структуры с перекрытиями (оверлейные структуры), эти программы пришлось разбивать на модули. С появлением ЭВМ со страничной (виртуальной) памятью управление оверлейными структурами осуществляется автома- тически, но эффективность распределения программы по стра- ницам зависит от выбираемого программистом способа разбие- ния ее на модули. Программы разбиваются на модули для того, чтобы: • упростить их разработку и реализацию; • Ъблегчить чтение программ; • упростить их настройку и модификацию; • облегчить работу с данными, имеющими сложную струк- туру; • избежать чрезмерной детализации алгоритмов;
102 Глава 4 • обеспечить более выгодное размещение программ в памя- ти ЭВМ. Методы проектирования программ, основанные на модуль ном принципе, делятся на три группы: методы нисходящего проектирования, методы расширения ядра и методы восходя- щего проектирования. На практике обычно применяются раз- личные сочетания этих методов. При решении задач системного и прикладного программиро- вания могут возникнуть затруднения нескольких типов. Одно из них связано с рассмотренными выше проблемами организа- ции начала обработки групп записей и формирования заголов- ков и окончаний в соответствии с переходом обработки от од- ной группы записей к другой. Эта ситуация возможна не толь- ко при генерации экономических отчетов. Она появляется при решении задач анализа статистических данных, при обработке программ в режиме пакетной компиляции, а также в некоторых других случаях. В потоке данных, представляющих собой нане- сенные на перфокарты программы, имеются карты определения задания и окончания задания, которые предназначены для уп- равления переходом обработки от одной группы записей к дру- гой, выполняемого монитором управления пакетом. Каждая программа при печати должна быть оформлена в виде отдель- ных страниц, а заголовок и конец ее распечатки должны со- держать общую информацию о соответствующем задании. Другое затруднение возникает при проверке правильности вводимых и определяемых внутри программ данных. Компиля- тор обрабатывает предложения языка с помощью проверки лексических единиц и синтаксиса отдельных предложений, а также с помощью общей структуры транслируемой программы. Программа сопровождения оценивает информацию в каждом очередном сообщении, определяет порядок сообщений, возмож- ные общие переменные, а также проверяет, соответствуют ли сообщения записям главного файла. Работа компилятора по проверке исходных предложений в общем случае сложнее, чем проверка файла сообщений и главного файла, но многие аспек- ты этих процессов практически одинаковы. В одном только слу- чае работу компилятора можно считать довольно простой, ког- да исходные предложения печатаются независимо от того, пра- вильны они или нет. Затем, если обнаружена ошибка, на пе-. чать выдается соответствующее сообщение. Обновляемое сооб- щение не печатается и не обрабатывается до тех пор, пока не будут проверены и помещены в набор правильные или непра- вильные сообщения, поступающие на корректировку. Обработку всей группы сообщений при некоторых обстоя- тельствах необходимо откладывать до тех пор, пока не будет установлено, что все сообщения правильны. Такой способ об- работки характерен для работы компилятора, когда программа
Проектирование программ 103 Не начинает выполняться до окончания проверки правильности исходного текста. В некоторых компиляторах печать сообщений об ошибках осуществляется после печати всего исходного тек- ста. Указанный способ печати ошибок возможен, если непра- вильные сообщения сначала только накапливаются, а затем отдельно печатаются. В любом методе проектирования программ должны преду- сматриваться возможности для управления программой при пе- реходе обработки от одной группы записей к другой, для про- верки правильности данных, а также для одновременной обра- ботки данных, поступающих в разное время. Кроме того, каж- дый метод должен объединять средства управления физической организацией некоторых данных (например, распределением информации по страницам) с логической организацией обра- ботки. 4.1. Метод нисходящего проектирования Метод нисходящего проектирования подобен методу получе- ния детального изображения из более общего вида с помощью телескопического увеличения. На начальном шаге формируется предложение, описывающее функцию всей программы. Затем определяются ее подфункции. Эта процедура является рекур- сивной, т. е., следуя ей, каждая из подфункций может расчле- няться до тех пор, пока ее составные части не будут оконча- тельно уточнены. Метод нисходящего проектирования, иногда называемый функциональной декомпозицией, основан на двух стратегиях: пошаговом уточнении, разработанном Е. Дейк- строй, и анализе сообщений, базирующемся на работах Йодана, Константайна и Мейерса. Эти стратегии отличаются способами определения начальных спецификаций, методами, используемы- ми при разбиении задачи на части, и правилами записи. 4.1.1. Пошаговое уточнение При пошаговом уточнении на каждом следующем этапе де- композиции определяются программы очередного, более низко- го уровня. Для этого используются процедурные языки про- граммирования. Во-первых, задается заголовок программы, соответствующий ее главной функции, например procedure обработка-пакетов Затем определяются основные шаги обработки пакетов инфор- мации: procedure обработка-пакетов; сортировать-записи-по-управляющим-полям, ятделить-правильные-записи-оТ'Неправильных-и-обработать endprocedure.
104 Глава 4 С помощью расширения шагов процедуры выполняется даль- нейшее уточнение программы. На некоторых шагах уточнения появляется необходимость в использовании управляющих структур: procedure обработка-пакетов; г сортировать-записи-по-управляющим-полям; взять-первую-запись while не-конец (входной-файл) do взять-правильную-управляющую-группу обработать-группу-записей endwhile; обработать-неправильные-управляющие-группы endprocedure. Операция сортировки не разбивается на шаги, так как для ее выполнения часто применяется имеющаяся в системе про- грамма сортировки. Если же такой подпрограммы нет, ее сле- дует написать и оформить в виде независимого модуля. Созда- ние подпрограммы сортировки включает выбор алгоритма. Но выбранный метод сортировки должен быть изолирован от ос- тальной части программы. Однако не все расширения можно откладывать на более поздний срок. Допустим, что отсортиро- ванная информация помещается в файл с последовательным доступом (вполне естественное предположение для рассматри- ваемых условий). Если метод доступа к данным на этой стадии проектирования неизвестен, можно использовать какое-либо нейтральное выражение, такое, как while еще-есть-данные do. На следующей стадии уточнения можно приостановить опре- деление каких-либо других предложений, выделяя их в вызы- ваемые функции и процедуры в том случае, если они функ- ционально независимы от основной процедуры обработки или если необходимо отложить их детализацию: procedure обработка-пакетов; сортировать-записи-по-управляющим-полям; взять-первую-запись; while не-конец (входной-файл) do repeat взять-управляющую-группу until найдена-правильная-группа или конец-файла (входной-файл) обработать-заголовок-группы; » обработать-записи-группы; обработать-окончание-группы _ end while; обработать-неправильные-управляющие-группы endprocedure. Единую группу записей можно получить, сохраняя каждую из записей вплоть до перехода к обработке другой группы, ко-
Проектирование программ 105 торий определяется с помощью единицы данных, указывающей На смену группы. Если в группе найдена неправильная запись, оставшаяся часть группы не обрабатывается по установленным правилам, а только проверяется на правильность. Для некор- ректной группы сохраняются записи для печати ошибок или сообщения об ошибках, но в последнем случае записи уничто- жаются. Если установлена корректность группы, ее записи об- рабатываются установленным порядком. Конец файла указы- вает на момент перехода обработки для всех управляющих по- лей: procedure обработка-пакетов; сортировка-записей-по управляющим-полям; взять-первую-запись; while не-конец (входной-файл) do repeat инициал изировать-управляющее-поле; принять-корректность-группы; repeat проверить-запись; 1 сохранить запись; взять-следующую-запись; until неправильная-запись или переход-к-другой-группе; if неправильная-запись then группа-некорректна; передать-сохраненные-записи-в-файл-ошибок; обработать-остаток-некорректной-группы; until установлена-корректность-группы или конец-файла (входной-файл)} обработать заголовок-группы; взять-первую-сохраняемую-запись-группы; while не-конец-группы do обработать-запись; взять-следующую-сохраненную-запись; endwhile; обработать -окончание-группы endwhile; взять-первое-сообщение-об-ошибке; while не-конец-файла (файл-ошибок) do. выдать-сообщение-об-ошибке; взять следующее-сообщение-об-ошибке; endwhile endprocedure. Подобная программа может быть использована для выпол- нения проверок в главном файле, составленном из контроль- ных оценок, каждая из которых записывается снова только в том случае, если все соответствующие ей сообщения коррект- ны. Такая же программа может быть использована для пакет- ной компиляции отдельных модулей. Выполнение этих 'модулей
106 Глава 4 возможно только при условии, что в соответствующих исход- ных текстах нет синтаксических ошибок. В случае пакетного компилятора записи должны быть за- 1 ранее отсортированы, даже если порядок следования входных записей указывается номерами строк, предусмотренными син- таксисом языка программирования. Для проектирования программ методом пошагового уточне- ния применяется способ кодирования с помощью псевдокода и управляющих конструкций структурного программирования. Взаимное расположение записей должно обеспечивать чита- бельность всей программы. Обычно используется псевдокод, подобный структурированному языку программирования. По- этому преобразование в программный код выполняется на лю- бом этапе проектирования непосредственно. Разбиение на модули осуществляется эвристическим спо- собом. На каждом этапе проектирования по возможности не уточняются операции с данными (эти вопросы откладываются на более поздние сроки). Но выбор управляющих конструкций нельзя откладывать, так как на последующих этапах труднее изменить ранее выбранные конструкции. Нельзя сказать, что этот метод полностью независим от языка. Так, например, ре- шение об использовании первоначального чтения из файла за- висит от определяемого языком программирования способа об- работки условия конца файла. Кроме того, некоторые конструк- ции циклов в одних языках реализуются легче, чем в других. Приведенный выше пример процедуры пошагового уточне- ния иллюстрирует основные принципы данного метода проек- тйрованпя программ. Но дальнейшее уточнение нельзя прово- дить без рассмотрения конкретных прикладных особенностей программы: без распределения по страницам, если целью об- работки является выдача отчета; без определения главного файла, если объектом проектирования служит программа со- провождения главного файла или программа, формирующая запросы к этому файлу; без детализации обработки ошибок. На этапе, когда принимается решение о прекращении дальней- шего уточнения, оставшиеся неопределенными подфункции ста- новятся вызываемыми функциями или процедурами, а проек- тируемый модуль — управляющим модулем. Преимущество метода пошагового уточнения заключается в том, что основное внимание при его использовании обращает- ся на проектирование корректной программы, а не только на детальное понимание задачи. Поскольку первый этап проектирования корректен, а каж- дый последующий этап является уточнением предыдущего лишь с небольшими изменениями, то легко может быть выполнена проверка корректности процесса разработки на всех этапах. Недостаток этого метода состоит в том, что на поздних стадиях
Проектирование программ 107 проектирования может обнаружиться необходимость в струк- турных изменениях, требующих пересмотра более ранних кон- струкций. НЕ ДЕТАЛИЗИРУЙТЕ РАНЬШЕ ВРЕМЕНИ ОПЕРАЦИИ С ДАННЫМИ 4.1.2. Анализ сообщений Анализ сообщений основывается на анализе потока данных, обрабатываемых программой. Этот вид анализа в большей степени относится к процессам передачи и преобразования от- дельных записей запросов или других входных элементов, чем к операциям управления потоком данных. На диаграмме, при- веденной на рис. 4.1, представлены потоки информации и обра- батывающие их процессы, являющиеся внутренними для про- граммы обработки пакетов данных. Шаги, выполняемые при идентификации потоков данных и процессов, аналогичны соот- ветствующим шагам в проектировании систем. Первоначальный поток данных разбивается на три потока: первый содержит Рис. 4.1. Потоки данных для программы пакетной обработки. непреобразованные входные данные, а последний — только вы- ходную информацию. Границы, разделяющие эти потоки, пока- заны на рис. 4.1 в виде штриховых линий, которые делят схему на три части. Данные, подлежащие обработке с помощью про- цесса, обозначенного кружком, могут не включать всю исход- ную информацию, но эту информацию еще можно считать частью входных данных. Кружок, представляющий обработку, обозначает процессы кодирования, декодирования, расчета, а также другие преобразования данных. Результаты, выходящие
108 Глава 4 из кружков, представляющих обработку, являются еще не от- редактированными и не отформатированными данными и могут содержать некоторые результаты, которые впоследствии унич- тожаются как неправильные, но при этом могут считаться вы- ходными. Три части программы принято называть соответственно ис- током, преобразователем и стоком. Преобразователь представ- ляет собой основную часть программы, тогда как исток и сток выполняют функции управления входным и выходным потока- ми данных. На рис. 4.2 приведена иерархическая структура мо- дулей, соответствующая рассмотренному разбиению диаграм- зователь Рис. 4.2. Анализ сообщений. в — анализ исток-преобразователь-сток; б — схема иерархии модулей. мы. Каждый из прямоугольников обозначает действительный программный модуль и может быть реализован в зависимости от его размера и сложности как подпрограмма, внутренняя про- цедура или только как секция программы. Линии указывают связи по управлению, а также изображают отношения типа вы- зывающий-вызываемый. Метод разбиения на исток, преобразователь и сток, рекур- сивно используемый на отдельных ветвях древовидной структу- ры модулей, представляет собой процесс декомпозиции про- граммы, в результате которого получаются модули нижнего уровня. Не все модули подвергаются разбиению на три части более низкого уровня. Результат декомпозиции модуля-стока должен содержать сток, модуля-преобразователя — преобразо- ватель, а модуля-истока — исток. Однако в модуле-истоке, на- иример, не обязательно должны содержаться части, выполняю- щие функции преобразователя или стока. Вызывающий модуль действует как главный сток для данных модуля-истока и как главный исток для модуля-стока. Оба они будут истоком и
Проектирование программ 109 стоком для модуля-преобразователя. Если какой-либо преобра- зователь использует информацию от нескольких истоков, один из них является вызывающей программой, а другие — подмоду- лями истока. На рис. 4.3 приведена иерархическая структура модулей для программы обработки пакетов. Поскольку объектом обработки а б Рис. 4.3. Анализ сообщений при проектировании программы пакетной обра- ботки. а — декомпозиция первого уровня; б — расширенная декомпозиция. является пакет, структура отображает процесс обработки одно- го пакета. Если все правильные записи пакета (независимо от того, содержатся ли среди них неправильные записи) подлежат обработке, объектом обработки была бы запись. Этап сортиров- ки информации и этап выдачи протокола выходят за рамки этой схемы, так как их выполнение не влияет на процесс обра- ботки групп записей и результаты этих этапов несущественны для обработки пакетов корректных данных. Программы, реали- зующие эти этапы, связаны с рассматриваемой здесь програм- мой только с помощью файлов, поэтому их правильнее считать •отдельными частями системы, чем подпрограммами. Если
по Глава 4 иерархическую структуру дополнить еще одниМ уровнем, сорти- ровка должна стать истоком, структура, приведенная на рис. 4.3,6, — преобразователем, а выдача протокола ошибок — стоком. Не все модули структуры используются для обработки пол- ного объема считанной информации. Каждый модуль при ин- формационном обмене использует определенную часть данных. Описание иерархической структуры должно содержать таблицу взаимодействия модулей, показывающую передачу данных между различными модулями. В этой таблице должны быть определены все способы информационного обмена, задаваемые как с помощью явного вызова процедур, так и через общие и внешние накопители. Если элементы данных, заданные на неко- тором уровне иерархии, явным образом используются на том же уровне, в таблице отмечается их передача по всем управ- ляющим линиям этого уровня. В табл. 4.1 указаны информа- ционные связи для структуры, приведенной на рис. 4.3. С ее помощью могут быть определены порядок и условия обработки данных. Таблица 4.1. Таблица межмодульных связей Модуль Вход Выход 1 2 Правильный-пакет 3 Результаты-пакета 4 5 Данные-пакета 6 Неправильный-пакет 7 Правильная запись 8 Правильная запись 9 Результаты-записи 10 Правильные-результаты Правильный-пакет, Конец- файла Результаты-пакета Данные-пакета, Конец-фай- ла Правильный-пакет, Непра- вильный-пакет Результаты-записи Правильные-результаты Табл. 4.1 отражает только те данные, которые передаются каждому модулю в момент его вызова. Поскольку главный уп- равляющий модуль не может быть вызван другими, то в табли- це не показана ни его входная, ни его выходная информация. Элементы данных, используемые главным модулем, изображе- ны как выходные для модулей, которые он вызывает. Выход- ные данные главного модуля отображаются как входные для вызываемых им модулей. Итак, модуль-исток имеет выходные данные, сток — входные данные, а преобразователь — те и дру- гие.
Проектирование программ 111 Стратегия анализа сообщений включает организацию как взаимодействия между модулями, так и функционирования от- дельных модулей. Корректность структурного разбиения осно- вывается на внутренней коммуникативной способности каждого отдельного модуля и различных их (сочетаний. 4.1.3. Связность модуля Связность модуля определяется как мера независимости его частей. Чем выше связность модуля, тем лучше результат про- ектирования. Для обозначения связности используется также понятие силы связности модуля. Типы связности модулей при- ведены в таблице: Связность Сила связности Функциональная 10 (сильная связность) Последовательная 9 Коммуникативная 7 Процедурная 5 Временная 3 Логическая 1 По совпадению 0 (слабая связность) Названия и оценки связности у разных авторов различаются, но незначительно. Модуль с функциональной связностью не может быть разбит на два других модуля, имеющих связность того же типа. Мо- дуль управления обработкой пакетов имеет функциональную связность. Если бы он включал начальную сортировку инфор- мации и конечную выдачу протокола об ошибках, то имел бы последовательную связность. Модуль, который может быть раз- бит только на исток, преобразователь и сток, также имеет функциональную связность. Он выполняет единственную функ- цию. Такой модуль реализуется последовательностью операций в виде единого цикла. Модуль, имеющий последовательную связность, может быть разбит на последовательные части, выполняющие независимые функции, но совместно реализующие единственную функцию. Если один и тот же модуль используется для оценки, а затем для обработки данных, то он имеет последовательную связ- ность. Модуль с последовательной связностью реализуется как последовательность операций или последовательность циклов. Если модуль составлен из независимых модулей, разделяю- щих структуру данных, он имеет коммуникативную связность. Общая структура данных является основой его организации
112 Глава 4 как единого модуля. Если модуль спроектирован так, чтобы - упростить работу со сложной структурой данных, изолировать эту структуру, он имеет коммуникативную связность. Такой мо- | дуль предназначен для выполнения нескольких различных и не- зависимо используемых функций,, таких, как запоминание и ' поиск данных. Если же модуль разработан так, чтобы изоли- ровать выбор алгоритма, он имеет функциональную связность. Такой модуль может обрабатывать данные с изолированной структурой, но при вызове считается, что он выполняет един- ственную функцию. Модули высшего уровня иерархической структуры програм- мы должны иметь функциональную или последовательную связность. Для модулей обслуживания предпочтительнее ком- муникативная связность. Если модули имеют процедурную, вре- менную, логическую или случайную связность, это свидетельст- вует о недостаточно продуманном их планировании. Модифика- ция уже существующей программы, когда модули разбиваются на части, часто приводит к этим типам связности. Процедурная связность обнаруживается в модуле, управ- ляющие конструкции которого организованы так, как изобра- ; жены на структурной схеме .программы. Такая структура моду- ля может возникнуть при расчленении длинной программы на ’ части в соответствии с передачами управления, но без опреде- ления какого-либо функционального базиса при выборе разде- лительных точек. Процедурная связность может появиться при «. группировании альтернативных частей программы. Если ком- пилятор обрабатывает различные типы предложений, например объявления, процедуры, присваивания и управляющие конст- рукции в различных секциях одного и того же модуля, этот мо- дуль имеет единственное функциональное назначение. Если для . уменьшения размеров он делится на два независимых модуля (один предназначен для обработки объявлений и процедур, а другой—для выполнения присваивания и управляющих кон- струкций), каждый из них имеет процедурную связность. Луч- шим решением следует считать такое, при котором исходный модуль вызывает четыре других, при этом каждый из них вы- полняет различные типы предложений. Модуль, содержащий части функционально не связанные, но необходимые в один и тот же момент обработки, имеет вре-. менную связность или связность по классу. Связность такого- типа имеет место в тех случаях, когда все множество требуе- мых в момент входа в программу функций выполняется неза- висимым модулем активации. Вместо использования одного не- зависимого модуля для активации в начале и другого для пе- ревода в пассивное состояние в конце программы функции следует распределить между другими модулями. Активацию переменной или файла необходимо выполнять непосредственно»
Проектирование программ 11& перед первой ссылкой к этой величине или файлу. Перевод же в пассивное состояние следует производить сразу после ссыл- ки. В результате кроме увеличения меры общей связности про- граммы устраняется возможность появления некоторых ошибок, файлы, например, должны быть открыты столько времени^ сколько требуется для их обработки. Если файлы открыты в те- чение предельно короткого времени, они менее подвержены си- стемным сбоям. Если в начале программы необходимо выпол- нять большое количество операций активации, выделение этих, операций в отдельный модуль менее желательно, чем включе- ние в управляющий модуль. Если в модуле объединены операторы только по признаку их функционального подобия (например, все они предназначе- ны для проверки правильности данных или для управления операциями обмена с внешними носителями), а для его на- стройки применяется алгоритм переключения, такой модуль имеет логическую связность, поскольку его части ничем не свя- заны, а имеют лишь небольшое сходство между собой. Мо- дуль, состоящий из разнообразных подпрограмм обработки ошибок, имеет логическую связность. Однако модуль, предна- значенный для записи разнообразных сообщений об ошибках,, имеет коммуникативную связность, если из файла сообщений об ошибках с его помощью может быть получена вся выходная информация. Если операторы модуля объединяются произвольным обра- зом, например когда необходимо указать их непосредственное- размещение в области памяти, такой модуль имеет связность. по совпадению. Три наиболее слабых типа связности (времен- ная, логическая и по совпадению) возникают в результате не- правильного планирования, обусловленного переделкой про- граммных модулей после их реализации. ДОБИВАЙТЕСЬ ФУНКЦИОНАЛЬНОЙ СВЯЗНОСТИ ПРОЕКТИРУЕМЫХ МОДУЛЕЙ 4.1.4. Сцепление модулей Сцепление модулей представляет собой меру относительной независимости модулей, которая определяет их читабельность и сохранность. Независимые модули могут быть модифицирова- ны без переделки каких-либо других модулей. Слабое сцепле- ние более желательно, так как это означает высокий уровень их независимости. Модули являются полностью независимыми^ «ели каждый из них не содержит о другом никакой информа- ции. Чем больше информации о других модулях используется в них, тем менее они независимы и тем теснее сцеплены. Эта информация появляется в результате перекрестного использо- 8-399
114 Глава 4 вания имен модулей, назначения вызываемых последовательно- стей, неявного применения входных и выходных кодов, а также из данных, определяемых структурами общих областей памяти. Чем очевиднее взаимодействие двух связанных друг с другом модулей, тем проще определить необходимую корректировку одного модуля, зависящую от изменений, производимых в дру- гом. Большая изоляция и непосредственное взаимодействие модулей приводит к трудностям в определении границ измене- ний одного модуля, которые устраняли бы неизбежные ошибки в другом. Ниже в таблице приведены меры сцепления моду- лей: Сцепление Степень сцепления модулей Независимое По данным По образцу По общей области По управлению По внешним ссылкам По кодам 0 (слабое сцепление) 1 3 4 5 7 9 (сильное сцепление) Так же как и для связности модулей, у различных авторов встречаются расхождения в оценке указанных в таблице коэф- фициентов. Модули сцеплены по данным, если они имеют общие еди- ницы, которые передаются от одного к другому как параметры, представляющие собой простые элементы данных, то есть вызы- вающий модуль «знает» только имя вызываемого модуля, а также типы и значения некоторых его переменных. Еще мень- ше знает вызываемый модуль о вызывающей программе. Изме- нения в структуре данных в одном из модулей не влияют на другой. Кроме того, модули с этим типом сцепления не имеют общих областей данных или неявных параметров. Меньшая степень сцепления возможна только в том случае, если модули не вызывают друг друга или не обрабатывают одну и ту же информацию. Модули сцеплены по образцу, если параметры содержат структуры данных. Недостатком такого сцепления является то, что оба модуля должны знать о внутренней структуре данных. Если программист, сопровождающий программу, модифициру- ет структуру данных в одном из модулей, он вынужден изме- нить структуру данных также и в другом. Поэтому вероятность появления ошибок, возникающих при кодировании и сопровож- дении программ, здесь больше. Модули сцеплены по общей области, если они разделяют одну и ту же глобальную структуру данных. В этом случае воз-
Проектирование программ Н5> мощностей для появления ошибок при модификации структуры данных в одном модуле намного больше. По изменениям, про- изводимым в объявленных параметрах, сразу можно опреде- лить модули, на которые эти изменения повлияют. Если моди- фицируются неявно заданные параметры, определить модули, нуждающиеся в корректировке, труднее, если только не су- ществует документации, отражающей использование модулями общих областей. Модули имеют4 сцепление по управлению, если какой-либо ,из них управляет решениями внутри другого с помощью пере- дачи флагов, переключателей или кодов, предназначенных для выполнения функций управления, то есть один из модулей знает о внутренних функциях другого. Возвращение флага со- стояния как явной переменной не означает сцепление по управ- лению. Если модуль передает информацию о самом себе или об обработанных данных, это не всегда служит проявлением сцепления по управлению. Передача флага конца файла позво- ляет решить вопрос о возможности обработки этого файла. Установка флага, указывающего, какой именно способ доступа используется в операции обмена (последовательный или пря- мой), означает, что осуществляется сцепление по управлению, когда это влияет на модули обмена. Если модуль имеет логи- ческую связность и при его вызове используется переключа- тель, указывающий на требующуюся функцию, вызываемый и вызывающий модули сцеплены по управлению. Говорят, что модуль предсказуем, если его работа обус- ловлена только одними параметрами. При этом среда вычисли- тельной машины «а функционирование данного модуля не влияет. Для того чтобы модуль был предсказуем, с другими модулями он должен быть сцеплен или по данным, или по об- разцу, или, наконец, по управлению. Он не может иметь досту- па к каким-либо внешним данным или общим областям па- мяти. Модуль сцеплен по внешним ссылкам, если у него есть дос- туп к данным в другом модуле через внешнюю точку входа. Таким путем осуществляется неявное управление функциони- рованием другого модуля. Сцепление такого типа возникает, например, при использовании ПЛ/1 или Паскаля, когда внут- ренние процедуры оперируют с глобальными переменными. Модули имеют сцепление по кодам, если коды их команд перемежаются друг с другом. Это сцепление возникает, когда Для одного из модулей доступны внутренние области другого без обращения к его точкам входа, когда два модуля исполь- зуют общий участок памяти с командами. Оно возникает пре- имущественно в тех случаях, когда модули проектируются как отдельные подпрограммы, путь через которые начинается в раз- личных точках входа, но приводит к общему сегменту кодов. 8*
ai6 Глава 4 Например, одна и та же программа-функция может быть на> писана для вычисления синуса и косинуса. Между этими триго- нометрическими функциями имеется следующее соотношение: sin(n/2—x)=cos х для 0^х^л/2. Путь через точки входа SIN и COS ведет к общему участку команд. Модули, неявно вызывающие друг друга, сцеплены между собой. Если один модуль косвенно обращается к другому и связь между ними осуществляется с помощью передачи пара- метров через промежуточные модули или посредством исполь- зования общей структуры данных, между ними существует сцепление. Модули, не вызывающие друг друга и не использую- щие общих данных, не сцеплены и являются полностью неза- висимыми друг от друга. Правильное применение анализа сообщений должно обеспе- чить сильную связность модулей. Сцепление модулей зависит от спроектированной структуры данных и способов взаимо- действия между модулями. ИСПОЛЬЗУЙТЕ ПРОСТЫЕ ПАРАМЕТРЫ НЕ ПРИМЕНЯЙТЕ ГЛОБАЛЬНЫХ ДАННЫХ Идентификация истока, преобразователя и стока представ- ляет собой в основном анализ способов обработки отдельных фрагментов информации (например, отдельных запросов, паке- тов или записей), которые на данном этапе не могут быть больше расчленены. Поэтому в первую очередь этот метод предназначен для структуризации программ обработки инфор- мации. Если структуры данных имеют большие размеры (на- пример, файлы и протоколы), управление ими осуществляется на уровне детализации данных для модулей истока и стока. Об- работка заголовков и управляющих точек является вспомога- тельным процессом для нормального процесса ввода-вывода и может считаться видом редактирования. Структурная схема анализа сообщений с учетом переходов к обработке следующей группы записи показана на рис. 4.4. Единицей обработки в этом случае является не отдельный пакет, а отдельная запись- Информация о переходе к обработке другой группы использу- ется во входном модуле для проверки пакета или счетчика за- писей. В выходных модулях аналогичная информация служит для управления обработкой заголовков и окончаний, а также для перевода в пассивное состояние данных пакета. Структуризация по типу исток — преобразователь — сто* (И—П—С) обеспечивает реализацию таких функций, как про- верка значений данных, анализ данных о смене группы, а так- же позволяет осуществлять одновременную обработку всей группы после приема всех ее данных, включая указанные функ- ции в модули истока и стока. Только правильные единицы да»’ ных передаются вызывающим модулям. Правильными едини-
Проектирование программ Н7 цами данных могут быть записи или пакеты. Модуль нижнего уровня иерархии изолирует от модулей более высоких уровней организацию памяти для хранения правильного пакета и струк- туру файла ошибок. Модули, указанные в иерархической структуре, могут быть как независимыми программами, так и подпрограммами. В структурном языке блочного типа они могут быть блоками Рис. 4.4. Проектирование методом анализа сообщений с учетом переходов об- работки во время смены групп записей. или внутренними процедурами. В языке Кобол возможно по- строение этих модулей в виде секций и параграфов. Форма, ис- пользуемая для реализации модулей, зависит от возможностей языка, а также от сложности всей программы. Однако при включении модуля в виде одного блока или параграфа в дру- гой, внешний по отношению к нему модуль следует учитывать возможные изменения меры связности внешнего модуля. 4.2. Метод расширения ядра Метод расширения ядра отличается от способа нисходящего проектирования: в нем больше внимания вначале уделяется вы- явлению множества вспомогательных функций, а не определе- нию функции всей программы в целом. Эти функции можно получить, применяя методы проектирования структур данных,
118 Глава 4 которые используются при иерархическом модульном проекти- ровании, разработанном Джексоном, или определяя области хранения данных с последующим анализом связанных с ними функциональных единиц (как в методе определения специфи- каций модуля, разработанном Парнасом). 4.2.1. Спецификация модуля Стратегия выбора спецификации модуля обеспечивает не- явное определение информационных структур и условных пере- ходов на процедурном уровне. Она позволяет формировать от- дельные блоки для построения семейств программ системы. На начальной стадии выделяется круг проблем, определяющих проектные решения. При решении каждой из этих проблем формируется отдельный модуль, что впоследствии облегчает модификацию уже созданной программы. Область определе- ния указанных проблем зависит от организации данных и спе- цификации алгоритма. Для программы обработки пакетов можно выделить сле- дующий круг задач: получение, сортировку, проверку и обра- ботку входных данных, а также проверку и сохранность выход- ных данных; Получение и сортировка входных данных, а также запомина- ние выходной информации связаны с проектированием структу- ры данных. Перемена типа носителя для входных файлов с магнитной ленты на диск или замена последовательного досту- па на прямой оказывают влияние на решение указанных проб- лем. Обработка и сортировка входной информации подразуме- вают решение проблемы выбора алгоритма. Проектирование спецификаций влияет на проверку входных и выходных данных на некоторых этапах. Одни модули зависят от спецификаций программы и должны перестраиваться при изменении соответ- ствующих спецификаций, другие проектируются так, чтобы мог- ли использоваться с измененными программами системы. Иног- да возможно применение 'модулей общего назначения, исполь- зуемых в различных системах. Список проблем, определяющих проектные решения, не яв- ляется описанием упорядоченных функциональных элементов или иерархии вызываемых модулей. Решение этих проблем производится независимо от определения управляющих струк- тур. Целью этого типа декомпозиции программ является не выбор структуры программы, а обеспечение изоляции критиче- ских ее частей, которая необходима для улучшения планирова- ния. Проектирование программы затем проходит по двум на- правлениям: 1) дальнейшее определение составных частей ре- шающих проблем, позволяющих нацти проблемы более низкого уровня, и 2) изменение межмодульных управляющих связей.
Проектирование программ_______________________119 После того как выбраны алгоритмы, структуры данных и ме- тоды доступа, связи между модулями перестраиваются с целью • объединить структуры данных и методы доступа к ним с обслуживающими процедурами; • связать функциональные элементы с процессами подго- товки их активации; • изолировать форматирование внешних данных, выбор ко- дирования данных, методы доступа к данным, зависящие от структур данных и обнаруживающие их, и выбор алгоритмов. Проблемы, решение которых влияет на длительность «жиз- ни» программы, должны решаться независимыми модулями. Такими проблемами являются, например, определение разме- щения записи с помощью непосредственного поиска или поиска по ключу или определение форматов выходных данных для печати на бумаге или для вывода на микрофиши. Некоторые модули, такие, как программа сортировки, могут уже находить- ся в библиотеке вычислительной машины. Может существовать возможность дополнения системной библиотеки новыми моду- лями, если они предназначены для выполнения общедоступных и многократно используемых функций. Эта стратегия проектиро- вания не предназначена для построения схемы управления мо- дулями, так как ориентирована в основном на построение вспо- могательных модулей, с помощью которых затем может быть создана прикладная программа. 4.2.2. Метод иерархического проектирования модулей Метод иерархического проектирования модулей, разрабо- танный Джексоном, применяется для построения структуры программы на основе структур входных и выходных данных. Он наиболее эффективен в случае высокой степени структуриза- ции данных (например, в задачах печати экономических отче- тов). При проектировании программ этим методом использу- ются иерархические диаграммы, называемые схемами Джек- сона. Допустим, что надо отпечатать отчет, в котором и вход- ные, и выходные данные сгруппированы в соответствии с одним Управляющим полем и, кроме того, входные данные располо- жены в порядке, необходимом для выдачи. На рис. 4.5 приве- дена структура входного потока данных для генерации отчетов и показана соответствующая ей структура отчета. Конструкции повторения отмечены звездочками. Цикл отражает процедуру выбора. Любая из записей может иметь то же самое или дру- гое значение в управляющем поле, что и предыдущая запись. Идентификация момента, перехода состояния осуществляет- ся тогда, когда отмечается различие в значениях управляющих Полей. Данные о моменте перехода представляют собой вход-
120 Глава 4 ную информацию, отличающуюся от данных, значения кото- рых содержатся во входных записях. Входные и выходные структуры отличаются друг от друга потому, что на входе разные группы записей не разделяются. Все записи имеют один и тот же формат. Если генератор отче- Рис. 4.6. Схема Джексона с учетом переходов обработки во время смены групп записей. Рис. 4.5. Структуры данных для про- граммы генерации отчетов. а — входные данные; б — выходной отчет. тов представляет собой компилятор с пакетным типом обра- ботки, распознающий карты управления заданиями и карты конца файла, то используются те же входные и выходные структуры. Если не разделять группы записей, входной файл будет представлять собой просто последовательность записей, хотя записи сгруппированы, но группы не являются подструк- турами файла. С другой стороны, отчет содержит строки с за- головком, строки с итоговой информацией, а также другие раз-
Проектирование программ 121 личные разделители между группами записей. Для последую- щего разбиения входной информации на правильные и непра- вильные записи необходимо, чтобы записи можно было разли- чать. В основе метода Джексона лежит предположение, что про- грамма, преобразующая данные одного вида в другой, должна быть организована соответственно структуре данных. Совокуп- ность схем на рис. 4.5 позволяет получить программу, иерархи- ческая структура которой показана на схеме, приведенной на рис. 4.6. Модули обработки одних и тех же данных совмещены. Таким образом, определяя соответствие между файлом и отче- том, можно установить соответствие между двумя модулями обработки записей. Если какая-либо из схем содержит модуль, отсутствующий в другой схеме, он включается в соответствую- Рис. 4.7. Схема Джексона для программы пакетной обработки. « — структура входных данных; б — структура выходных данных; в — программа.
122 Глава 4 щее место на этой схеме. Затем к схеме могут быть добавлены дополнительные элементы, обозначающие начальную стадию процесса обработки группы записей, а также обработку си- туации конца файла как модифицированного перехода обра- ботки. Рис. 4.7в Более общий пример программы обработки пакетов, про- веряющей полные пакеты входных записей и обрабатывающей только правильные пакеты, рассмотрен в этой главе при иллю- страции метода нисходящего проектирования. На рис. 4.7 пока- зана соответствующая этому примеру схема Джексона. Вход- ные данные логически разделяются на правильные и непра- вильные пакеты, причем и те и другие подлежат обработке. Неправильные пакеты затем делятся на правильные и непра- вильные записи. Сравнивая рис. 4.7 и рис. 4.3, можно видеть, что основное отличие рассмотренных методов заключается в способе определения неправильных входных данных. Структу- ризация по типу исток — преобразователь—сток ориентире-
Проектирование программ 123 вана на обработку правильных данных. При, этом неправиль- ные записи передаются во вторичный выходной поток, который позднее обрабатывается другой программой. При использова- нии способа Джексона правильные и неправильные данные анализируются одинаково, как если бы существовали некото- рые дополнительные изменения в атрибутах записей. Схемы Джексона основаны на анализе данных, поэтому при печати отчетов возникают трудности с распределением 'инфор- мации по страницам. Входные и выходные схемы не являются чисто логическими описаниями, а представляют собой комбина- цию элементов логической и физической структур. Описание структуры отчета, основанное на понятии групп записей, не сов- падает с описанием распределения 'информации по страницам. / \ / \ Постраничный Информация /ГенератоД отчет /генератор) 0™>т отчетов / страниц у Рис. 4.8. Декомпозиция программы генерации отчетов с формированием стра- ниц. Если распределение по страницам подчинено в структурном от- ношении группам записей, то каждая группа записей будет на- чинаться с новой страницы и занимать столько страниц, сколь- ко потребуется. Это позволяет размещать заголовки и оконча- ния страниц (их номера, например) в заголовках и оконча- ниях групп. Если же структура групп записей подчинена рас- пределению по страницам, то на одной странице может появиться несколько групп записей. Распределения по группам записей и по страницам, хотя внешне не зависят друг от друга, являются взаимосвязанными. Эти виды группирования информации одинаково важны, но, поскольку принадлежат к различным уровням описания дан- ных, нельзя построить схему Джексона, используя одновремен- но оба эти вида группирования. Чтобы лучше понять это раз- личие, представим оба типа распределения информации в виде независимых процессов (рис. 4.8). Первый из них превращает входную структуру в отчет, игнорируя при этом распределение по страницам. Выходная информация этого процесса является входной для второго, который распределяет отчет по страни- цам. Логическая структура отчета преобладает над физической. Входные и выходные структуры, приведенные на рис. 4.5, пред- ставляют собой структуры данных для процесса генерации от- чета. Структуры данных для процесса распределения по стра- ницам приведены на рис. 4.9. Можно использовать две независимые программы — одну Для генерации отчетов и другую для распределения по стра- ницам, — но этот способ неэффективен, так как информация от-
124 Глава 4 чета выводится в промежуточный файл, а затем печатается программой распределения по страницам. При параллельной обработке эта информация может быть передана непосредст- венно программе распределения по страницам. Тогда эти две программы логически связаны между собой как сопрограммы и оба процесса выполняются одновременно и независимо друг от друга, но с помощью согласованных между собой программ. a б Рис. 4.9. Информация для програм- мирования формирования страниц. а — вход в блокировщик страниц; б — вы- ход из блокировщика страниц.. Рис. 4.10. Схема Джексона для про- граммы формирования страниц. а — программа формирования страниц; б — инвертированная программа. Конец 1 страницы Если возможно .изменение носителя выходной информации, например смена формата бумаги или замена бумаги на микро- фиши, то более удобно использование независимых программ. Если же система не может обеспечить параллельную работу программ, то выполняется инвертирование одной из них, т. е. создается программа, содержащая указанные сопрограммы в виде отдельных секций, используемых как подпрограммы дру- гой программой. Если программа формирования страниц под- чинена программе генерации отчета и разбита на подпрограм- мы, соответствующая ей схема, приведенная на рис. 4.10, а, преобразуется в схему, показанную на рис. 4.10,6 (где малень- кими квадратами отмечены входы в подпрограммы, а стрелка- ми— конструкции повторения). Программа формирования
Проектирование программ 125 страниц становится вспомогательной для генератора отчетов, который в случае 'необходимости при выводе информации на печать обращается к различным ее точкам входа. Необходимый в данный момент режим работы подпрограммы задается пере- менной внутреннего состояния, например счетчиком числа строк. В реальной практике программа формирования страниц может быть разделена на части, размещенные в вызывающей программе, и к каждой из них возможно обращение из любой точки. Тогда переменная состояния становится частью вызы- вающей программы. При использовании метода анализа сообщений структура файла игнорируется, а основное внимание уделяется преобра- зованию единственного входа. В противоположность этому при иерархическом проектировании по методу Джексона основное внимание уделяется логической структуре данных. Оба метода основаны на построении древовидной иерархической структуры модулей, причем число разветвлений в каждом узле в основном равно трем, т. е. каждый модуль имеет три подчиненных ему модуля. Однако это сходство является чисто внешним. Модули, соответствующие внешним сторонам схемы Джексона, выполня- ют вспомогательные функции, тогда как модули, расположен- ные внутри диаграммы и на более низких уровнях, предназна- чены для выполнения основных функций обработки. В схемах И—П—С основным функциям отводятся более высокие уровни иерархии, а вспомогательным — подчиненные, более низкие уровни. 4.3. Метод восходящего проектирования При использовании метода восходящего проектирования в первую очередь определяются вспомогательные функции, ко- торые могут потребоваться для проектируемой программы.. В этом смысле рассматриваемый метод аналогичен методу мо- дульной декомпозиции Парнаса. Модульная декомпозиция, или анализ первичных определяющих областей, заключается в на- хождении ключевых модулей промежуточных уровней, которые затем разрабатываются восходящим и нисходящим способами одновременно. Эти модули не являются вспомогательными в том смысле, что потребность в них возникает в нескольких точ- ках программы. Необходимость в использовании этих модулей может возникать в других программах или системах. Функции, определяемые как вспомогательные при восходя- щем проектировании, реализуются с помощью модулей самых нижних уровней, предназначенных для выполнения таких опе- раций, как чтение, сортировка, формирование страниц и печать. Кроме того, они обеспечивают элементарные операции над об- ластями хранения данных, а также позволяют расширить воз-
126 Глава 4 можности выбора встроенных и библиотечных функций. На рис. 4.11 показаны вспомогательные функции нижнего уровня, предназначенные для управления внешними областями хране- ния информации в системе сопровождения файлов. Эти функ- ции изолируют структуру файла и 'используют абстрактный тип данных, определяемый файлом. После того как модули низкого уровня, предназначенные для управления внешней и оператив- ной памятью, разработаны, они используются для определения функций более высокого уровня, таких, например, как выбор- ка соответствующих записей, обновление записей главного фай- Размес- тить Читать Писать Конец файла Главный файл Рис. 4.11. Описание файлов на общем уровне. Читать Конец файла Сорти- ровать Файл сообщений ла и уничтожение остаточных записей. Эти функции использу- ются при проектировании программы на более высоком уровне и т. д., пока не будет завершена разработка всей программы. 4.4. Анализ внутреннего потока данных Методы проектирования внешних и внутренних для програм- мы данных аналогичны друг другу. Когда программа разбита на модули, могут быть сформированы таблицы потоков данных, определены информационные потоки между модулями и все об- щие области хранения информации, такие, как файлы и масси- вы глобальных данных, и могут быть определены также сло- вари для идентификации данных. Внешние потоки данных переходят от одних программ к другим через области хранения информации. Внутренние по- токи данных передаются между модулями программ в виде списков параметров. Они должны иметь по возможности про- стую структуру, чтобы обеспечить слабое сцепление модулей. Иерархические схемы модулей часто соответствуют табли- цам межмодульных связей, таким, как, например, табл. 4.1. Другой метод определения потоков данных состоит в их изо- бражении на иерархических схемах (рис. 4.12). В зависимости от назначения схемы на ней может быть определен поток толь- ко явно передаваемых данных или поток неявно передаваемых данных. i Данные из областей хранения информации отличаются от . пересылаемых данных тем, что они обычно не размещаются | внутри модуля. Объявление глобальных данных в блочных
Проектирование программ 12Г структурированных языках, таких, как ПЛ/1 или Паскаль, представляют собой исключение. Общие данные могут содер- жать информационные единицы, которые недоступны для неко- торых из модулей. Полный обзор используемых данных, а так- же определение степени сцепления модулей могут быть обеспе- чены путем включения всех информационных единиц в схемах потоков данных. Здесь используются те же критерии структурности для внут- ренних или общих областей хранения данных, что и при анали- зе областей их хранения в системах. Даже если внутренние Рис. 4.12. Иерархическая схема с потоком данных. файлы и другие области хранения данных определены в не- скольких модулях программы, они должны быть логически свя- заны только с одним программным модулем. Глобальные дан- ные для языков ПЛ/1 и Паскаль определяются также только в одном модуле. Передача пересылаемых данных осуществляет- ся из специального модуля-источника в -момент его очередной активации. Причины, по которым для пересылаемых данных выделяется специальный модуль, те же, что и в случае приме- нения особого модуля для определения файлов и областей хра- нения данных при проектировании оверлейной программы. Ин- формация должна быть доступна только для тех модулей, ко- торым она нужна. Множество модулей, вызываемых непосредственно или кос- венно некоторым модулем, называется областью управления этого модуля. Область влияния модуля определяется как мно- жество таких модулей, на которые оказывают воздействие ре- шения внутри этого модуля. Программа является более простои Для понимания и сопровождения, если область влияния каждо- го модуля содержится в его области управления. Вложенность
128 Глава 4 этих областей следует определять по различным видам межмо- дульного взаимодействия. Так, например, передача кода воз- врата, отмечающего конец файла, не нарушает вложенности этих областей, поскольку возвращается лишь значение неко- торого данного. Не следует избегать использования кода воз- врата, обозначающего неправильные данные, поскольку реше- ние 'модуля проверки данных влияет на модули, находящиеся вне пределов его области управления. На рис. 4.13, а показаны области управления и влияния для модуля, возвращающего флаг правильности данных. Флаг Рис. 4.13. Области влияния и управления модуля проверки данных. . влияет на работу вызываемого модуля следующим образом: если входные данные неправильны, флаг примет другую инфор- мацию; если же они правильны, то возвращает правильные дан- ные вызывающему модулю. Модуль приема правильных данных находится вне пределов области управления модуля проверки данных, но внутри области его влияния. Структура, показанная на рис. 4.13,6, лучше 'Структуры, показанной на рис. 4.13, о. Модуль проверки данных в ней управляет модулем, принимаю- щим очередные данные. Если область влияния модуля не со- держится в его области управления, принимающий решения мо- дуль может быть объединен с находящимся над ним модулем (рис. 4.13,6). В обеих структурах флаг физически перемещает- ся в модуль более высокого уровня. Если область влияния под- чинена области управления, модули организуются так, что флаг устанавливается и проверяется в том модуле, в который он передается. Если флаг используется для управления, а не для передачи информации, установка флага на возможно бо- лее высоком уровне исключает сцепление по управлению.
Проектирование программ 129 Если решения осуществляются в том же модуле, в котором обнаруживается ситуация, связанная с ними, это свидетельст- вует о том, что ненормальные и исключительные ситуации об- рабатываются на том же уровне, на котором они возникают. Основным исключением из этого правила является организация управления при условии обнаружения конца файла, а также при других условиях окончания, таких, как появление приводя- щих к аварийному завершению ошибок обработки. В этих слу- чаях возможно несколько способов организации управления. Ситуации окончания обработки могут обнаруживаться в не- скольких модулях. Тогда может быть определен особый модуль окончания обработки, вызываемый из различных точек. Другой способ заключается в обратной передаче флага управления для анализа условий окончания на верхнем уровне, т. е. на том уровне, где была начата обработка. 4.5. Вспомогательные средства проектирования программ Задачи сопровождения файлов, обработки пакетов и генера- ции отчетов выбраны для обсуждения различных сторон про- цесса проектирования программ потому, что они представляют сложные ситуации управления. Метод выбора спецификации модулей позволяет избежать решения проблем, связанных с управлением. При использовании других стратегий проектиро- вания программы чаще всего применяются иерархические схе- мы модулей. Проблема управления решается с помощью раз- мещения конкретных модулей на схемах и путем определения спецификаций модульных связей. Наличие решений о том, фор- мировать или пропускать обращение к определенному модулю или повторять использование некоторого модуля, может быть отражено в схемах, но не исследуется во всех подробностях. Только метод пошагового уточнения непосредственно связан с решением проблемы управления. Часто управляющие реше- ния для сложных ситуаций могут быть легко получены с ис- пользованием методов, разработанных специально для этой цели. Один из этих методов основан на применении таблиц ре- шений. 4.5.1. Таблицы решений Метод проектирования с помощью таблиц решений заклю- чается в перечислении вариантов управляющих решений, при- нимаемых на основе анализа данных. Поскольку в этих таб- лицах перечисляются все возможные сочетания данных, суще- ствует гарантия того, что учитываются все необходимые реше- ния. Таблицы решений обычно состоят из двух частей. Верхняя 9-399
130 Глава 4 Таблица 4.2. Таблица решений для программы сопровождения файлов Условия Комбинации увловий Конец файла сооб- Да Да Нет Нет Нет Нет Нет Нет щений Нет Нет Нет Конец главного Да Нет Да Да Нет файла Правильная запись из файла сооб- щений Нет Нет Да Нет Да Да Да Нет Найдена соответ- ствующая за- Нет Нет Нет Нет Да Нет Нет Нет пись Отсутствует соот- ветствующая запись Нет Нет Да Нет Нет Да Нет Нет ' Действия Обновить запись главного файла Выдать сообщение об ошибке Читать запись из главного файла Читать запись из файла сообще- ний Повторить Закончить главный файл Выйти из програм- мы X Комбинации действий X XX X X X X х X X X X X X X х X X X часть используется для определения условий, а нижняя — для действий. Левая часть таблицы содержит описание условий и действий, а правая часть —соответствующую ситуацию. Табл. 4.2 демонстрирует возможность использования таблицы решений для определения управления модулями в программе сопровождения файла. Вопросы, на которые следует ответить в структуре управле- ния, перечислены в столбце условий. Действия, выполняемые в зависимости от ответов, указаны в столбце действий. Затем рассматриваются все возможные комбинации ответов «да» и «нет». Если какая-либо комбинация невозможна, она может быть опущена. Крестами отмечены действия, необходимые для каждого набора условий. РАССМАТРИВАЙТЕ ВСЕ ВОЗМОЖНЫЕ КОМБИНАЦИИ ДАННЫХ
Проектирование программ 131 Порядок расположения условий не должен 'влиять на поря- док их проверки. Однако действия могут быть записаны в по- рядке их выполнения. Таблицы решений будут более подробно рассмотрены в гл. 6. Они могут быть использованы для проектирования структуры управления модулями в иерархической схеме. Их также можно преобразовать в двоичные деревья решений и принять как ос- нову для проектирования любого модуля, использующего реше- ния. 4.5.2. Схемы Варнье — Орра Другой широко известный тип схем — схемы Варнье — Ор- ра — вкратце описан как алгоритм проектирования систем. Его применение для проектирования программ подобно использо- ванию схем Джексона, и поэтому его нельзя рассматривать как отдельный метод проектирования. Описания данных и про- грамм на этих схемах такие же, как на схемах Джексона, но в отличие от них строятся в первую очередь не по вертикали, а по горизонтали. На рис. 4.14 показана схема Варнье — Орра, по содержанию аналогичная схеме Джексона, приведенной на ’Заголовок файла Входной файл ч $g™™ 1 Переход к обработке новой группы Записи данные Конец файла а Входная структура 'Заголовок* отчета Отчет « Отчет на основе одной группы записей ^Окончание отчета Заголовок группы J Строки отчета’ Окончание группы "б Выходная структура Рис. 4.14. Схема Варнье — Орра для информации, используемой при генерации отчета. £ис. 4.5. Атрибуты схем Варнье — Орра в отличие от схем Джексона объединяются вместе, как, например, на рис. 4.15, а. На рис. 4.15,6 показано, как получаются из рассмотренной структуры соответствующие ей модули. Структура программы аналогична объединенной структуре данных. Следует отметить, что благодаря такому объединению структур возможна детали- зация 'ИСТОЧНИКОВ выходной 'информации и входных условий, инициализирующих эти источники. При построении схемы для программы информационные единицы могут быть подвергнуты расширению, что позволяет учитывать обработку данных. При- меняемый в этом методе способ записи отражает этапы функ- 9*
132 Глава 4 Заголовок файла Заголовок отчета Входной файл и для отчета Группы записей входного файла* Группы записей отчета* Конец файла ‘Окончание отчета а Совмещение структуры данных Переход к обработ- Г Заголовок группы ке новой группы J Данные Окончание группы Данные записи Начало отчета Открытие файла Печать заголовка отчета Чтение первой записи Генерация отчета Генерация отчета по группе записей Проверка смены группы записей Выдача данных ’Печать заго- ловка группы Печать окон- чания группы Вычисления Печать данных Конец отчета Печать окончания группы Печать окончания отчета б Структура программы Рис. 4.15. Схема Вариье — Орра с учетом переходов обработки во время сме- ны групп записей. циональной декомпозиции программы. Каждый столбец следу- ет понимать как описание программы, являющееся уточнением предыдущего столбца. 4.5.3. Схемы информационных связей Таблицы решений целесообразно использовать при исследо- вании подробных схем управления и определении данных, кото- рые могут остаться нерассмотренными, если основное внимание уделяется обработке обычных данных. Схемы Варнье — Орра и другие виды иерархических схем позволяют строить основную структуру управления. Поэтому желательно объединение мето- дов, использующих схемы потоков данных, с другими методами проектирования программ. Один из таких способов заключается в преобразовании иерархических схем в схемы информационных связей, осу- ществляемом путем удаления всех линий управления модуля- ми и включения линий потоков данных. Эти линии отражают в большей степени источники и назначение информации, чем пути ее перемещения. На рис. 4.16 показана схема информаци- онных связей, полученная на основе схемы Джексона (рис. 4.6). Она может быть использована для установления порядка рабо- ты модулей программы и определения физического размеще- ния данных в ней. Данные должны быть размещены в модуле самого высокого уровня, в области управления которого на- ходятся все модули, использующие эти данные. Таким образом,
Рис. 4.16. Схема информационных связей. Рис. 4.17. Схема информационной зависимости.
134 Глава 4 флаг конца файла должен быть расположен в главном модуле генерации отчетов. Идентификация группы записей должна производиться в главном модуле. Если последовательность ра- боты модулей неочевидна, она определяется с помощью сле- дующих действий: 1. Удалить все модули, не имеющие входящих и'выходя- щих информационных линий. Они являются вспомогательными и должны срабатывать в соответствии с линиями управления. 2. Удалить все модули, имеющие только выходящие инфор- мационные линии. Они срабатывают во вторую очередь. 3. Повторять шаг 2 до тех пор, пока не будут удалены все модули только с выходящими информационными линиями. 4. Удалить модули, не имеющие информационных линий. Они должны срабатывать в любое время после предшествую- щих им модулей. В результате этих действий останутся только модули, зам- кнутые в циклы, что приводит к появлению обратных связей. Последовательность срабатывания этих модулей должна стро- иться на некоторой другой основе. Функции, выполняемые ими, могут оказаться такими, что одни из модулей должны логиче- ски срабатывать раньше других. Например, возможна ситуация типа поставщик — потребитель, когда поставщик должен что- нибудь создать, прежде чем потребитель начнет использовать созданный продукт. Применяя эту процедуру из четырех шагов к схеме, пока- занной на рис. 4.16, получаем схему информационной зависи- мости, приведенную на рис. 4.17. Модули, расположенные в верхней части схемы, должны закончить работу прежде, чем сработают нижние модули. Когда модуль А расположен выше модуля В в схеме управления данными, но ниже него в схе- ме информационной зависимости, это означает, что модуль А прямо или косвенно вызывает модуль В. В этом случае мо- дуль А начинает работать до модуля В, но заканчивает после него. Если модуль А расположен выше модуля В в схеме управ- ления и оба модуля не имеют соединений в схеме информаци- онной зависимости, модуль А может быть выполнен перед мо- дулем В и ему не нужно ждать данных от В. Если модуль А находится ниже модуля В в схеме информационной зависимо- сти и оба модуля не связаны в схеме управления, модуль А выполняется после модуля В, согласно линиям информацион- ной зависимости. 4.5.4. Схемы HIPO Этап разработки модулей на стадии проектирования про- граммы зависит от определения функций модулей. При обсуж- дении проектирования систем рассматривалось применение
Проектирование программ 135 схем HIPO для определения функций модулей (гл. 2). На рис. 4.18 показана схема HIPO для модуля обновления запи- сей главного файла, входящего в состав программы сопровож- дения файла. Для любого независимо создаваемого модуля должна быть разработана своя схема HIPO. Любая из функ- ций, для которой не может быть выделен отдельный модуль, Вход Обработка Выход Файл сообщений ; Номер счета Код сообщения Новый адрес Новая подписка Срок подписки ' 1. Определение типа сообщения —2.Обработка сообщения > Список оши- бок Сообщение об ошибке Главный файл Дата окончания подписки 5.Регистрация сообщения 3 Исправление записи главного файла 4.Включение новой подписки Главный файл Адрес Подписка Дата окон- чания под- писки АРегистрацион- Т'ный файл Номер счета Код сообще- ния Старая за- пись глав- ного файла Новая за- пись глав- ного файла Рис. 4.18. Схемы HIPO для программы обновления файла. должна быть дополнена и описана во время разработки моду- лей, а не на стадии проектирования программы. Проектирование модулей закончено, когда программа с по- мощью какого-либо метода расчленена на модули и определе- ны связи между модулями, а также области хранения данных. Модули затем анализируются путем оценки их силы связности, независимости и 'степени сцепления исходя из структурных осо- бенностей данных. 4.6. Программная документация Программная документация должна быть организована так, чтобы была легко доступна информация по отдельным моду- лям. Список модулей, описание программы и иерархические
136 Глава 4 схемы используются как справочники. Кроме того, модули должны быть пронумерованы. Модули обычно нумеруются в соответствии с их позициями в иерархических диаграммах. На рис. 4.3 показан один из спо- собов нумерации. Другой способ (иллюстрируется рис. 4.19, где номера показывают уровень модуля на. схеме и отношение между вызывающим и вызываемым модулями. Перечисление модулей слева направо по уровням позволяет отобразить взаи- мозависимость модулей с помощью десятичных цифр. Этот спо- соб нумерации показывает, что, например, модули 2.1 и 2.2 вы- Рис. 4.19. Нумерация модулей. зываются непосредствен- но только модулем 2 и кос- венно только управляющим модулем. Модуль 2.2 вызы- вает только модуль 2.2.1. Если эти модули являются вспомогательными модуля- ми общего назначения, как, например, подпрограммы сортировки или ввода-вы- вода, т. е. такими, которые вызываются более чем од- ним модулем, удобного спо- соба для включения их в схему перечисления нет. Если же они вызывают свои собствен- ные подмодули, то для перечисления можно пользоваться их собственным набором номеров. НУМЕРУЙТЕ модули в соответствии с их иерархической упорядоченностью Описания модулей должны быть организованы в соответст- вии с иерархическими схемами и порядком нумерации. Доку- ментация всей программы должна (содержать следующие дан- ные: . а) имя программы; б) общее функциональное описание; в) описание внешних областей хранения данных; г) описание человеко-машинного интерфейса; особенно это касается входных и выходных форматов, а также инструкций по эксплуатации программы; д) схемы программы, такие, как иерархические схемы, схе- мы Варнье — Орра, псевдокод и таблицы интерфейса; е) краткое описание составляющих программу модулей, на- пример схемы HIPO; ж) описание общих областей хранения данных, таких, как схемы потоков данных или их словари, позволяющие, в частно-
Проектирование программ 137 сти, идентифицировать модули, имеющие доступ к области хра- нения данных, а также способные ее модифицировать; з) тестовые данные и результаты; и) список исправлений; к) справочный указатель для документации. ТЩАТЕЛЬНО СОСТАВЛЯЙТЕ ДОКУМЕНТАЦИЮ Если программа является частью системы, руководство по использованию программы может не понадобиться. Докумен- тация в основном нужна для программиста, эксплуатирующего црограмму. Если же программа представляет собой отдельную и независимую часть системы, используемую вне вычислитель- ного центра, или вовсе не является частью системы, то нужна вся документация стадии проектирования системы, руководст- ва для пользователя и оператора, а также инструкция по экс- плуатации. 4.7. Упражнения 1. Возьмите старую программу, состоящую по крайней мере из пяти моду- лей, и определите тип связности каждого модуля и тип сцепления для каждой пары модулей. Для определения типа сцепления используйте таблицу. 2. Возьмите старую программу, состоящую по крайней мере из пяти мо- дулей, и постройте для нее иерархическую схему модулей, схему управления данными и схему информационной зависимости. Для каждого из модулей опре- делите области влияния и управления. 3. Матрица размером NXN содержит расстояние по прямой между пара- ми из ДО точек. Если между точками i и j нет прямого пути, то элементы мат- рицы A(i, j) и А(/, 0 равны —1. Главная диагональ (элементы A(i, 0, где 1<1<ДО) заполнена нулями. Используя метод пошагового уточнения, построй- те программу, рассчитывающую число треугольников, представляемых с по- мощью матрицы. Напоминаем, что сторона треугольника меньше суммы двух других его сторон. 4. Доска для китайских шашек на одного или двух игроков представляет собой разлинованное по вертикали и горизонтали поле размером 9X9. Перво- начально 10 фишек образуют треугольник в одном углу доски. Цель игры заключается в перемещении фишек в противоположный угол за минимальное число ходов. В одном из вариантов игры ход состоит или из перемещения фишки на соседнее поле, или из последовательности «прыжков» фишки через любое число других фишек. Перемещения и прыжки должны выполняться по горизонталям или вертикалям поля. Используя метод пошагового уточнения, постройте программу для определения наилучшей стратегии игры для одного игрока. 5. Постройте программу для упражнения 3, используя метод анализа исток — преобразователь — сток. 6. Метод Парнаса, заключающийся в определении спецификаций модулей, применяется для идентификации базисных решающих модулей. Какие модули этого типа потребуются для реализации программы в упражнении 3? Какие модули потребуются для подсчета числа четырех- и пятиугольников? 7. Используя произвольный язык программирования, спроектируйте набор модулей, изолирующих обработку стека. Один из возможных методов заклю- чается в передаче модулю управления стеками имени требуемой функции—-
<38 Глава 4 ВЫТОЛКНУТЬ, ВСТАВИТЬ, СОЗДАТЬ, ПУСТО. Этот модуль будет затем вызывать другой модуль, предназначенный для выполнения нужной функции. 8. Игральная доска из упражнения 4 может быть представлена несколь- кими различными способами. Спроектируйте набор модулей доступа, изолиру* ющих представление доски. 9. Постройте схему Джексона для входа и выхода модулей программы из упражнения 3 для того случая, когда матрица выводится построчно на пер- фокарты, а таблица треугольников печатается. Модифицируйте эти схемы так, чтобы можно было обработать четырех- и пятиугольники и напечатать соответствующую информацию, сгруппированную по числу сторон анализируе- мых фигур. 10. Преобразуйте схему исток — преобразователь — сток (рис. 4.4) так, чтобы ее можно было использовать для построения: а) программы обновления записей главного файла; б) программы генерации отчетов; в) пакетного компилятора. 11. Преобразуйте схему Джексона, показанную на рис. 4.7, для построе- ния: а) программы обновления записей главного файла; б) программы генерации отчетов; в) программы печати частот появления слов в тексте. 12. Спроектируйте программу, реализующую процесс перераспределения, вписанный в упражнении 8 гл. 3. Составьте документацию по вашему проекту, жак описано в разд. 4.6. Не включайте в документацию никаких руководств.
Глава 5 АЛГОРИТМЫ Под алгоритмом понимается совокупность действий, необхо- димых для решения задачи. Алгоритм отличается от системы и программы тем, что в нем содержится только описание дей- ствий, производимых над данными, но полностью отсутствуют какие-либо описания данных. Алгоритмы могут быть представ- лены с помощью таблиц решений и псевдокодов (гл. 4). Иерар- хические схемы для этого непригодны. Одни типы схем позво- ляют описывать данные, другие определяют вид обработки дан- ных, но не предусматривают порядок обращений к модулям. Алгоритмы содержат определение пошагового процесса обра- ботки данных с описанием преобразований данных и функций управления. Они могут быть записаны на естественном языке, на языке программирования, а также с помощью математиче- ской или другой символической нотации. Название алгоритма может указывать на его назначение (например, алгоритм сор- тировки, обращения матриц, игры в «крестики и нолики» и т. д.) или определять используемый в нем метод решения. г' 5.1. Типы алгоритмов Каждая научная дисциплина имеет свои методы получения результатов. В этом отношении программирование для ЭВМ не является исключением. Основное различие между задачами заключается в том, что для одних существуют прямые методы решения, а другие не могут быть решены без дополнительной информации, получаемой из ответов на некоторые вопросы, причем варианты ответов заранее предусмотрены. Если задача может быть решена прямым способом, говорят, что она имеет детерминированный метод решения. Детермини- рованные алгоритмы всегда обеспечивают регулярные решения. В них отсутствуют элементы, вносящие неопределенность, кро- ме того, для них невозможна произвольность в выборе реше- ний, определяющих последовательность действий. Для построе- ния детерминированных алгоритмов недопустимо применение методов проб и ошибок. К задачам, имеющим детерминиро- ванные решения, относятся математические уравнения, а также такие задачи, как проверка данных и печать отчетов.
140 Глава 5 Если решение задачи не может быть получено прямым мето- дом, а выбирается из заранее определенного множества вариан- тов, такая задача имеет недетерминированное решение. Алго- ритм недетерминирован, если для его реализации используются методы проб и ошибок, повторов или случайного выбора. К числу подобных задач относятся такие, как нахождение де- лителей числа, поиск страницы, а также ставшие классически- ми задачи о коммивояжере, о восьми ферзях и задачи нахож- дения кратчайшего пути через лабиринт. Задача о восьми ферзях заключается в нахождении такого способа расстановки на шахматной доске восьми ферзей, при котором ни один из них не находится под угрозой других. Най- денное размещение ферзей, удовлетворяющее этому условию, легко проверить на правильность, но поиск решения представ- ляет определенные трудности. В задаче о коммивояжере нужно найти кратчайший путь через определенное множество городов, при условии что этот путь через каждый город проходит только один раз. Для решения этих задач на ЭВМ используется моде- лирование с помощью метода проб и ошибок. Этот же метод может быть применен для нахождения ответа ручным спосо- бом. Третий, основной тип алгоритмов, предназначен не для поис- ка ответа на поставленную задачу, а для моделирования физи- ческих систем с использованием ЭВМ. 5.1.1. Детерминированные и недетерминированные алгоритмы Многие задачи могут быть решены с помощью как детер- минированных, так и недетерминированных алгоритмов. Алго- ритм решения задачи нахождения наибольшего общего дели- теля двух целых чисел, построенный на основе перебора всех возможных делителей, начиная с наибольшего числа и кончая единицей, является недетерминированным. Другой способ ре- шения этой задачи основывается на использовании алгоритма Евклида. Этот алгоритм заключается в нахождении последо- вательности пар чисел, каждая из которых образуется из мини- мального числа предыдущей пары и разности между состав- ляющими ее числами. Ответ представляется парой, содержа- щей два равных, но отличных от нуля числа. В первом из этих двух методов каждое число проверяется на соответствие усло- вию. Во втором такая проверка не нужна. Но заранее известно, что правильное использование алгоритма приведет к верному ответу. Известные алгоритмы разложения числа на простые множи- тели являются недетерминированными, так как в них исполь- зуется метод проб и ошибок. Каждое предполагаемое число может быть проверено на правильность путем деления на него
Алгоритмы 141 исходного числа, ио единственный известный способ разложе- ния заключается в испытании различных вариантов множите- лей. Было предпринято много попыток математически опреде- лить детерминированный алгоритм разложения числа на мно- жители. Но до сих пор не известно, существует ли такой алго- ритм. Некоторые из задач являются по природе недетерминиро- ванными, и можно показать, что для них не существует детер- минированного алгоритма. Задача о восьми ферзях является недетерминированной, так как при ее решении используется метод проб и ошибок. До тех пор пока не будет найдено правильное размещение ферзей, ре- шение производится перебором их местоположений. Перед полу- чением решения может быть просмотрено множество различных вариантов расположения ферзей. Недетерминированный харак- тер свойствен играм и головоломкам, и это превращает их в приятное развлечение. Смысл игр и головоломок заключается в нахождении стратегии, позволяющей победить противника или решить головоломку. По сути дела, недетерминированный алгоритм описывает си- стематическую процедуру поиска нужного решения среди всех возможных. Его существование в первую очередь зависит от построения множества потенциальных решений, в котором со- держится искомое. Задача о восьми ферзях может быть решена с помощью определенным образом построенной последователь- ности всех возможных попыток размещения ферзей на шах- матной доске. Простой множитель числа может быть найден путем проверки всех чисел, не больших квадратного корня чис- ла. Но идеальный алгоритм для реальной игры в шахматы не может быть построен, поскольку последовательность вариан- тов игры слишком велика для реального использования метода перебора. Это же можно сказать о решении в общем виде зада- чи о коммивояжере. Различие между задачей поиска простых множителей и дру- гими задачами Заключается в том, что при ее решении для ге- нерации и проверки всех вариантов простых множителей воз- враты и повторные попытки м-е осуществляются. Таким обра- зом, этот процесс является эффективным1). Для задачи о восьми ферзях не известен другой способ нахождения решения, кроме метода, использующего возврат и попытку перестановки неко- торых. ферзей. Этот процесс не очень эффективен. Необходимым условием цравильного решения задачи о вось- ми ферзях является размещение на каждой горизонтали и каж- дой вертикали по одному ферзю. Один из способов выполнения 11 Не следует смешивать понятие эффективной вычислимости, принятое в Ге°рии алгоритмов, с понятием эффективности процесса поиска решения, ис- пользуемым здесь автором. — Прим, перев.
142 Глава 5 этого условия заключается в поиске решения с помощью гене- рации всех перестановок ряда от 1 До 8. Значение каждой циф- ры соответствует номеру горизонтали для одного из ферзей, а ее позиция определяет номер вертикали для того же ферзя. Для каждого варианта перестановок следует проверять, не на- ходятся ли какие-либо два ферзя на одной диагонали. В общей сложности проверке подлежат 81 (факториал) перестановок. Более сложный, .но более эффективный способ поиска реше- ния заключается в моделиро- вании размещения ферзей на доске. Для исключения непра- вильных вариантов еще до то- го, как будут размещены все восемь ферзей, могут быть ис- пользованы различные виды эвристик. Например, когда первый ферзь расположен так, как показано на рис. 5.1, то следующие ферзи не должны расставляться на атакованных полях. Если для некоторого положения первого ферзя ' нельзя найти решение, не сле- Рис. 5.1. Позиции, атакованные фер- дует располагать ферзя в по- зем- ложениях, симметричных к рас- смотренному. Например, если нет решения для ферзя, расположенного в верхнем левом углу, то его нет и для ферзя, помещенного в один из других углов. Нахождение простых множителей для числа п требует пере- бора менее чем fn возможных вариантов множителей. Но да- же с учетом этого при очень больших значениях п на процесс поиска решения может быть потрачено много лишнего време- ни. Определение условия расположения восьми ферзей, удов- летворяющего условию задачи, заключается в проверке боль- шого числа вариантов. Для решения задачи размещения п фер- зей на доске размером пХп потребуются намного большие за- траты времени, чем при разложении числа п на множители. Основным фактором при выборе метода для задач, решаемых с помощью перебора большого числа возможных вариантов, является суммарное время нахождения решения. Методы, ис- пользуемые для сокращения числа вариантов при переборе или позволяющие выбирать наиболее правдоподобные варианты, называются эвристическими. Используя эвристические методы (или эвристики) для определения и сравнения позиций на дос- ке по таким параметрам, как их сила и перспективность, мож- но создать программы для игры в шахматы. Такие программы могут разыгрывать хорошие шахматные партии, но не обеспе-1
Алгоритмы 143 чивают беспроигрышную игру. Иногда требуется найти хоро- шее, но не обязательно лучшее решение. Один из эвристиче- ских методов для решения задачи о восьми ферзях заключает- ся в размещении ферзей попарно в каждом из четырех квад- рантов шахматной доски. Если для решения задачи используется метод проб и оши- бок, генерация и проверка множества возможных вариантов решения в соответствующем недетерминированном алгоритме должны осуществляться по некоторому правилу, т. е. систе- матически. Если для решения могут быть использованы различные недетерминированные алгоритмы, перед выбором какого-нибудь из них следует оценить его эффективность по сравнению с другими. Эффективность применения алгоритма зависит от метода генерации наиболее вероятных вариантов и от эвристики, направляющей и регулирующей поиск. Выбор детерминированного алгоритма осуществляется с учетом эко- номии времени работы или используемой машинной памяти. В другом случае может быть выбран наиболее простой для по- нимания алгоритм. При выборе алгоритма решения задачи в целях повышения эффективности процесса вычислений следует отдавать предпочтение детерминированным алгоритмам. Простым примером задачи, решаемой детерминированным и недетерминированным способами, служит задача поиска нуж- ной строки в таблице. Если индекс строки известен, алгоритм ее поиска детерминирован. На языках высокого уровня возмож- на непосредственная ссылка к этой строке. В случае использо- вания машинного кода позиция строки может быть вычислена. Если ее нельзя непосредственно вычислить, можно применить способ случайного поиска, наиболее просто реализуемый с по- мощью метода цроб и ошибок. Введя в случайный процесс ге- нерации вариантов некоторые правила, можно избежать по- вторного рассмотрения вариантов. Кроме применения ограниче- ния сверху на. генерируемые числа наименее эффективным яв- лятся ’метод линейного поиска. По этому методу поиск начи- нается с одного конца таблицы и последовательно производит- ся по всем строкам таблицы. Последовательность пробных ва- риантов получается с помощью увеличения или уменьшения на единицу номера текущей строки. Если поиск осуществляется не случайным, а регулярным способом, для таблицы из п строк в худшем случае требуется п проб. В среднем число проб при регулярном способе перебора равно (п-|-1)/2. Если строки в таблице упорядочены, в худшем случае понадобится число проб, равное log2 «4-1. 5.1.2. Сложность алгоритмов Зависимость времени работы программы от объема обраба- тываемых даянык определяется оценкой сложности алгоритма.
144 Глава 5 Если алгоритм предназначен для обработки равных по объему данных, продолжительность его работы каждый раз остается неизменной и сложность алгоритма является постоянной. Время работы алгоритма обработки каких-либо массивов данных зависит от размеров этих массивов. Время работы ал- горитма, выполняющего только операции чтения и занесения данных в оперативную память, определяется формулой ап+д, где а — время, необходимое для чтения и занесения в память одной информационной единицы, и b — время, затрачиваемое на выполнение вспомогательных функций. Поскольку эта фор- мула выражает линейную зависимость от п, сложность соот- ветствующего алгоритма называют линейной. Обменная сортировка (сортировка по методу пузырька) п элементов списка представляет собой следующий процесс: оп- ределяется наименьший элемент всего списка и осуществляет- ся его обмен с первым элементом, затем определяется наи- меньший элемент оставшегося списка и производится его обмен со вторым элементом и т. д. При выполнении такого алгорит- ма производится п—1 сравнений при определении первого наи- меньшего, п—2 сравнений при определении второго и т. д.. Общий объем сравнений для такого алгоритма вычисляете» по формуле (л_1)+(п_2)+(п-3)+...+4 + 3+2 + 1= Так как число сравнений здесь выражается полиномом второй степени, сложность этого алгоритма квадратична. Если сложность алгоритма оценивается по уже написанной программе, вместо числа сравнений вычисляется число внут- ренних циклов, являющихся основой рассматриваемой про- граммы. Обменная сортировка может быть выполнена с по- мощью следующей программы: procedure сортировка; for к := 1 to n - 1 do min := А(к); loc := к; for i := к + 1 to n do if min > A(i) then min := A(i); loc := i; A(loc) := A(k); A(k) :=min (Пример 5.1) end.
Алгоритмы 145 Внешний цикл выполняется п—1 раз. Внутренний цикл сраба- тывает в среднем га/2 раз для каждого вхождения во внешний цикл. Общее число обращений к внутреннему циклу равно (га2—л)/2. При больших значениях п за оценку этого выраже- ния принимается п2. Сложность можно оценить как по отношению к задаче, так и по отношению к алгоритму. Оценками времени работы алго- ритма являются максимальное, минимальное и среднее время его выполнения. В зависимости от используемых эвристик эти оценки могут совпадать или не совпадать. В приведенной про- цедуре сортировки они совпадают. Но если прекращать выпол- нение процедуры, как только будет установлено, что список упорядочен, временные оценки работы алгоритма не будут одинаковыми. Сложность задачи должна оцениваться по реа- лизациям правильных алгоритмов. Она представляет собой верхний предел для времени работы алгоритма. Но часто мож- но найти также и нижний предел. Очевидно, что для выполне- ния какого-либо вида обработки п элементов требуется время, по крайней -мере пропорциональное га. Для приведенного ниже простого алгоритма перемножения двух матриц размером гаХга число срабатываний внутреннего цикла равно га3: procedure умножение_матриц; for i := 1 to n do for j := 1 to n do C(i,j) := 0; for k := 1 to n do C(i,j) := C(i, j) + A(i,k) * B(k,j) (Пример 5.2) end. Из определения произведения матриц следует, что минималь- ное время работы также пропорционально п3. Изменения в ал- горитме, в результате которых уменьшается число срабатыва- ний внешних циклов, приведут к изменению коэффициента про- порциональности, но не вида зависимости от п. Оба рассмот- ренных выше алгоритма имеют полиномиальную оценку време- ни работы: п2 и га3. Однако алгоритм с оценкой п2 работает быстрее алгоритма с оценкой п3. Формально сложность алгоритма определяется как порядок функции, выражающей время его работы. Функции f(ra) и g(ri)—одного порядка, если для больших п существует кон- станта k, такая, что f (rt)/g(n) t^k. Это записывается следующим образом: f(n) =O(g(n)). Алгоритм чтения и занесения в память набора данных имеет оценку О (га). Алгоритм двоичного поиска в таблице с упорядоченными элементами оценивается как О (log2n). Простая обменная сортировка имеет оценку О (га2), а умножение матриц—О (га3). 10-399
<46 Глава 5 Таблица 5.1. Сложность алгоритмов п 0 (log2«) О(п) O(nlog2n) О(п2) О (2") О (и!) О (л") 1 0 1 0 1 2 1 1 5 2,3219 5 11,6096 25 32 120 3,125 10 3,3219 10 33,2193 100 1,024 3,6-10® 1010 20 4,3219 20 86,4386 400 106 2,4-Ю18 1026 30 4,9069 30 147,2067 900 109 2,640s2 2-Ю44 100 6,6439 100 664,3856 10000 Юзо 1051 Ю2°о Когда говорят, что алгоритм поиска имеет сложность О (log2«) или что сложность сортировки равна О (п2), под этим подразумевается, что элемент данных будет найден за время, в худшем случае пропорциональное log2/i, или что сор- тировка информации будет выполнена в самом лучшем случае за время, пропорциональное п2. В описанной процедуре сорти- ровки оценка О(п2) является как минимальной, так и макси- мальной. Сложность решения задачи можно уменьшить, если найти более выгодный метод (как, например, при замене ли- нейного поиска двоичным) или использовать оптимизирующие эвристики. Среднее время для оптимизированной сортировки по методу пузырька равно O(nlog2n). В таблице 5.1 показана зависимость различных видов слож- ности алгоритмов от возрастания п. Логарифмическая зависи- мость более приемлема, чем линейная, а линейная зависимость предпочтительнее полиномиальной и экспоненциальной. Рассмотрим игру между двумя игроками, которые по оче- реди получают ход, заключающийся в выборе одного из двух перемещений. Если игра заканчивается через п ходов, это озна- чает, что может быть разыграно всего 2п вариантов игры. Игрок, желающий найти на будущее наилучшую стратегию игры, может изучить все 2" возможных партий. Для этого по- требуется времени 0(2"), т. е. зависимость является экспонен- циальной. Из табл. 5.1 видно, что для всех задач, кроме самых коротких, экспоненциальная зависимость нежелательна. Даже для информационных массивов, имеющих средние размеры, тре- буются чрезвычайно большие затраты времени. Для больших же объемов данных нежелательна даже полиномиальная слож- ность. ИЗБЕГАЙТЕ СЛИШКОМ СЛОЖНЫХ АЛГОРИТМОВ 5.2. Способы реализации алгоритмов Целью любой программы является преобразование входных данных в выходные. Если отдельному выходному элементу со- ответствует свой входной элемент, основу программы составля-
Алгоритмы 147 ет конструкция повторений. Бели организация выходного пото- ка отличается от организации входного (например, если каж- дая карта на входе содержит 10 величин, а каждая выходная строка состоит из 8 величин или если входные записи идут друг за другом непрерывно, а на выходе блокируются по 20 за- писей на страницу), логичнее использовать две независимые сопрограммы: одну для получения выходных данных, другую — для их организации. Как показано выше, эти сопрограммы мо- гут быть реализованы путем инвертирования одной из них, в. результате чего образуется модуль с несколькими программны- ми секциями, используемыми другой сопрограммой. Если поря- док следования данных несуществен (например, при обновле- нии записей главного файла прямого доступа), можно исполь- зовать параллельную обработку. Перезапись данных, произво- димая в режиме on-line с помощью одновременно работающих терминалов, с точки зрения пользователей, находящихся за тер- миналами, выполняется параллельно. Координирование и упо- рядочивание реально выполняемых операций перезаписи могут производиться на любом уровне аппаратного и программного обеспечения систем управления. Перезапись данных, получае- мых с помощью независимых вычислений, может рассматри- ваться как параллельный процесс. Если же вычисления ориен- тированы на обработку файла сообщений, они выполняются скорее последовательно, чем параллельно. Все виды обработки могут быть разделены на следующие классы: последователь- ную, использующую повторения, структурное распараллелива- ние с помощью сопрограмм и произвольную обработку с приме- нением параллельных вычислений. 5.2.1. Итерация и рекурсия Повторение является основной управляющей конструкцией обработки данных, за исключением случаев, когда входные данные состоят из одного элемента, который преобразуется в один выходной элемент (например, в случае системы обработ- ки запросов или при обновлении записей в режиме on line). Су- ществуют две основные формы повторений: итерация и рекур- сия. Итерация в основном используется для тех видов обработ- ки, которые лучше всего определяются выражением типа «вы- полнить для всех х», а рекурсия — для получения результи- рующих данных, которые легче всего описать рекурсивно, т. е. задать выражением вида «выполнить то же, что и в последний раз». Текущее действие определяется с помощью предыдущего ответа или предыдущих стадий вычислений. В действительно- сти итерация и рекурсия взаимозаменяемы. Рекурсивные про- цедуры могут быть переписаны в итерационном виде в языках, в которых рекурсия невозможна (например, в Фортране).. 10»
148 Глава 5 Итерационные процедуры могут быть переписаны как рекур- сивные в языках, в которых невозможна итерация (например, в Лиспе). Классическим примером рекурсии может служить опреде- ление факториала в следующем виде: 1 для " = ° (5.1) ( п(п— 1)! для п> 0. Это определение (рекурсивно, так как задает функцию с ее же помощью. Все рекурсивные определения в основном представ- ляют собой множество выражений, из которых по крайней мере одно не является рекурсивным и обеспечивает окончание рекур- сивных вызовов. Аналогично этому для каждой итерации необ- ходимо наличие граничных условий. Другое, менее формальное определение факториала описывается следующим выражением: п! = 1-2.3...(п—1)-п. (5.2) Рекурсивная процедура, построенная на математической за- висимости (5.1), показана ниже: procedure факториал (п); (Пример 5.3) if гт = 0 then return(1) else return (п’ факториал (n - 1)) end. Следующая процедура реализует соотношение (5.2) и основана на итерационном алгоритме: procedure факториал (п); (Пример 5.4) f := 1; , к := п; while к > 0 do * f:=f«k; к := к- 1; return (f) end. В итерационной процедуре используется переменная k для под- счета числа возвратов от п до 0. В рекурсивных процедурах для той же цели предназначен аргумент функции. Выполнение операции умножения начинается со старших чисел в итераци- онной процедуре и с младших — в рекурсивной. Условия окон- чания выполнения обеих процедур аналогичны друг другу. Итерационная и рекурсивная процедуры могут применяться для простой задачи распечатки списка вводимых чисел, которая обычно решается итерационным способом. Итерационная про-
Алгоритмы 149 цедура содержит явно заданный цикл: procedure список; (Пример 5.5) while not конец файла do читать (число); печатать (число) end. Повторения рекурсивной процедуры получаются с помощью об- ращения ее к самой себе: procedure список; if not конец файла читать (число) печатать (число); список end. (Пример 5.6) Условие окончания обеих процедур одно и то же. Обе процедуры расчета факториала и обе процедуры печа- ти списка имеют линейную сложность. В том и другом случае итерационная форма более предпочтительна, так как для ре- курсивной процедуры требуются дополнительные расходы па- мяти и времени. При каждом рекурсивном вызове содержимое всех регистров машины сохраняется, а цри возврате — восста- навливается так же, как и при любом вызове подпрограммы. Кроме этого, должны сохраняться все локальные переменные до момента возврата, так как они могут быть изменены вызы- ваемой подпрограммой. Поскольку происходит многократное обращение из подпрограммы к самой себе, должно быть созда- но и сохраняться много копий регистров, переменных и точек возврата. Для хранения этой информации используется стеко- вая память. При возврате после соответствующего вызова ре- гистры восстанавливаются и вызов завершается. Управление возвращается к точке вызова столько раз, сколько была вызва- на подпрограмма. При обычном вызове подпрограмм глубина гнездования редко превышает 6. Глубина гнездования рекур- сивных вызовов обычно существенно больше. Рекурсию можно использовать только в тех языках программирования, в кото- рых для реализации вызовов подпрограмм используется стек. Вычисление биномиальных коэффициентов представляет собой простой пример расчетов, для которых существует много алгоритмов. Обычно биномиальные коэффициенты определяют по формуле п! J Й1 (п — Л)! • (5-3)
150 Глава 5 Алгоритм имеет линейную сложность, но, если вычисляются все три факториала, время его работы пропорционально 2га. Это время можно сократить в 2 раза, если учесть, что вычисления трех факториалов избыточны и k\ и (га—k)\ определяются во время расчета п\. Все, что надо сделать, — это рассчитать га! я в процессе вычислений определить и сохранить две другие ве- личины до его окончания. Реализация С(га, k) не рекурсивна, но вычисление факториала производится с помощью рекурсии или итерации. Трудности при вычислении л! и k\(n—k)\ заклю- чаются в том, что значения обоих факториалов слишком вели- ки и переполнение памяти при расчете С(п, k) возникает рань- ше, чем закончатся вычисления. Улучшенное математическое определение, позволяющее устранять данный недостаток, при- водится ниже: 1 для k = 0 C(n,k) = C(n.fe-l)(n-fe+l) для J < k < п & JL /V /4. (5-4) Этот алгоритм также может быть реализован и итерационным, и рекурсивным способом. Сложность его также является линей- ной, но скорее вида O(k), чем О (га). Другой способ определения биномиальных коэффициентов дает следующее выражение: C(ra,Jfe) = (1 дляй = 0иД=я I С(п— для 1 <6<га. Треугольник Паскаля, используемый для вычисления биноми- альных коэффициентов, заполняется с помощью этого рекур- сивного определения. Часть треугольника Паскаля показана в табл. 5.2. Рекурсивная реализация имеет сложность 0(2*), так как каждый вызов порождает два других вызова. Такая организация вычислений также избыточна, так как для полу- чения С (га—1, k—1) и С (га—1, k) необходимо вычислить С (га—2, k—1), рассчитываемое в каждом случае отдельно. При использовании итерационной процедуры можно избежать из- быточности в расчетах: Однако тогда требуется память для Таблица 5.2. Треугольник Паскаля 1 1 1 1 2 1 13 3 1 1 4 6 4 1 1 5 - 10 10 5 1 1 6 15 20 15 6 1
Алгоритмы 151 хранения k величин. За один цикл заполняется один ряд тре- угольника Паскаля, и этот ряд записывается на место преды- дущего. Первый аргумент функции указывает число повторных записей массива памяти, а второй обозначает номер позиции в этом массиве. Если запоминать весь треугольник, требуется массив размером пХ&. Этот алгоритм имеет сложность О (п2). Так как алгоритм имеет полиномиальную сложность, памяти требуется больше, чем для алгоритма, использующего одномер- ный массив, но он имеет преимущество перед последним, по- скольку k биномиальных коэффициентов для одномерного мас- сива и k-п коэффициентов для двумерного массива получают- ся при одинаковых затратах времени. Получение треугольника Паскаля иллюстрирует неодно- значность символа С (n,k), который может обозначать вызов функции или элемент двумерного или одномерного обновляе- мого массива. В языках программирования установлено разли- чие между записью обращений к функциям и ссылками к мас- сивам, поскольку существует различие в соответствующих про- граммных средствах обработки этих элементов. Однако в мате- матике указанные элементы часто обозначаются одинаково. При проектировании программ эти два вида записи могут ис- пользоваться как взаимозаменяемые. Параметр функции или индекс массива может обозначать индекс итерации или рекур- сии. В методе Ньютона — Рафсона для нахождения корней урав- нений вида хк—п=0 используется итерация. Обычно при опре- делении этого метода используются индексы, котррые можно применять в итерационной процедуре. Если требуется найти квадратный корень s из числа п, процедура задается следую- щим образом: для k = 0, п ф О sk для &>0 (5-6) Вычисление заканчивается при том значении индекса k, когда значения s*-i и s* приблизительно равны между собой. При итерационной реализации индексная переменная указывает на необходимое число проходов цикла вычислений. В случае ре- курсивной реализации индекс является аргументом вызывае- мой процедуры и определяет требуемое число рекурсивных вы- зовов. Если требуется сохранить все результаты вычислений
152 Глава 5 в массиве, индекс служит для адресации элементов массива и одновременно является индексной переменной итерационной процедуры. 5.2.2. Параллельная обработка На примере задачи обновления записей в файлах с прямым доступом можно наиболее просто проиллюстрировать приме- нение параллельной обработки. Теоретически можно допустить» что каждого пользователя обслуживает своя подпрограмма, вы- полняющая одну операцию перезаписи. Общим ресурсом в этом случае является только файл. При этом необходимо координи- ровать операции перезаписи. Трудности, связанные с парал- лельной обработкой, возникают при решении задачи координа- ции доступа к файлу. Одним из наиболее распространенных ви- дов процессоров для параллельной обработки служат матрич- ные процессоры. Имеется много видов матричной обработки для различных математических расчетов, логика которых позволяет парал- лельное выполнение. Если массив заполняется при инициали- зации одной и той же величиной или если в каждый инициали- зируемый элемент пересылается величина, зависящая только от положения элемента в массиве, порядок, в котором заполняют- ся в этой ситуации элементы, не имеет значения. Инициализа- ция может выполняться с помощью перечисления элементов массива по возрастанию их номеров, как показано в примере 5.7, или по их убыванию, как показано в примере 5.8. procedure инициализация (А); for i := n downto 1 do A(i) := 1 end. procedure инициализация (A); / for i := 1 to n do A(i) := 1 end. r (Пример 5.7) (Пример 5.8) Возможен любой другой порядок работы, поэтому операцию инициализации можно выполнять на п процессорах, запускае- мых в произвольном порядке, каждый из которых предназна- чен для инициализации одного элемента массива. Параллельная обработка может применяться для поиска в
Алгоритмы 153 неупорядоченном массиве с помощью одновременного доступа ко всем элементам, для обработки неупорядоченного набора данных, выполнения операций на различных ветвях программы и .проверки множества различных вариантов решений недетер- минированной задачи. Все эти операции выполняются одновре- менно с доступом ко всем элементам. 5.2.3. Сопрограммы Сопрограммы, так же как и параллельные процессы, выпол- няются одновременно. Их различие между собой заключается в том, что параллельные процессы стартуют в одно и .то же время и являются равнозначными по важности. Сопрограммы же состоят из головной программы и подчиненной ей подпро- граммы. Головная программа передает управление ее сопро- грамме. После этого управление многократно может переходить от сопрограммы к головной программе; пока в конечном счете оно не вернется к головной программе. При передаче управления сопрограмме последняя, как правило, продолжает выполняться с того места, на котором она была прервана, в отличие от под- программы, которая чаще начинает выполняться сначала. В многопроцессорных системах сопрограммы могут в дейст- вительности выполняться параллельно. Это возможно в огра- ниченных пределах в большинстве ЭВМ. Центральный процес- сор инициализирует функционирование процессоров, предназна- ченных для обмена, после чего продолжает работать одновре- менно с ними. Сигналы, передаваемые между центральным про- цессором и другими процессорами, служат для координации работы системы. Это характерно для задачи типа «поставщик — потребитель», которая является классическим примером взаимо- действия сопрограмм. Один процесс (Р) поставляет некоторый продукт, другой (С) этот продукт использует. Р может выпу- стить столько единиц продукта, сколько может разместиться в общей области хранения. Если же область хранения полна, то Р должен ждать С. С может использовать единицы продукта только в том случае, если они есть в области хранения. Если же она пуста, .то С должен ждать Р. Процесс начинается с того, что Р производит первую единицу продукта, а заканчивается, когда или С получил достаточное для него количество продукта, или у Р кончился материал, нужный для производства продукта, или, наконец, когда Р извещает С о прекращении работы. Управление сопрограммами можно рассмотреть на следую- щем примере. Пусть читаются числа, отперфорированные по 10 на каждой карте. Эти числа надо распечатать, расположив на строке по 8 чисел. Подпрограммы могут быть организованы так, что одна из них разблокировывает числа и передает их поодиночке другой подпрограмме, выполняющей блокировку
154 Глава 5 чисел для печати. Другой способ реализации этой операции основан на применении в качестве области хранения цикличе- ского буфера емкостью 40 чисел. Первая подпрограмма может разблокировывать числа и записывать их в буфер, а вторая при этом осуществляет блокировку чисел и их печать. Печать отчетов, имеющих заголовки страниц, представляет собой аналогичную ситуацию. Подпрограмма-поставщик гене- рирует выходные строки отчета из данных. Подпрограмма-по- требитель печатает отчет постранично, снабжая каждую стра- ницу заголовком и окончанием. Если в языке программирова- ния не предусмотрено взаимодействие сопрограмм, оно дости- гается путем инверсии программы-потребителя, что обеспечива- ет выполнение ее функции с помощью обращения к отдельным секциям инвертированной программы из программы-постав- щика. 5.3. Методы построения алгоритмов Из рассмотренных выше примеров сортировки, поиска в списке и вычисления биномиальных коэффициентов следует, что для решения задачи часто можно использовать разные ал- горитмы. Было также показано, что каждый алгоритм может быть реализован несколькими способами. Многие известные ал- горитмы приведены в томах 1—3 книги Кнута1), а также в «Сборнике алгоритмов АСМ». При разработке алгоритмов следует пользоваться стандартными подходами. Если метод ре- шения сначала не очевиден, то проблему следует проанализи- ровать, чтобы точно определить исходные условия и цели. Может оказаться, что проблема станет разрешимой или частич- но разрешимой, если будет использована некоторая дополни- тельная информация. Иногда проблему можно разбить на час- ти, которые проще поддаются решению. При определенных об- стоятельствах может быть использован такой подход: сначала отыскивается приближенное решение, которое затем уточня- ется. Способ пошагового уточнения и другие методы декомпози- ции программ определяют основу для построения программных модулей. Они обеспечивают методологию проектирования и удобные способы ведения записи в процессе разработки про- грамм путем ее разбиения на отдельные модули. Но эти мето- ды оказываются менее пригодными при проектировании тех модулей, которые предназначены для преобразования данных. Указанные методы не описывают всю информацию и структуры управления, необходимые при разработке модулей. D Кнут Д. Е. Искусство программирования для ЭВМ. Т. 1—3. — М.: Мир, 1976—1978.
Алгоритмы 155 5.3.1. Метод «разделяй и властвуй» Некоторые проблемы по природе носят аддитивный харак- тер. Такие проблемы можно разделить на части, и решение всей проблемы можно получить с помощью решения ее частей. При таком подходе можно использовать параллельную обра- ботку. Задание начальных значений элементов массива осуще- ствляется для всего массива и выполняется с помощью тех же начальных значений для частей массива. Матричные операции сложения и вычитания выполняются таким же способом, так как эти операции определены для отдельных элементов. На том же принципе строится и численное интегрирование. Аналогич- ным примером из экономики служитдвойной бухгалтерский учет, когда итоги по столбцам вычисляются для различных катего- рий, включающих дебиты и кредиты, после чего результаты в нужных сочетаниях сравниваются. Рис. 5.2. Площадь неправильного пятиугольника (A=B+C+D+E+F). Простым примером использования аддитивности служит за- дача вычисления площади неправильного многоугольника с по- мощью разбиения его на треугольники и вычисления их площа- дей по отдельности (рис. 5.2). Площадь всего многоугольника равна сумме площадей его частей. В сортировке слиянием, выполняющейся разделением п эле- ментов списка на две группы и с помощью сортировки каждой из них с последующим их слиянием, используется подход «раз- деляй и властвуй». Для сортировки каждой половины списка с помощью процедуры из примера 5.1 требуется (л2—2л)/4 сравнений, тогда как для полного списка нужно (л2—л)/2 сравнений. При слиянии упорядоченных частей требуется п—1 сравнений. Общее число сравнений равно (л2-)-2л—4)/4, т. е. при таком способе сортировки тратится в два раза меньше времени, чем при сортировке по методу пузырька.
156 Глава 5 Сортировка Шелла также основана на использовании прин- ципа «разделяй и властвуй». Сначала список разбивается на п/2 подсписков, содержащих по два элемента каждый. Под. списки сортируются. На втором внешнем цикле сортировка про- изводится над га/4 четырехэлементными, затем над п/8 восьми- элементными списками. На первый взгляд не очевидно, что на каждом цикле сортируется .различное число подсписков^ так как подсписки перемешиваются друг с другом: procedure сортировка Шелла (список); /Пример 5.9) число групп = п/2; while (число групп 1) do i := 1; j := 1 + число групп; white j < n do if список (i) > опиоок (j) • обменять(слиеок0),список(])); i :=i + 1; число групп:= число групп/2 end. Во многих ЭВМ деление чисел на части выполняется на аппаратном или на микропрограммном уровне. Вычисления мо- гут производиться каждый раз над одним байтом, при этом ис- пользуется прямой и обратный переносы между соседними па- рами байтов. Это тоже иллюстрирует принцип «разделяй и властвуй». Алгоритм двоичного поиска, однако, не является аддитив- ным, хотя в нем и используется разделение списка на части. Каждый раз берется одна из двух частей. Но при решении за- дачи поиска вершины в неупорядоченном дереве может быть использован алгоритм типа «.разделяй и властвуй». Дерево раз- деляется на поддеревья, и поиск осуществляется по всем под- деревьям. Каждое из поддеревьев может быть в свою очередь также разделено на части. Получаем, таким образом, рекур- сивную процедуру. Если дерево — двоичное, поиск по нему
Алгоритмы 157 производится с помощью следующей процедуры: procedure поиск (дерево); (Пример 5.10) if деревом nil return (неудача) else 1Тдерево_вершина=«ершина_требуемая return (удача) else искать (левое_поддерево); if удача return (удача) else искать (правое_поддерево); if удача return (удача) else return (неудача) end. Поиск в левом поддереве и поиск в правом имеют одинаковый приоритет. При параллельной обработке они могут быть иссле- дованы одновременно. РАЗБИВАЙТЕ ЗАДАЧУ НА ЧАСТИ Аддитивный подход при решении задачи поиска пути в ла- биринте, показанном на рис. 5.3, а, иллюстрируется на рис. 5.3,6. Лабиринт разделяется на четыре части и через каж- Рис. 5.3. Лабиринт. а — вход и выход; б — четыре части лабиринта.
si 58 Глава 5 дую из них отыскивается путь. Затем конечные точки сравни- ваются между собой. Объединяя в каждом из квадрантов сооб- щающиеся между собой входные и выходные точки (рис. 5.3, б), можно получить следующие группы: а) А, В, D б) С в) Е, F, G, Н, I, J, К г) L, М, N, О Д) Р е) Q, R, S, Z. Вход в лабиринт отмечен квадратом А, а выход — квадра- том Z. Смежные квадраты соединяются между собой по сле- дующим конечным точкам: В—F, С—L, Н—Р, К—Q, М—Р, О—S. Они соединяют группу а), содержащую вход, с груп- пой е), содержащей выход, через группу в), т. е. (В—F—К— Q), или через группы в), г) и д) (В—F—Н—Р—М—О—S), •определяя, таким образом, два различных пути. 5.3.2. Метод последовательных приближений Когда известно приближенное решение и есть способы для •его уточнения, можно использовать подход, описанный ниже. Начиная с исходного приближения f0 производится последова- тельное уточнение решения fi, затем уточнение решений fz, {з и т. д. Этот ряд представляет собой последовательность при- ближенных решений, которые или сходятся к действительному решению, или приближаются к нему настолько, насколько это нужно для практического использования. Каждый член после- довательности может зависеть или только от предшествующего ему члена, или от всех более ранних приближений. Метод Ньютона — Рафсона для вычисления квадратного корня (соотношение (5.6)) представляет собой пример такого подхода. Последовательные приближения используются также для математических функций, обычно представляемых такими рядами, как уЗ у5 Г у7 sin(x)-x-i-+i-i- + .... (5.7) Частичные суммы представляют собой сходящийся ряд прибли- жений функции синуса. Они могут быть вычислены рекурсивно; при этом члены ряда задаются также с помощью рекурсивного
Алгоритмы 159* соотношения: sin to-/ tk для & = 0, ЫПь (л J — < I для k> 0, ’ x для k = 0, 2Л(2Л+\) ДЛЯ k > °' (5.8> Каждый из членов зависит только от предшествующего, каж- дое новое приближение к функции синуса соответственно зави- сит от ранее полученного приближения и нового члена ряда. Итерационная реализация более эффективна, чем рекурсивная, так как при использовании рекурсии приходится выполнять из- быточные вычисления членов ряда. К сожалению, эти ряды при. больших значениях х сходятся слишком медленно, и поэтому их нельзя использовать в пакетах подпрограмм для вычисления, функции синуса. Рис. 5.4. Приближенное вычис- ление определенного интеграла. A-D-B-F-G-H-P-M-N-L-C-L-O-S-R-Q-K-E-F-E-K-Q-^ Рис. 5.5. Путь через лабиринт. Другим примером сходящихся приближений служит числен- ное интегрирование. Определенный интеграл представляет со- бой площадь между кривой, изображающей функцию, и осью абсцисс. Для вычисления интеграла отрезок, на котором он оп- ределен, делится на равные части, и на каждой из этих частей вычисляется приближение с помощью значения /(хй)Дх: (рис. 5.4), где Xk — некоторое значение х в пределах k-й час- ти, а Дх— размер каждой из частей отрезка. Затем произво- дится дальнейшее разбиение частей на более мелкие и вычис- ляется следующее приближение. Когда два приближения до- статочно близки друг к другу, то решение найдено. В этом при.*-
160 Глава 5 мере использовались два различных способа построения алго- ритма: аддитивный метод применялся для нахождения после- довательных приближений к значению интеграла. ХОРОШЕЕ РЕШЕНИЕ ЧАСТО МОЖЕТ БЫТЬ УЛУЧШЕНО Метод последовательных приближений можно использовать, если известно, что существует такая формула, которая обеспе- чивает сходимость к желаемой величине. Затем, когда два при- ближенных ответа имеют достаточно близкие значения, они принимаются примерно одинаково близкими к действительному решению. Этот метод при численных расчетах следует исполь- зовать очень осторожно, так как влияние погрешностей вычис- лений на ЭВМ возрастает со временем. На рис. 5.5 приведен снова тот же лабиринт, что и на рис. 5.3. Если требуется найти кратчайший путь через лаби- ринт и первым найденным приближенным решением является маршрут, показанный на рисунке, с помощью последовательно- го исключения замкнутых частей пути, а также циклов и воз- вратов его можно все более укорачивать, при этом будет полу- чаться каждый раз более точное приближение к кратчайшему пути. Наилучшее решение может быть найдено или не найдено в зависимости от того, насколько удачно выбран способ удале- ния лишних частей маршрута. Не существует другого метода, который позволил бы уменьшить длину пути, кроме эвристиче- ского. Изображеный путь проходит через 53 квадрата. Исклю- чение возвратов через D и L—С—L уменьшает длину пути до 49, а затем до 45. Удаление возврата через Q—К—Е—F—Е— К—Q позволяет сократить путь до 27 квадратов. Но более пра- вильным решением здесь было бы исключение большой замкну- той части маршрута, начинающейся и заканчивающейся в квад- рате F. При этом остается путь А—В—F—Е—К—Q—Z длиной 17 квадратов. Но даже это решение может быть улучшено, если найти более короткий путь из F в К. Наилучшим реше- нием является путь А—В—F—G—Н—I—J—К—Q—Z, имею- щий длину 15 квадратов. Каждый путь через лабиринт являет- ся приближением кратчайшего пути, т. е. в этом примере также используется 'метод последовательных приближений. 5.3.3. Метод намскорейшего спуска Название этого метода отражает заложенный в него прин- цип: для того чтобы достигнуть дна, требуется лишь идти вниз. Следует отметить, что движение вниз не гарантирует достиже- ния самой низкой точки. Вместо этого отыскивается дно пер- вого обнаруженного углубления. В начале поиска необходимо очень аккуратно выбирать исходную точку. Рассматриваемый метод позволяет получить хороший результат при решении за-'
Алгоритмы 161 дачи, когда нельзя найти оптимальное решение, потому что получение наилучшего решения или невозможно, или обходит- ся слишком дорого. Метод наискорейшего спуска отличается от метода последо- вательных приближений тем, что во втором случае за исходное берется любое приближение, которое затем улучшается. В рас- сматриваемом же методе направление каждого шага планиру- ется так, чтобы он был направлен в сторону нужного решения. Использование метода последовательных приближений при соз- дании программ заключается в том, что, принимая какую-либо работающую программу за исходную, производится ее последо- вательное улучшение с помощью отладки и настройки. Способ Рис. 5.6. Граф задачи о коммивояжере. наискорейшего спуска применяется в методах нисходящего проектирования и частично в методе пошагового уточнения. Задача коммивояжера представляет собой классический пример задачи, которую легко поставить, но очень трудно ре- шить. Коммивояжер должен посетить некоторое множество городов, и задача заключается в том, чтобы найти кратчайший маршрут, следуя которому он может попасть во все города не более одного раза и вернуться в исходную точку. Во все города можно попасть из любого другого или по прямому пути между ними, или через другие города. На рис. 5.6 показана постанов- ка одной такой задачи. Города обозначены буквами. Город А является исходным пунктом маршрута. Цифрами указаны от- носительные раостоямия между городами. Решение данной за- дачи выполняется с помощью недетерминированного алгорит- ма. Один из возможных способов получения решения заключа- ется в генерации всех перестановок городов, когда А — исход- ный путь маршрута. При этом возможны такие комбинации, в которых рядом будут расположены города, не имеющие пря- мых путей между «ими (т. е. в некоторые города коммивоя- жер попадет несколько раз). Устранив эти комбинации, с по- мощью вычисления длины маршрутов среди оставшихся можно определить кратчайший из них. Сложность алгоритма равна О(п!). Почти во всех реальных случаях эта задача становится Для слишком большого числа городов, чтобы ее можно было решить таким способом на ЭВМ за приемлемое время. 11—399
162 Глава 5 Хорошее, но не обязательно оптимальное решение может быть найдено с помощью 'метода наискорейшего спуска. Начи- ная с города А, следующим для посещения выбирается бли- жайший другой город. От этого города берется следующий участок маршрута, представляемый кратчайшим расстоянием между этим городом и еще не посещенными городами. Этот процесс продолжается, пока не будут пройдены все города. Данный алгоритм имеет полиномиальную сложность. С по- мощью описанного метода после нахождения частичного пути А—В—Е (рис. 5.6) следует сделать выбор между Е—С и Е—D. Если выбран участок Е—С, получится маршрут А—В— Е—С—F—D—А, длина которого составляет 16. Этот путь пред- ставляет оптимальное решение для такого выбора. В том слу- чае, когда выбран участок Е—D, маршрут найти нельзя, если только не устранить ограничение, по которому запрещается по- сещать один и тот же город дважды. На самом деле, если снять это ограничение, можно использовать более короткий путь, проходящий от F к D через С и Е. При этом общая длина маршрута будет равна 14. Нахождение кратчайшего пути в лабиринте можно рассмат- ривать как разновидность задачи о коммивояжере. Вместо ус- ловия, что каждый из городов должен войти в маршрут только один раз, ставится условие нахождения такого подмножества городов, по которым можно провести маршрут, соединяющий начальный и конечный города (соответствующие входу и выхо- ду лабиринта) кратчайшим путем и включающий каждый город только один раз. Диагональ, показанная на рис. 5.7, а жирной штриховой линией, задает главное направление от входа в ла- биринт А к выходу из него Z. Применяя стратегию наискорей- шего спуска для поиска маршрута, можно использовать это направление как компас. Штриховой линией показан получен- ный в конечном результате маршрут. Его общая длина рав- на 27, а после исключения из маршрута возвратов уменьшает- ся до 23. Другой алгоритм поиска кратчайшего пути в лабирин- те, использующий метод наискорейшего спуска, основан на ну- мерации всех позиций, находящихся на расстоянии одного квад- рата от входа, затем всех позиций, находящихся на расстоянии двух квадратов и т. д., пока не будет достигнут выход, Прону- мерованнный лабиринт показан на рис. 5.7,6. Кратчайший путь может быть получен при движении в обратную сторону от выхода. На рис. 5.7, в вход, выход и все узловые точки помечены буквами. Кратчайший путь через лабиринт соответствует крат- чайшему пути между А и Z по графу, показанному на рис. 5.10. Этот метод, используемый при анализе графа, показанного на рис. 5.6, позволяет получить маршрут А—В—С—D—Е—F—G—
Алгоритмы'' 163 Рис. 5.7. Определение пути через лабиринт. . а — с помощью главного направления; б — с помощью анализа расстояний; в — с по- мощью анализа узловых пунктов. J—М—Z, представляющий один из двух одинаково оптималь- ных путей. Применение рассматриваемого метода в математике можно проиллюстрировать на примере использования последователь- ных приближений, которые сами по себе не являются аппрок- симациями требуемой величины. Таким примером служит ряд чисел Фибоначчи 1, 1, 2, 3, 5, 8, 13, 21, 34,..., каждое из кото- рых является суммой двух предыдущих. Эту последователь- ность можно записать в виде F(fe) = (1 Для k =1,2, 5 \F(k— 2)4-F(£— 1) для 6>2. 11*
>64 Глава 5 к-е число последовательности отыскивается с помощью вычис- ления всех предшествующих чисел, ни одно из которых не яв- ляется его приближением. Ряд, образуемый этими числами, не сходится, а расходится. То же можно сказать и о последова- тельностях для получения биноминальных коэффициентов, за- даваемых соотношениями (5.4) и (5.5). При использовании ка- кого-либо из этих рекурсивных определений значение каждого биноминального коэффициента получается с помощью вычисле- ния последовательности значений, заканчивающегося одновре- менно с получением требуемого коэффициента. ХОРОШЕЕ РЕШЕНИЕ ЛУЧШЕ, ЧЕМ НИКАКОЕ 5.3.4. Метод обратного прохода Этим методом можно решить некоторые хорошо известные головоломки. Пусть имеются два (резервуара; объем одного из них измеряется пятью, а объем другого — девятью чашками. Требуется с помощью этих резервуаров отмерить 6 чашек воды. Для решения задачи можно применить метод обратного про- хода. Если можно было бы отмерить одну чашку воды, то, вы- лив ее в больший резервуар и добавив в него воду из другого резервуара объемом 5 чашек, можно было бы получить тре- буемый объем воды. Одна чашка воды может быть отмерена, если из полного меньшего резервуара отлить 4 чашки воды. Это можно сделать, если в большем резервуаре уже имеется 5 чашек воды. Таким образом, если наполнить меньший резер- вуар дважды и вылить эту воду в больший, в меньшем останет- ся 1 чашка воды. Вода из большего резервуара выплескивает- ся, и в него переливается оставшаяся 1 чашка из малого резер- вуара, из которого затем переливается еще 5 чашек воды. Та- ким образом найдена последовательность действий, соответст- вующая решению задачи, путем обратного прохода. В другой задаче надо определить, сколько прыжков потре- буется лягушке, чтобы выбраться из ямы глубиной 1 м, если за один прыжок лягушка поднимается на 30 см, а между прыж- ками сползает вниз на 20 см. Прослеживая путь лягушки в об- ратную сторону, находим, что она сможет выпрыгнуть из ямы, поднявшись на 70 см. Поскольку после каждого прыжка она поднимается на 10 см, ей потребуется 7 прыжков, чтобы под- няться на 70 см. На восьмом прыжке лягушка выбирается на- ружу. Метод обратного прохода применяется тогда, когда задан порядок (направление) решения некоторой задачи; замена это- го направления на обратное может помочь упростить задачу без .ее изменения. Этот метод можно использовать не всегда. Выдача последовательности чисел от 1 до 10 направленна, но
Алгоритмы 165 необратима, так как перемена направления этой процедуры из- менит результат. Задача нахождения всех простых чисел меж- ду 1 и 100 направленна и обратима, но, если начинать со стар- ших чисел, решение ее существенно усложнится. Задача поис- ка пути в лабиринте направленна и обратима, если не осущест- вляется истинный поиск в «реальном» лабиринте. Однако, если за исходную точку взять выход и находить путь ко входу, такая задача аналогична первоначальной. 5.3.5. Метод динамического программирования. Метод динамического программирования заключается в одновременном использовании прямого и обратного проходов для решения задачи. Перемещение в одном направлении позво- ляет получить одно из возможных решений. Движение в дру- гом направлении обеспечивает альтернативное решение. После сравнения этих решений выбирается лучшее из них. Классиче- ским примером использования этого метода является решение варианта задачи о коммивояжере, в котором требуется найти кратчайшее расстояние между двумя пунктами. Эти пункты могут обозначать один и тот же город, при этом найденный путь представляет кратчайший циклический маршрут, следуя которому коммивояжер не повторяет ни один из участков пути. На графе, приведенном на рис. 5.8, точками 1 и 9 отмечены соответственно исходный и конечный пункты маршрута. До- пустим, что для каждого отрезка задана его длина. Применение обратного прохода для решения этой задачи заключается в том, что перед получением кратчайшего пути между точками 1 и 9 определяются кратчайшие пути от точки 2 к точке 9 и от точки 3 к точке 9. Для нахождения этих маршрутов необходи- мо определить кратчайшие пути из точек 4, 5 и 6 к точке 9. Шаг 1. Анализ путей, ведущих к конечному пункту из со- седних: 7—9, 8—9. Шаг 2. Анализ путей, ведущих к конечному пункту и имею- щих по одному промежуточному: 4—7—9, 5—7—9, 5—8—9, 6—8—9. Шаг 3. Анализ путей с двумя промежуточными пунктами: 2—4—7—9, 2—5—?—9, 3—5—?—9, 3—6—8—9. Шаг 4. Анализ путей с тремя промежуточными пунктами: 1—2—?—?—9, 1—3—?—?—9. Длина путей вычисляется одновременно с определением маршрутов. Таким образом, путь 2—5—?—9, анализируемый на третьем шаге, не длиннее путей 2—5—7—9 и 2—5—8—9, поскольку при его определении используется кратчайший из пу- тей 5—7—9 и 5—8—9, полученный на втором шаге. Перед выполнением шага 4 уже известны кратчайшие маршруты меж-
166 Глава 5 программирования. Рис. 5.9. Дерево возможных решений задачи о коммивояжере. ду 2 и 9, а также между 3 и 9, в результате чего может быть определен кратчайший путь между 1 и 9. С помощью этого метода может быть найдено хорошее ре- шение для задачи о коммивояжере, иллюстрируемой рис. 5.6. Из заданного множества городов наиболее удалены друг от друга города А и F. При поиске кратчайшего пути между А и F обратным методом динамического программирования необ- ходимо рассматривать варианты маршрута, ведущие из А через В или через D. Если известны длины кратчайших путей от В
Алгоритмы 167 до F и от D до F, можно вычислить длину путей из А через В и через D, что позволит определить выбор между этими двумя городами. Длина пути от В к F будет известна, если найдены расстояния по кратчайшим путям от городов С и Е до F. На рис. 5.9 показано дерево рассматриваемых путей, демонстри- рующее работу метода динамического программирования. Жир- ными линиями отмечены наилучшие выбираемые варианты участков пути для каждого пункта, начиная с нижнего уровня дерева. Найдены два пути с минимальной длиной: А—В—Е— С—F и А—D—Е—С—F. По случайному совпадению эти марш- руты включают все города. Циклический маршрут через все Рис. 5.10. Граф для решения задачи нахождения кратчайшего пути через ла- биринт. города может быть получен с помощью объединения этих двух минимальных путей. Для исключения городов, вошедших в маршрут несколько раз, а также для включения в него про- пущенных городов, если такие появляются, можно использовать эвристические методы. Применение этого способа решения поз- воляет получить хороший, но не обязательно лучший маршрут. Продемонстрируем использование описанного метода на примере нахождения кратчайшего пути через лабиринт, пока- занный на рис. 5.7. Граф, соответствующий данному, процессу решения и немного упрощенный с помощью удаления квадра- тов В и Н, приведен на рис. 5.10. Этот пример иллюстрирует прямой метод динамического программирования. Шаг 1: Путь A —G A — D Длина 13 7 Шаг 2: — G — J 15 • • — G —F 15 (отбросить) • • • — D — F 9 • • • — D — Е 8 Если отбросить более длинные пути к F, останутся те из них, которые состоят из участков, проходящих через G и D (после первого шага) и ведущих к J, F и Е (после второго шага).
168 Глава 5 Шаг 3: ---- — J —М 16 ... —J —L 17 — F — G 11 ... —F — Е 10 (отбросить) ... —Е —F 9 (отбросить) — Е — I 13 Пути к F, полученные на втором и третьем шагах, имеют равную длину. Оба из них можно сохранить или отбросить один из них, что и было сделано. Новый путь к Е был отбро- шен, так как более ранний путь к тому же квадрату короче. Шаг 4: — М — Z 17 — М —L 17 (отбросить) — L-—М 18 (отбросить) -L-K 24 (отбросить) — G —J 13 — Е — I 13 (отбросить) -I-K 14 Путь к Z найден, но не были просмотрены другие пути, поэтому процесс продолжается. Шаг 5: — j—м 14 — J — L 15 -K-L 21 (отбросить) Шаг 6: — М — Z 15 — L — М 16 (отбросить) -L-K 22 (отбросить) В качестве решения принимается тот путь, который был полу- чен раньше, если он короче других, или самый короткий из всех полученных в конечном итоге путей. Действительный путь по- лучается с помощью обратного прохода по цепочке Z—М—J— G—F—D—[С]—А. 5.3.6. Метод поиска с возвратом Для многих задач нет других способов решения, кроме ме- тода проб и ошибок. Динамическое программирование пред- ставляет собой один из способов решения подобных задач. Де- рево является наиболее наглядным способом изображения мно- жества возможных решений, даже если метод выбора не имеет явно выраженной древовидной структуры. При решении зада- чи о коммивояжере методом динамического программирования (граф этого решения показан на рис. 5.8) выбор нужного марш- рута осуществлялся с использованием метода поиска в глубину по дереву возможных вариантов. В ранее рассмотренном ре- шении задачи нахождения кратчайшего пути через лабиринт прямым методом динамического программирования использо- вался способ поиска в ширину с отбрасыванием лишних ва- риантов т. е. когда становится очевидно, что некоторые из маршрутов слишком длинны; они отбрасываются, поэтому до
Алгоритмы 169 конца просматриваются не все возможные пути. Метод поиска в глубину состоит в том, что, когда, согласно дереву вариан- тов, получено решение, осуществляется возврат к той точке, где был выбран один из альтернативных 1вариантов. После этого генерируется другой путь и из полученных двух маршрутов выбирается кратчайший, а затем выполняется следующий воз- врат и т. д. Если п — число уровней сбалансированного дерева, слож- ность поиска поэтому дереву будет зависеть от п по экспонен- циальному закону. Для дерева, содержащего п узлов, слож- ность поиска зависит по квадратичному закону. Для уменьше- ния числа уровней иерархии дерева или для исключения беспо- лезных его ветвей можно использовать эвристические методы. Если дерево изображает множество всех путей через лабиринт, многие из путей должны оканчиваться в вершинах, представ- ляющих тупики лабиринта, другие же могут привести к прой- денным уже квадратам. Такие пути можно исключить, если использовать только смежные квадраты лабиринта. Данный способ позволяет также уменьшить число уровней дерева ре- шений. Комбинируя процессы генерации возможных ветвей де- рева с процессом поиска пути, можно прекратить генерацию таких ветвей, которые не будут затем рассматриваться. Любой метод, использующий поиск с возвратом и попытки повторных решений, можно представить в виде дерева поиска. При рекурсивном поиске автоматически используется метод по- иска с возвратом, что можно показать на примере следующей процедуры поиска пути прохода сквозь лабиринт. procedure искать-в_ лабиринте (путь) (Пример 5.11) repeat следовать-путь until тупик or развилкаог были—здесь_раньше or достигнута—цель; if развилка if правый_путь искать—в_лабиринте (правый—путь); if не_достигнута—цель if левый-путь искать—в_лабиринте (левый—путь); if не_достигнута—цель if прямой—путь искать—в—лабиринте (прямой_путь) end.
170 Глава 5 Если во время каждого вызова процедуры печатать обозначе- ние рассматриваемого квадрата лабиринта, будет отпечатай весь путь, проходимый при поиске. Если аналогичную печать выполнять при каждом выходе из процедуры, когда достигнут выход из лабиринта, в результате будет напечатай путь через лабиринт, направленный в обратную сторону (от выхода к вхо- ду), без тупиков и циклов, которые цроходятся при поиске. Но этот путь не обязательно будет наикратчайшим. Данный пример иллюстрирует метод поиска с возвратом к точке альтернативного выбора, обеспечивающий проверку раз- личных возможных вариантов решения. С помощью рекурсии в ЭВМ запоминаются предыдущие со- стояния, что затем обеспечивает воз- врат к ним. Если программист не ис- пользует рекурсивные обращения, он должен запоминать состояние в каждой точке альтернативного выбора в стеке, а затем для возврата к этой точке из- влекать описание соответствующего со- стояния из этого стека. Метод поиска с возвратом можно ис- пользовать для решения задачи о вось- ми ферзях, для построения кроссвордов из списка слов, для анализа шахматных Рис. 5.11. Синтаксическое ходов, для решения задачи о раскраске дерево. карты четырьмя цветами и т. д. Этот метод используется также для решения такой простой задачи, как вычисление арифметического выра- жения. Пусть задано следующее арифметическое выражение: 2 + Зх(4 + 5) —6. Если оно сканируется слева направо, после сложения 4 и 5 ЭВМ должна вернуться назад и выполнить умножение ре- зультата на 3, затем вернуться назад еще tpas и произвести сложение с 2. Указанное выражение может быть представлено в виде синтаксического дерева, показанного на рис. 5.11. Вы- числение выполняется с помощью поиска по синтаксическому дереву в глубину. Чтобы избежать повторного просмотра пер- вой части рассматриваемого выражения, требуется запомнить символы в стеке в том порядке, в каком они были считаны, или необходимо использовать набор рекурсивных процедур, напри- мер таких, какие описаны ниже: (Пример 5.12) procedure выражение; {вычислить выражение} значение: = 0; оп: = " +
Алгоритмы 171 значение, следующая—оп:-суммма-произведений (значение, оп); if следующая—оп=конец-выражения геиип(значение, следующая-оп); if следующая—оп = ”)” следующая—оп:=взять— символ; return (значение, следующая—оп) end. procedure сумма—произведений {вернуть сумму произведений} - repeat (значение, оп) символ:=взять—символ; {принять операнд} if символ = ”(” число:«выражение {вычислить новое выражение} else число:= символ; следующая_оп:=взять символ;{принять оператор} if следующая—оп = ” * "or следующая—оп = число:=произведение—членов (число, следующая—оп); if оп=" + " значение: = значение + число if on = — значение:=значение—число оп:= следующая-оп until оп = ")” or оп=конец—выражения; return (значение, оп) end. procedure произведение—членов {вернуть сумму произведений} repeat (величина, оп) символа взять—символ {принять операнд} if символ = "(" числом выражение {вычислить новое выражение} else число: = символ if оп= ” * " значение: = значение * число; if оп = значение: = значение/число; оп :=следующая—оп until оп = ")" or оп = "+" or on = or on = конец—выражения; return (значение, on) end.
172 Глава 5 Более простой случай применения метода поиска с возвра- том можно продемонстрировать на примере пакетной обработ- ки, когда весь пакет информации забраковывается, если в нем обнаружены какие-либо неверные данные. Единицы пакета не обрабатываются (выполняется только проверка на наличие ошибок), но должны быть сохранены до тех пор, пока не бу- дет закончена проверка всего пакета. Затем необходимо вер- нуться к началу пакета и обработать все данные (правильные и неправильные), Часто, если обрабатывается большой объем данных, пакеты запоминаются в определенном порядке и все возвраты производятся к началу сохраненной информации. Использование в этом случае стековой памяти было бы беспо- лезным усложнением. Для запоминания пакета последователь- ных данных достаточно использовать простой информационный массив или файл. Использование управляющих полей для группирования дан- ных также требует применения возвратов. Смена группы дан- ных не может быть определена до тех пор, пока не будет про- читана первая запись следующей группы и не будет обнаруже- но, что она содержит другое значение в управляющем поле. По логике процесса эта запись не должна быть прочитана, пока не закончится обработка предыдущей группы. На практике же эта прочитанная уже запись сохраняется до момента, когда она логически должна была бы появиться. Аналогичная ситуация возникает, если во время работы пакетного компилятора или операционной системы оказывается, что пропущена карта кон- ца файла и вместо нее прочитана управляющая карта. В этом случае возврат осуществляется назад на одну карту. Использование точек контроля за работой программы с большим временем счета приводит к аналогичной, но более сложной ситуации. В случае сбоя ЭВМ каждый файл должен быть восстановлен до того состояния, в каком он находился в момент прохождения последней контрольной точки, и после этого обработка записей файлов, предшествующих сбою, по- вторяется. 5.3.7. Метод выделения подцелей Многие из приведенных в этой главе примеров могут иллю- стрировать метод выделения подцелей, который заключается в разбиении задачи на отдельные подзадачи. С помощью этих подзадач исходную задачу можно решить по частям или ее упростить и решить. Идея использования подцелей пришла из математики, где доказательство некоторой теоремы А осущест- вляется следующим образом: Доказательство А следует из истинности В. Доказательство В следует из истинности С.
Алгоритмы 173 Доказательство С следует из истинности D. Теорема D может быть доказана. Реальное доказательство проводится от D к С, затем к В и затем к А. Использование подхода «разделяй и властвуй» может слу- жить примером применения метода выделения подцелей, если, например, процесс решения имеет следующий вид: Подцели: разделить задачу на части; решить каждую часть. Цель: объединить все частичные решения вместе, чтобы по- лучить решение всей задачи. В методе последовательных приближений также используются две подцели. Подцели: найти приближенное решение; уточнить его. Вторая из этих подцелей повторяется до тех пор, пока не будет получено достаточно точное приближение. В методе на- искорейшего спуска используется единственная подцель — про- двинуться на один шаг дальше, и эта подцель повторяется, по- ка не будет достигнута такая позиция, из которой дальнейшее продвижение невозможно. Применение метода поиска с возвратом для различных эври- стических способов упрощения дерева поиска решения пред- ставляет собой пример использования метода определения под- целей не путем разбиения задачи на части, а с помощью ее мо- дификации. Если для поиска кратчайшего пути через лабиринт применяется метод определения подцелей, во время фазы уста- новления целей анализ задачи может выполняться следующим образом: а) кратчайший путь может быть найден, если известно, ка- кие узлы расположены на нем и каким путем можно попасть к каждому из них; б) вычеркиваются недостижимые со стороны входа узлы; в) направления, ведущие в тупики или к рассматриваемым в настоящий момент узлам, вычеркиваются; г) тупики исключаются путем последовательного удаления позиций, имеющих только одну соседнюю; д) лишние направления исключаются, если удалить все на- правления между двумя соседними узлами, кроме кратчайших прямых. Такой способ предварительного анализа лабиринта позволяет получить кратчайший путь следующим образом: 1. Вычеркнуть все позиции, имеющие только одну соседнюю. 2. Найти вход, выход и все узлы. 3. Определить и измерить прямые пути между соседними узлами. 4. Вычеркнуть все пути между соседними узлами, кроме кратчайших.
174 Глава 5 5. Вычеркнуть узлы, из которых возможно движение только в двух направлениях. На рис. 5.12, а показан лабиринт, упрощенный описанным выше способом. Оставшиеся узлы отмечены буквами. Граф, указы- вающий расстояния между узлами, показан на рис. 5.12,6. Чис- ло путей, соответствующих этому графу, может ‘быть еще боль- Рис. 5.12. Упрощенный лабиринт (а) и граф (б). ше уменьшено с помощью следующего шага (6) и повторного применения всех шагов, начиная с шага 1. 6. Удалить прямые пути между каждыми двумя узлами, если между ними есть более короткие непрямые пути. 7. Выбрать кратчайший оставшийся путь. Примером применения метода выделения подцелей служит хорошо известная универсальная программа решения задач GPS, созданная Ньюэллом, Шоу, Саймоном и Эрнстом. Для этой программы описание среды задается с помощью опреде- ления объектов и операций над ними. Задается также описание желаемой среды. Программа должна рассчитать, как выпол- нить необходимые преобразования. Например, может быть за- дано множество аксиом, а также правила, позволяющие про- изводить преобразования этих аксиом, и при этом требуется
Алгоритмы 175 доказать простые математические теоремы. Могут быть заданы какие-либо логические головоломки, например задача о мис- сионерах и каннибалах. В этой задаче требуется определить, как переправить трех миссионеров и трех каннибалов, подо- шедших к реке, с одного берега 1на другой на лодке, способной поднять только двух человек. По условию задачи во время перевоза число каннибалов на каждом берегу не должно пре- вышать числа миссионеров. Программа GPS решает эту зада- чу автоматически с помощью метода выделения подцелей, ре- шая сначала соответствующие им задачи. 5.3.8. Метод моделирования Рассматриваемый здесь метод отличается от моделирования физических систем. Применение последнего заключается не столько в получении решения некоторой задачи, сколько в сбо- ре и анализе статистических данных о некоторых коэффициен- тах. Полученные данные могут быть использованы для выпол- нения дедуктивных выводов о системе или для улучшения про- цесса моделирования в заданном случае. Некоторые примеры применения метода моделирования уже рассматривались выше. К таким задачам относятся задачи о поставщике и потребите- ле, о читателях и писателях1), а также игровые задачи. В том смысле, что ЭВМ все время делает то, что обычно выполняют люди, процесс счета представляет собой моделирование. Одна- ко этот термин чаще употребляется в том случае, когда глав- ным является сам процесс, а не его результат. Моделирование дискретных событий и моделирование пере- ходов состояний представляют собой близкие по типу задачи. В обоих случаях проблемная среда описывается как совокуп- ность функциональных единиц и действий. Действия произво- дятся не непрерывно, а лишь в дискретные моменты времени, приводя к изменениям в среде. В задаче типа моделирования переходов состояний определяются по крайней мере п состоя- ний среды, где п сравнительно мало, и последовательность дей- ствий устанавливается в соответствии со средой. В задачах мо- делирования дискретных событий рассматривается большое число состояний или используется случайная величина. и Задача о читателях и писателях моделирует конфликтную ситуацию, часто встречающуюся при использовании несколькими одновременно работа- ющими программами одной области памяти. Программы, которым необходимо осуществить запись, называются писателями. Программы, пытающиеся прочи- тать информацию из этой области, называются читателями. Обычно в этой задаче ставится условие, согласно которому во время доступа к области па- мяти какого-либо писателя для остальных писателей и читателей эта область является недоступной. Чтение из указанной области памяти может произво- диться произвольным числом читателей. — Прим, перев.
176 Глава 5 В большинстве игровых задач решение может быть получе- но с помощью моделирования дискретных событий. Число возможных состояний, определяемых, например, расположением фишек на игровом поле, является конечным, но слишком боль- шим для того, чтобы только с их помощью игрок мог пол- ностью рассчитать каждый свой ход. Интуитивный анализ иг- ровой ситуации вносит в процесс определения очередного хода элемент случайности, что соответствует методу дискретных со- бытий. В такой простой игре, как крестики и нолики, если ис- пользуется поле небольших размеров, число всех возможных позиций невелико и можно рассчитать стратегию беспроигрыш- ной игры. Ходы игрока могут быть полностью детерминирова- ны, если он знает стратегию, в противном же случае его игра носит случайный характер. Следование стратегии во время игры соответствует методу моделирования переходов состоя- ний. Задача о читателях и писателях решается методом модели- рования случайных дискретных событий, если времена появле- ния читателей и писателей непредсказуемы или зависят только от доступности ресурсов. Задача о поставщике и потребителе не имеет неопределенности или случайного элемента и поэтому решается с помощью моделирования переходов состояний. По- ставщик и потребитель работают, пока есть необходимость в продукте и возможность его потребления. Их деятельность пол- ностью определена состоянием области хранения. Классическим примером моделирования дискретных собы- тий является процесс обслуживания клиентов единственным мастером. Течение времени отмечается событиями двух видов: появлением клиента и окончанием обслуживания клиента мас- тером. Если клиент появляется в тот момент, когда мастер не занят, он обслуживается немедленно. В противном случае он встает в очередь на обслуживание. Когда мастер закончил об- служивание клиента и обнаруживает, что его ждут другие кли- енты, он обслуживает клиента, находящегося первым в очере- ди. Иначе он переходит в состояние ожидания клиентов. Этот вариант, соответствующим образом дополненный, может слу- жить моделью процессов операционной системы или управле- ния дорожным перекрестком. Состояния процессоров или оче- редей машин на перекрестке изменяются в дискретные момен- ты времени и остаются постоянными до следующего события. Другой тип проблем моделирования дискретных событий связан с экологическим и социологическим моделированием. Ис- ходной информацией для этих задач служит начальная засе- ленность некоторого жизненного пространства популяциями за- данного вида (например, лис и фазанов, койотов и зайцев при экологическом моделировании) или людей (при социологиче- ском моделировании). Заданы формулы, определяющие увели-
Алгоритмы 177 чение и уменьшение численности каждой популяции. При со- циологическом моделировании законы увеличения и уменьше- ния популяции основаны на возрасте и плотности населения. В экологическом моделировании эти законы определяются до- ступностью пищи. Моделирование проводится с помощью из- менений численности населения на основе текущей численно- сти и его плотности. Вычисление новых значений для видов производится одновременно, с учетом величин всех видов по- пуляции. Если численность зайцев зависит от числа койотов и наоборот, обе популяции изменяются одновременно. Метод моделирования дискретных событий осуществляется в основном с помощью обмена значениями между различными переменными. Новые значения переменных могли бы быть установлены, например, следующим образом: ОДНОВРЕМЕННО (X := Y; Y := X) Этот обмен можно выполнить с помощью временных перемен- ных temp := х; х:= у; у := temp Аналогично рассмотренному процессу выполняется обновле- ние файла на магнитной ленте в системе сопровождения фай- ла. Перед обновлением файл должен быть скопирован. Для всех остальных подсистем операция обновления выполняется так, как будто все записи (в тот момент, когда лента, содер- жащая обновленный главный файл, заменяет старую) перепи- сываются одновременно. Классическим примером моделирования переходов состоя- ний является машина Тьюринга, представляющая собой упро- щенную модель ЭВМ. Машина Тьюринга в качестве входной и выходной информации имеет двоичные данные на ленте. Набор команд состоит из инструкций: ЧИТАТЬ, ПИСАТЬ-0, ПИСАТЬ-1, СДВИНУТЬ-ВПРАВО, СДВИНУТЬ-ВЛЕВО и СТОП. Кроме этого, машина Тьюринга имеет память, в кото- рой может храниться только один символ из ограниченного ал- фавита. В языке программирования для этой машины содер- жатся обычные управляющие структуры. Данная модель пред- ставляет собой теоретическую абстракцию, используемую для изучения основных возможностей вычислительных машин. Не- смотря на ее простоту, возможности машины Тьюринга такие Же, как и у любой ЭВМ, и поэтому она может служить мо- делью работы любых ЭВМ. Поскольку в этом случае в памяти и на ленте могут находиться символы лишь из конечного алфа- вита, здесь используется моделирование переходов состояний. Состояние машины Тьюринга определяется символом, располо- женным под головкой записи-чтения, и. символом, находящимся 12-399.
178 Глава 5 в памяти машины. В этой модели явно выражены принципы моделирования переходов состояний, поскольку ее функциони- рование зависит только от ее состояния. Другим примером моделирования переходов состояний мо- жет служить инвертирование программы печати страниц для генерации отчетов. Обычно программа распределения по стра- ницам функционирует независимо от генератора отчетов и име- ет следующий вид: procedure распределение» по»страницам; (Пример 5.13) repeat печатать—заголовок_страницы; while not конец—.страницы and по1конец_отчета do взять—строку—отчета; печатать.строку—отчета; печатать—конец—страницы until конец—отчета end. Если программа распределения по страницам используется в виде подпрограммы, она при каждом обращении должна быть организована так, чтобы выполнять одну из следующих трех функций: печатать заголовок страницы, печатать строку отчета или печатать окончание страниц. В этой программе должна быть предусмотрена переменная состояния, которая указывает, какую из трех функций необходимо выполнить. Эта переменная состояния инициализируется таким образом, чтобы при пер- вом вызове подпрограмма печатала заголовок страницы. В опи- санном случае используется моделирование переходов состоя- ний, поскольку применяется переменная состояния, управляю- щая работой программы и содержащая лишь конечное число заранее определенных значении. Подпрограмма имеет следую- щий вид: (Пример 5.14) procedure распределение» по-страницам (строка-отчета); initial состояние=начало; if состояние = конец; печатать—конец-страницы; состояние =начало; if состояние:=начало печатать—начало» страницы; состоянием продолжение; if состояние=продолжение печатать-строку- отчета;
Алгоритмы 179 if конец—страницы состоянием конец; if конец—отчета печатать» конец—страницы end. Эта подпрограмма должна вызываться генератором отчетов каждый раз, когда необходимо выдать на печать строку. 5.4. Документация алгоритмов Документация алгоритмов является частью документации программ и модулей. Если используемый алгоритм хорошо из- вестен или его описание можно найти в специальном источнике, документация должна содержать имя алгоритма или ссылку на источник и авторов. Так, например, простые числа могут быть получены с помощью алгоритма «решето Эратосфена», а для решения задачи о коммивояжере можно воспользоваться алго- ритмом Дейкстры. Для нахождения квадратных корней в основ- ном используется метод Ньютона — Рафсона, а для вычисления sin (х) — полиномы Чебышева. Упорядочение списков выполня- ется с помощью сортировки Шелла, а также различных видов пузырьковой сортировки. Если в алгоритме использован специальный математический аппарат (как, например, в случае биномиальных коэффициен- тов), в документацию должно быть включено математическое обоснование метода. Если для упрощения задачи или увеличения скорости счета ее на ЭВМ применяются какие-либо эвристические методы, они должны быть указаны в документации. В ней должно содер- жаться общее описание используемого метода и подхода к при- меняемой модели, если оно поможет лучше понять алгоритм. Так, например, должны быть указаны особенности реализации алгоритма в случае применения рекурсии или параллельной обработки. Хорошо известные алгоритмы не нуждаются в де- тальном описании. Если же используются менее известные ал- горитмы или они были разработаны программистом, описания Должны быть составлены подробно, возможно с применением псевдокодов. 5.5. Упражнения 1. С помощью псевдокода напишите две сопрограммы: одну для чтения с перфокарт чисел, по 5 с каждой карты, и другую для печати этих чисел. Во время каждого обращения к печати должно выдаваться по три числа. Исполь- зовать в каждой программе инструкцию wait until <условие>, переводящую программу в состояние ожидания, когда появляется указанное условие. 12*
280 Глава 5 2. Объедините подпрограммы из предыдущего упражнения в одну двумя способами: а) с помощью инвертирования подпрограммы печати, обеспечив обраще- ние к ней из программы чтения; б) с помощью инвертирования подпрограммы чтения, обеспечив вызов ее из подпрограммы печати. 3. Функция Аккермана определяется следующим образом: А (т, п) = n + 1 ДЛЯ т= 0, А (т — 1, 1) для п= 0, А (т— 1, А (ту п — 1)) для ту 0. С помощью псевдокода напишите рекурсивную процедуру вычисления А(/п, м). Вычислите А (4, 5) вручную. 4. Напишите с помощью псевдокода рекурсивные и итерационные про- цедуры для вычисления: а) квадратного корня с помощью метода, заданного соотношением (5.6); б) функции синуса с помощью метода, заданного соотношением (5.8). 5. Определите сложность каждого из описанных в этой главе методов на- хождения кратчайшего пути через лабиринт. Сложность должна выражать зависимость от п (для лабиринта пХп) или от k (длина пути). 6. Измените процедуру поиска пути в лабиринте из примера 5.11 так, чтобы с ее помощью можно было определить кратчайший путь через лабиринт. 7. Измените процедуру вычисления арифметического выражения из при- мера 5.12 так, чтобы можно было осуществлять проверку на правильность рассматриваемого выражения. 8. Для каждого из типов алгоритмов: аддитивного, последовательного приближения, наискорейшего спуска, обратного прохода и поиска с возвратом приведите пример задачи, которая не рассматривалась в этой главе, но может быть решена с их помощью. Объясните использование при решении этих задач рассматриваемого подхода. 9. Найдите два различных способа вычисления среднего для набора чисел без сортировки. Реализуйте каждый из них с помощью рассмотренных в этой главе методов и определите их сложность. 10. Задача о рюкзаке заключается в определении способа размещения по возможности большего числа объектов в заданном фиксированном объеме. Найдите три различных способа решения, представляющих три разных типа алгоритмов. 11. Последовательность бинарных чисел называется ^-случайной, если в ней не существует повторяющейся дважды последовательности длиной k. По- следовательность с таким свойством, имеющая максимальную длину, называет- ся максимальной Л-последовательностью. Так, например, 10 и 01 представляют собой максимальные 1-случайные последовательности, а 10011—максимальная 2-случайная последовательность. Постройте алгоритм генерации максимальной ^-случайной последовательности. Используйте дерево поиска. 12. Изобразите дерево поиска для задачи о каннибаллах и миссионерах (разд. 5.3.7). 13. Преобразуйте процедуру постраничной печати из примера 5.13, ис- пользующую метод моделирования состояний, так чтобы каждая новая группа записей отчета печаталась с новой страницы. 14. Постройте алгоритм поиска пути для игры в китайские шашки для одного игрока, описанной в упражнениях 4 и 8 гл. 4. 15. Постройте алгоритм поиска для задачи о треугольниках и квадратах, описанной в упражнениях 3 и 9 гл. 4.
Глава 6 ПРОЕКТИРОВАНИЕ МОДУЛЕЙ Программисты, имеющие опыт работы только на одном язы- ке программирования, выполняют свои разработки, пользуясь определениями этого языка. Когда проект программного модуля в общих чертах закончен, определены входные и выходные дан- ные и разработаны структуры данных, в этот момент существует большой соблазн приступить к кодированию модуля. Первый вариант модуля представляет собой часто версию модуля с комментариями или обращениями к процедурам в тех местах, где детали еще не определены. Другими словами, первый вари- ант модуля является скелетной схемой программы, дополненной комментариями. Вместо того чтобы использовать возможности естественного языка, программист при планировании модуля ограничивается формулировками, определяемыми правилами очень простого искусственного языка. Очень часто некоторые второстепенные задачи программирования, предназначенные, например, для того, чтобы удовлетворить требованиям заказчи- ка или чтобы обеспечить отладку, формулируются только после того, как программа уже написана. Это может привести к тому, что программа будет настолько «залатана», что лучшим выхо- дом окажется начать ее разработку заново. Проектирование последовательности передач управления программы и ее кодирование не одно и то же. Программист не должен решать вопросы синтаксиса, пунктуации, а также про- блему хранения данных одновременно с задачей, как заставить ЭВМ делать то, что требуется. Если начинать сразу с кодиро- вания, это впоследствии приведет к тому, что структуры данных и программ сформируются прежде, чем полностью будет опре- делено их использование. Предварительное планирование в ос- новном предназначено для такого способа формирования конст- руктивных деталей программ, чтобы впоследствии можно было избежать больших переделок из-за изменений в структурах. Проектирование модулей включает разработку модульных интерфейсов, внутренних структур данных, структурной схемы передач управления, средств управления в исключительных со- стояниях, а также подробное описание алгоритма. При рассмотрении управления в исключительных состояни- ях обычно ограничиваются обработкой информации о коррект-
182 Глава 6 ности или некорректности функционирования модулей, а также об использовании ошибочных условий при управлении работы циклов. Проблема учета всех условий ненормального функцио- нирования модулей возникает при проектировании схемы пере- дач управления в каждом отдельном модуле. ВСЕ, ЧТО МОЖЕТ ИСПОРТИТЬСЯ, ПОРТИТСЯ Когда возникают исключительные состояния, вызванные не- правильной работой аппаратуры, всего программного обеспече- ния или отдельного модуля, управление может осуществляться в соответствии с одним из следующих вариантов: а) попробовать выполнить операцию еще раз; б) устранить ошибку и продолжать; в) сформировать диагностическое сообщение и выйти из мо- дуля, выдав сигнал об ошибке; г) сбросить ошибку. Когда детали проекта уточнены, каждая из них проверяется на возможность появления исключительных ситуаций, для которых должен быть выбран один из приведенных выше вариантов уп- равления. Известны три основных вида средств проектирования струк- туры управления программой: структурированные алгоритмы, схемы передач управления и управляющие таблицы. Все они предназначены для организации нормального функционирова- ния программы, т. е. с их помощью определяются следующие свойства программы: порядок следования отдельных шагов об- работки, ситуации и типы данных, вызывающие изменения про- цесса обработки, а также повторно используемые функции про- граммы. Средства проектирования представляют собой стан- дартные способы выявления всех перечисленных свойств. Эти способы относительно независимы от специфики языков про- граммирования и предназначены для организации процесса все- стороннего планирования, выполняемого на модульном уровне и аналогичного процессу разработки систем и программ с по- мощью схем HIPO и структурных схем. Названные выше сред- ства применяются не только для разработки схемы управления модуля, но также используются для создания основной части программной документации, особенно если позволяют отразить все модификации программ. Кроме того, они обеспечивают про- ектирование средств отладки и тестирования программ еще на стадии планирования. Средства программного проектирования просты в практическом применении. С их помощью можно со- здавать стандартные структуры, но в более свободной форме, чем это позволяют языки программирования, что обеспечивает как простоту проектирования, так и простоту понимания разра- батываемых структур.
Проектирование модулей 183 6.1. Структурированные алгоритмы Псевдокод представляет собой метод применения стилизо- ванного естественного языка для описания структуры управле- ния программы, конструкции которого близки к конструкциям блочных структурных языков программирования. Это позволяет создавать машинно-независимое описание процесса решения за- дачи, допускающее использование языков программирования. Псевдокод состоит из таких предложений, которые могла бы вы- полнить ЭВМ, но которые содержат вместо идентификаторов, зависящих от используемого языка программирования, фразы в свободной форме. Большая свобода при описании процеду- ры— основное достоинство псевдокода — является и его недо- статком. В противоположность языкам программирования при использовании естественного языка трудно придерживаться нуж- ных ограничений и точных определений. Поэтому, применяя псевдокод, следует придерживаться определенных условий. Основные команды псевдокода аналогичны командам для ЭВМ, т. е. командам машинного языка. Они комбинируются стандартным способом, в результате чего образуются более сложные команды. Основными типами сложных команд (кро- ме последовательностей инструкций) являются команды выбо- ра, повторений и обработки исключительных состояний. Эти команды псевдокода являются обобщениями инструкций типа if, а также команд организации циклов и обработки прерыва- ний. Выполнение данных команд заключается в реализации простых команд в зависимости от условий среды. Под средой здесь понимается состояние ЭВМ, данных и переменных про- граммы. Псевдокод отличается от обычных детализированных устных алгоритмов стандартизацией конструкций, форматированием описания, использованием ключевых слов и удобным для пони- мания, строгим оформлением. Ключевые слова выбираются так, чтобы сделать алгоритм ясным, строгим и однозначным. 6.1.1. Основные управляющие конструкции Обычная конструкция следования имеет следующий вид: Р; Q Здесь Р и Q — простые предложения, обозначающие операции преобразования данных или информационного обмена, напри- мер такие, как ЧИТАТЬ, ПИСАТЬ или ВЫЧИСЛИТЬ; а так- же определяющие обращения к другим процедурам. Повторения
184 Глава 6 имеют несколько стандартных форм, среди которых наиболее распространены while С do Р и repeat Р until С Здесь С — проверяемое условие, а Р — простое предложение. Не обязательно использовать обе эти формы, так как с по- мощью дополнительных управляющих конструкций одна из них может быть преобразована в другую. Так, конструкция while do эквивалентна следующей: if С then repeat Р until not С тогда как конструкция repeat until эквивалентна конструкции Р while not С do Р Различие между конструкциями while do и repeat until состоит в том, что если в первой из них проверка условия производится в начале (таким образом, Р может вообще не выполняться), то во второй — в конце, после выполнения Р. Обе~ конструкции типа повторений считаются базовыми, и поэтому каждая из них обеспечивается многими языками программирования. Конструкция выбора имеет следующий вид: if С then Р else Q Можно показать, что сочетания этих четырех базовых конструк- ций путем их гнездования и соединения позволяют реализовать все структуры программ. Кроме того, программы, использую- щие только эти конструкции, могут быть формально проверены на корректность. Для правильно совмещенных конструкций может быть ис- пользован символ группировки begin... end. Кроме того, иногда используются конечные ограничители операторов endif и endwhile. Оформление программы, при котором предложения вложенных блоков начинаются правее предложений внешних по отношению к ним блоков, а предложения одного уровня вложен- ности располагаются с одинаковых позиций, не является су- щественным для ЭВМ. Однако для программиста оформление с использованием выравнивания по уровням вложенности может обозначать группирование отдельных конструкций. Широко применяется еще один дополнительный тип операто- ра, который может быть добавлен к группе базовых управляю- щих конструкций. Это оператор case, обозначающий множест-
5 Проектирование модулей 185 венное ветвление (т. е. выбор одной ветви из многих). Он име- ет следующую форму: case Сх: Рх; С2 fP2;...otherwise Р„ Предложение case представляет собой расширение предложе- ния if, предназначенное для передачи управления одной из не- скольких ветвей данного оператора. Для ограничения этого опе- ратора применяется слово endcase. ИСПОЛЬЗУЙТЕ СТАНДАРТНЫЕ КОНСТРУКЦИИ 6.1.2. Конструкции следования Конструкции следования представляют собой наборы опера- торов, выполняемых в порядке их записи. Если запись ведется на псевдокоде, операторы должны быть расположены последо- вательно на отдельных строках и выровнены по левому краю. При необходимости оператор можно продолжить на второй строке, причем продолжение должно начинаться правее пози- ции, по которой осуществляется выравнивание: Читать карту Печатать данные с нее, как заголовки столбцов, располагая их в верхней части страницы Читать другую карту Печатать данные с нее, располагая их под заголовками соответствующих столбцов Прописные и строчные буквы, а также знаки препинания долж- ны использоваться таким образом, чтобы их применение под- черкивало структуру программы и повышало ее читабельность. Иногда требуются дополнительные комментарии. Так же как это принято в языке Паскаль, комментарии, поясняющие струк- туру программ, приводимых в примерах, заключаются в фи- гурные скобки. 6.1.3. Конструкции выбора Конструкции выбора представляют собой операторы, выпол- няемые только один раз и при определенных условиях. Сущест- вуют различные способы реализации этих конструкций в разных языках программирования. Обычно синтаксически параллель- ные части в данных конструкциях принято выравнивать по ле- вому краю. Это означает выравнивание всех слов then и else,
186 Глава 6 относящихся к одному уровню, и смещение вправо всех вложен- ных конструкций. Если требуется более подробное объяснение использования различных вариантов выбора, следует добавлять комментарии: if найдена—карта—конца-файла then {данных больше нет} печатать—окончание if конец—данных {обнаружен} then печатать-окончание (Пример 6.1) (Пример 6.2) (Пример 6.3) if b2 - 4ас <0 { отрицательный дискриминант} then {уравнение не имеет действительных корней} печатать-сообщение—об—ошибке else {существуют два действительных корня} вычислить (-Ь ± V(b2 - 4ас))/(2а) > печатать—корни В операторе case параллельные части не имеют ключевых слов и отмечаются парами типа условие — действие. Они могут быть выровнены, как показано ниже: case of тип—сообщения (Пример 6.4) проверить или отложить: найти—запись—главного—файла (проверка) обработать—сообщение взнос: найти-запись_главного—файла (ссуда) обработать-ссуду добавление или изъятие: найти-запись— главного—файла(сохранение) обработать—сообщение otherwise {тип сообщения не опознан}: печатать—сообщение—об-ошибке endcase Конструкция if хорошо известна по многим языкам програм- мирования. Конструкция case, имеющаяся в одних языках, в других может быть реализована в упрощенной форме как вычисляемый оператор goto (например, на языке Фортран): GO ТО (10,20, 36, 45), К
Проектирование модулей 187 или в форме каскада операторов if, как, например, на языке Кобол: IF...ELSE IF...ELSE IF...ELSE... Если структура case построена в виде последовательности операторов if, условия должны быть взаимоисключающими. При такой реализации оператора case выполняется та его ветвь, ко- торая соответствует первому истинному условию. Если мно- жество заданных условий не полно, должен использоваться вы- ход по несоблюдению условий, задаваемый ключевым словом otherwise. Соответствующая этому выходу ветвь выполняется всегда, когда ни одно из условий не соблюдено. Если выход по несоблюдению условий в структуре case или выход else струк- туры if не используется, эта ветвь может быть пропущена или ей должен соответствовать пустой оператор. В языке Паскаль имеется конструкция case, но она ограничена использованием значений единственной переменной. Для псевдокода не требует- ся такое ограничение, но если условия, подлежащие проверке, независимы, следует применять структуру if, а не case, посколь- ку при этом повышается читабельность программы. УЧИТЫВАЙТЕ ВСЕ ВОЗМОЖНЫЕ СЛУЧАИ 6.1.4. Конструкции повторений Конструкции повторений представляют собой последователь- ность операторов, выполняемых несколько раз. К их числу от- носятся циклические структуры различных видов, в том числе и циклы, использующие счетчики и индексные переменные. Уп- равление функционированием большей части циклических структур осуществляется с помощью проверки условия оконча- ния или условия продолжения выполнения цикла. В псевдокоде, описывающем цикл, следует указывать, что тело цикла выпол- няется по крайней мере один раз, если проверка производится в конце-цикла, или что возможен случай, когда тело цикла ни ра- зу не выполняется, если проверка осуществляется в его начале. Способы реализации циклов в языках программирования су- щественно отличаются друг от друга. При разработке про- граммы на псевдокоде и при использовании какой-либо разно- видности оператора управления циклами следует соблюдать осторожность при переходе к кодированию программы. Так, на- пример, программисту, использующему для кодирования язык Кобол, следует заметить, что конструкция repeat until не экви- валентна оператору Кобола PERFORM... UNTIL. Этот опера- тор Кобола реализует конструкцию while do.
188 Глава 6 При описании циклов следует использовать выравнивание строк и применять комментарии: (Пример 6.5) while not конец—файла db {обработка данных} читать данные сообщений найти соответствующую запись главного файла обновить запись главного файла (Пример 6.6) > while счетчик изменяется до 50do {печатать данные} читать карту печатать карту {эхо-печать} repeat {печатать карты с помощью:} (Пример 6.7) читать карту печатать ее until конец—файла {найден} Включение в псевдокод комментариев, отмечающих цель цикла, повышает читабельность программы. Программа в окончатель- ном виде должна быть оформлена таким же образом. В конст- рукциях повторений тело каждого цикла состоит из независи- мых операторов. Обычно эти операторы повторяются или за- данное число раз, или пока выполняется условие цикла, или пока не произойдет некоторое заранее предусмотренное собы- тие. Главным фактором, влияющим на выбор нужной конструк- ции повторения, является метод проверки условий окончания циклов. Проверка может быть проведена сразу, и, если соответ- ствующие условия выполняются еще до входа в цикл, тело цик- ла не будет выполняться. Если проверка осуществляется в кон- це, операторы тела цикла будут выполнены по крайней мере один раз. В некоторых случаях для окончания цикла могут проверяться условия, сигнализирующие о том, что произошли определенные события. Например, цикл, управляемый считы- ваемыми картами, заканчивается, когда обработана последняя карта данных, несмотря на то что была считана следующая карта данных. проверяйте корректность окончания циклов
Проектирование модулей 189 Особую проблему представляют проверки концов файлов. Выбранный язык программирования может обеспечивать изме- нение значения внутреннего флага при чтении последней кар- ты с данными (Паскаль) или при попытке прочитать файл (Ко- бол и Фортран). Проверка флага может быть включена в опе- раторы чтения (Кобол и некоторые версии Фортрана) или неяв- но использоваться в этих операторах, как, например, в случае использования одного из предложений управления в исключи- тельных состояниях языка ПЛ/1 ON ENDFILE. В других же случаях проверка флага может производиться программистом (Паскаль, Бейсик). Эти варианты влияют на окончательную реализацию модулей, но не должны отражаться на процессе проектирования общей схемы управления, поскольку разработ- ка должна быть подробной как для этапов, учитывающих ис- пользование конкретных данных, так и для этапов, не завися- щих от наличия данных. Другая проблема возникает, когда имеется несколько усло- вий окончания. Если при чтении файла на перфокартах про- верка выполняется и для условия окончания файла, и для ус- ловия обнаружения карты конца файла, сначала проверяется конец файла, и, если он обнаружен, на этом проверка заканчи- вается, поскольку не осталось больше карт. Порядок проверки составных условий зависит от языка реализации. Если запись, составленная на псевдокоде, понятна, трудностей такого рода на этапе программирования не возникает. Беспорядочное использование в программе операторов goto свидетельствует о плохом стиле программирования, поскольку оно приводит к тому, что программы, написанные таким спосо- бом, трудны для понимания и сопровождения. Многие автори- тетные программисты считают, что при проектировании циклов, которые должны завершаться сразу после возникновения усло- вия окончания цикла, удобно пользоваться командой выхода, но применение для этой цели команды goto нежелательно, так как она имеет слишком общий смысл. Для обеспечения такого завершения цикла нужна команда, передающая управление первому оператору вне цикла. С помощью флага, указывающе- го, что условие окончания выполнилось, можно проводить про- верку как внутри цикла, так и в конце его. В псевдокоде для отметки точки выхода из цикла можно пользоваться предложе- нием exit: (Пример 6.8) while данные_не_кончились do {чтение и печать карт} читать_ карту case of состояние-.чтения_карг карт—нет: -
190 Глава 6 печатать «пропущена карта конца данных» exit считана.карта^конца: пропустить строку exit otherwise {прочитана карта с данными}: печатать данные с карты endcase endwhile Иногда требуется такой же оператор выхода из подпрограм- мы, как и оператор exit в цикле. Часто необходимо различать нормальный выход от выхода в случае ошибки. Иногда необхо- димо производить установку возвращаемых значений при вы- ходе из цикла или подпрограммы. Так, например, во время вы- хода можно устанавливать нужные значения индикаторов собы- тий. В псевдокоде предложение вида exit (...) можно использо- вать для перехода к строке псевдокода, следующей непосредст- венно за циклом в момент выполнения условий выхода. Это по- зволяет использовать структуру повторений более общего вида, т. е. цикл без условия окончания. Такой вид управления рабо- той цикла необходим в операционных системах и программах, работающих в режиме on-line, в которых нет условий, ограничи- вающих число их циклов. Применение предложений exit позво- ляет также программисту управлять последовательностью про- верки любых ограничивающих работу цикла условий: N=:0 (Пример 6.9) взять число while {запоминание списка чисел } do case of данные—найдены конец_файла: exit(N) карта—конца—файла: exit(N) список—заполнен: exit (длина—списка) не-цифра: запросить—исправления г if нет—исправлений return (неправи л ьные__данные)
Проектирование модулей 191 else N := N + 1 список(1М) 1-ЧИСЛО ВЗЯТЬ—число endcase endwhile Для возврата значения вызывающей подпрограмме можно использовать предложение return (...). При выходе из подпро- граммы можно возвращать флаги, сигнализирующие нормаль- ное завершение или наличие ошибок. Если на псевдокоде явно указываются возвращаемые переменные, при этом не уточняет- ся, передаются ли с помощью соответствующих аргументов зна- чения данных или ссылки на них (например, адреса). Посколь- ку способы передачи аргументов в различных языках програм- мирования отличаются, программист при разработке модуля на уровне псевдокода должен избегать определений в терминах конкретного языка программирования. В примере 6.9 ситуации появления неправильных данных, конца файла или карты конца файла рассматриваются как нор- мальные альтернативные варианты для обработки правильных данных. На самом деле, если предполагается, что после пра- вильных данных последует карта конца файла, ситуации обра- ботки неправильных данных и конца файла уже не являются нормальными. Программы создаются для обработки правиль- ных данных. Проверка же данных на корректность включается потому, что при ручном кодировании информации могут быть допущены такие ошибки, которые ЭВМ не может обнаружить автоматически без специальной обработки, и это заставляет еще на стадии планирования учитывать возможность их появления. Можно считать, что в рассмотренном выше примере считывание неправильных данных и обнаружение конца файла являются ненормальными условиями. Считывание и определение карты конца файла является «почти нормальным» условием, потому что, хотя такая ситуация и учитывается, она не является частью нормальной обработки. Вместо того чтобы рассматри- вать, ненормальные и почти нормальные условия как обычные альтернативы, их можно гораздо эффективнее обрабатывать как исключительные состояния. 6.1.5. Исключительные состояния Исключительные состояния представляют собой особые ус- ловия, которые во время нормальной обработки не возникают часто, но с их появлением требуется применять специальное управление. Эти условия могут быть машинно-зависимыми, и
192 Глава 6 управление при их возникновении производится на уровне язы- ка ассемблера с помощью обработки прерываний. Они могут также создаваться программистом для выполнения операций восстановления или переходных процессов, возникающих во время непрерывной нормальной обработки. Эти ситуации созда- ются, например, при обнаружении неправильных данных, отсут- ствия данных и при переходе к обработке другой группы. В языках Ада и ПЛ/1 существуют средства, позволяющие про- граммисту вызывать прерывания и исключительные состояния для таких ситуаций. В ПЛ/1 оператор ON (оператор задания обработки прерываний) позволяет программисту определять действия, которые необходимо выполнить при возникновении некоторых ненормальных или «почти нормальных» ситуаций. Обработка исключительных состояний осуществляется с по- мощью небольшой подпрограммы. При возникновении прерыва- ния нормальная обработка приостанавливается, управление пе- редается обработчику прерываний, после чего восстанавливает- ся нормальная обработка. Каждый оп-оператор в псевдокоде должен размещаться так, чтобы при чтении программы было ясно, относится ли этот опе- ратор ко всем случаям возникновения данного исключительного состояния или только к некоторым из них. Таким образом, об- ласть действия оп-оператора должна легко определяться. Выде- ление оп-оператора в отдельную структуру позволяет програм- мисту перемещать его в то место программы, которое наилуч- шим образом соответствует языку реализации: (Пример 6.10}' procedure обновление {последовательного главного файла} on конец—главного—файла печатать «файл пропущен» return on конец—файла—сообщений копировать остаток главного файла return читать главный файл while not конец-файла-сообщений do repeat читать сообщение проверить сообщение на правильность until найдено—правильное—сообщение while главный—ключ < ключ.сообщения do on конец.главного_файла * , - установить максимальное значение главного ключа 1
Проектирование модулей 193 exit читать главный файл endwhile if найдена—соответствующая-запись обновить запись главного файла else печатать «запись пропущена» endwhile end Пример 6.10 иллюстрирует гнездование конструкции on. Пер- вые два оп-оператора относятся ко всей процедуре, но первый из них не работает внутри вложенного цикла while, в котором определен оп-оператор для того же условия. С помощью вырав- нивания выделены тела циклов и оп-операторов. Для улучше- ния читабельности программы использовано ограничительное предложение endwhile. Каждый простой оператор начинается с глагола, что под- черкивает функциональную сущность структур программы даже на уровне операторов. Управляющие конструкции начинаются ключевыми словами, и подструктуры каждой из них либо выров- нены по левому краю, либо начинаются правее. Нумерация от- дельных предложений и использование меток не обязательны, так как все переходы заданы неявным образом структурами про- граммы, а также предложениями exit и return. При реализации программы псевдокод должен быть модифи- цирован в соответствии с использованием языка программиро- вания. Псевдокод не следует понимать как один из языков про- граммирования. При этом использование стандартных конст- рукций , sequence if...then...else... case....otherwise... while...do... repeat...until... exit(...) return(...) on... а также метода выделения структур с помощью выравнивания в сочетании с описанием операторов на естественном языке обеспечивает большую свободу при обдумывании разрабаты- ваемой программы и более надежную проверку ее правильно- сти, чем это позволяют языки программирования. 6.2. Схемы передач управления Для изображения передач управления в программном моду- ле обычно используются структурные схемы программ. Струк- турное программирование и его влияние на применение основ- 13—399
Ограничение/прерывание Решение Подготовка Обработка Вызов модуля Линия передачи управления Соединитель Г _____J I Пояснение а Цикл со счетчиком индекса Рис. 6.1. Символы структурных схем. а — стандартные символы; б — нестандартные символы. Рис. 6.2. Базовые управляющие конструкции. а — следование; б — выбор; в — повторение.
Проектирование модулей 195 ных управляющих конструкций способствовали тому, что стан- дартные символы схем были дополнены новыми символами и были разработаны новые типы схем. В частности, схемы Нас- ей— Шнейдермана представляют для программиста средство описания вложенных управляющих структур. 6.2.1. Структурные схемы программ На рис. 6.1 показаны стандартные и более новые нестандарт- ные символы для изображения структурных схем. Их можно ис- пользовать для представления организации программы так же, как и для передач управления. Как и псевдокод, структурные схемы можно применять на любом уровне абстракции. Посколь- ку они воспринимаются в первую очередь визуально, их следует изображать таким образом, чтобы структура программы стано- вилась сразу очевидной. Краткость, выразительность и плано- мерность при проектировании позволяют создавать структурные схемы высокого качества. Основная тенденция в использовании Рис. 6.3. Вложенные конструкции структурных схем. структурных схем в настоящее время — не указание последова- тельности операций, а группирование символов, выражающих базовые конструкции: следование, выбор и повторение. На рис. 6.2 показаны схемы этих управляющих конструкций. Каж- дая конструкция имеет единственный вход и единственный вы- ход. Структурные схемы изображаются по вертикали. Если пер- вый прямоугольник обработки конструкции повторения опущен, эта конструкция имеет вид while do. Если же отсутствует второй прямоугольник, конструкция повторения имеет вид repeat until. Такую конструкцию называют «циклом в п с половиной повто- 13*
196 Глава 6 рений». Управляющие конструкции могут быть представлены в виде гнезд. На рис. 6.3 показана конструкция выбора, где альтернатива- ми являются следование и другая конструкция выбора. Альтер- нативные ветви внизу объединяются, формируя законченный вид конструкции выбора. Конструкции могут быть заключены в штриховые прямоугольники. Это упрощает понимание их функ- ционального назначения. case ,.. when .., when ... when ... of erwise ,,„ Рис. 6.4. Варианты конструкций выбора. Конструкции выбора с двумя ветвями (рис. 6.2 и 6.3) соот- ветствуют конструкции if с двумя вариантами выбора, общепри- нятыми в большинстве языков программирования. Для изобра- жения структуры выбора из множества вариантов, соответствую- щей конструкции case, схемы дополняются, как показано на рис. 6.4. Это обеспечивает компактный способ изображения структуры, существующей в различных видах в большинстве языков программирования, но в состав стандартных элементов схем не входит. В структуре на рис. 6.4 явно выделен парал- лельный характер множества альтернатив, который при исполь- зовании гнездованных конструкций выбора из двух ветвей не был бы очевидным. Применение стандартных символов структурных схем (рис. 6.1, а) при преобразовании схем в программы не обеспечи- вает структурности программ и не является адекватным исполь- зованию блочно-структурных языков или даже также конструк-
Блок входа/выхода. Этот символ используется для обозначения операций ввода и вывода информации. Отдельным логическим устройствам или отдельным функциям обмена должны соответствовать отдельные блоки. В каждом блоке указывается тип устройства или файла, тип информации, участвующей в обмене, а также вид операции обмена. Блок обработки. Этот символ применяется для обозначения одного или нескольких операторов, из- меняющих значение, форму представления или раз- мещения информации. Для улучшения наглядности схемы несколько отдельных блоков обработки могут быть объединены в один блок. Представление отдель- ных операций достаточно свободно; например, для обозначения вычислений можно использовать матема- тические формулы, для пересылок данных — стрелки, а для остальных — пояснения на естественном языке. Содержимое блоков структурных схем никогда не имеет вид машинных кодов. В зависимости от уров- ня детализации схемы пояснения на естественном языке могут быть более или менее подробными. Структурные схемы, так же как и псевдокод, неза- висимы от специфики языков программирования. По- этому в описаниях операторов не следует использо- вать резервированные слова и символы языков про- граммирования, а также применять имена данных, составленные в соответствии с синтаксическими тре- бованиями этих языков. Блок решения. Этот символ используется для обозначения переходов управления по условию. Для каждого блока решения должны быть указаны: во- прос, решение, условие или сравнение, которые он определяет. Стрелки, выходящие из этого блока, должны быть помечены соответствующими ответами так, чтобы были учтены все возможные ответы. Сле- дует отметить, что ответы, которые не могут появить- ся при анализе правильной информации, иногда по- являются при рассмотрении некорректных данных. Следует всегда учитывать такие ситуации. Символ ограничения/прерывания. Этот символ предназначен для обозначения входов в структурную схему, а также для указания всех выходов из нее. Каждая структурная схема должна начинаться или заканчиваться символом ограничения. В этих симво- лах следует размещать пояснения к их использова- нию. Если символ указывает на прерывание, он дол- жен идентифицировать соответствующую исключи- тельную ситуацию и блок структурной схемы, осу- ществляющий управление в данной ситуации. Между символами ограничений могут находиться цепочки та- ких символов структурных схем, как последователь- ности блоков, обработки, вызовов модулей, ввода/вы- вода, решений и т. д. /Вернуть запись на, fмагнитную ленту / с главным фай- / лом анкетных / данных / Читать карту с анкетными данными служащего / А г2 Учесть сумму в стро- ке баланса и в итого- вой строке Перезаписать дубль Рис. 6.5. Примеры использования символов структурных схем.
198 Глава 6 Начало ЧИТАТЬ И ПЕЧАТАТЬ карты, пока не встретится карта конца файла Сорти- ровать файл Конец Блок подготовки. Этот символ используется для обозначения процессов предварительной обработки. Для многих программ должно быть определено вы* полнение вспомогательных функций, таких, как опе- рации открытия и закрытия файлов, установка на- чальных значений флагов, а также инициализация переменных. Эти функции не реализуют какую-либо часть основного алгоритма. Для обозначения вспо- могательных функций часто используются блоки об- работки, но более правильно размещать их внутри блока подготовки. Блок вызова модуля. Этот символ используется для представлений обращений к модулям или под- программам. Вертикальные линии обозначают обра- щение к внешним модулям обработки, горизонталь- ные— данный блок представлен в документации от- дельной структурной схемой. Блоки вызова модулей используются для обращений к библиотечным под- программам, а также для обозначения части програм- мы, не зависящей от основной схемы управления. В таких языках как Алгол или Паскаль, эти блоки обозначают вызовы процедур, а для языка Кобол со- ответствуют использованию параграфов. Блоки вы- зова модулей применяются также для обозначения некоторой части программы, которая будет кодиро- ваться вместе со всей программой, но в документа- ции представлена отдельной структурной схемой. Ес- ли такая часть программы представляет итерацион- ный процесс, в соответствующий ей блок вызова дол- жны быть включены описания условий окончания цикла. Если блок вызова представляет рекурсивное обращение, его выход должен быть соединен со вхо- дом стрелкой. По мнению некоторых авторов книг по программированию, использование более одной струк- турной схемы для одной программы затрудняет ее понимание. Однако практика показывает, что удоб- нее всего применять структурные схемы, разбитые в соответствии с уровнями абстракции. Таким образом, блоки вызова модулей могут быть использованы для определения отдельных частей программ, которые удобнее представлять отдельными структурными схе- мами. Обозначение циклов в виде указанных симво- лов позволяет производить детализацию этих частей программы на более поздних этапах проектирования. ций, как предложения языка Кобол GOTO...DEPENDING ON и READ... AT END. С помощью этих символов особенно трудно изобразить управление в исключительных состояниях. Поэтому были предложены дополнительные символы; некоторые из них показаны на рис. 6.1,6. Примеры одновременного использова-
Проектирование модулей 199 Линии переходов. Эти символы используются для обозначения порядка выполнения действий. Для по- вышения читабельности программы следует придер- живаться стандартных правил изображения передач управления: сверху вниз и слева направо. Если не- обходимо показать передачу управления снизу вверх или справа налево, в этом случае следует отметить направление стрелкой. За исключением блоков пре- рываний, все подструктуры, такие, как последователь- ности, разветвления и повторения, должны иметь по одной входной и одной выходной линии переходов. Соединители. Эти символы используются в том случае, если структурная схема долж- на быть разделена на части или не уме- щается на одном листе, или для того, что- бы избежать излишних пересечений линий переходов. Применение соединителей не должно нарушать структурности при изо- бражении схем. Блок пояснений. Этот символ позволяет вклю- чать в структурные схемы пояснения к функцио- нальным блокам. Частое использование коммен- тариев нежелательно: оно усложняет структур- ную схему. Но иногда необходимо пояснить при- менение атрибутов некоторых переменных, при- нятые допущения или назначение программных сегментов. /Читать / карту ___________ _ । Первая карта должна содержать I описание колоды Блок множественного выхода. Этот символ представляет собой комбинацию какого-либо функционального блока с блоком решений. Он используется в тех случаях, когда, например, исключительные состояния, возникающие при открытии файла, вызывают передачу управле- ния из процедуры открытия в точку головной программы, отличающуюся от точки обычного возврата. Для соблюдения принципов струк- турности для всей программы последняя долж- на иметь вид конструкции выбора, а решения следует определять с помощью блока множест- венного входа. Каждая ветвь этого блока Должна быть помечена. 'сообщение; Конец файла Данные Ошибка пропущены Неправильные данные
200 Глава 6 Цикл со счетчиком индекса. Этот символ исполь- зуется для организации циклических конструкций» таких, например, как DO —цикл в языках Фортран и ПЛ/1, PERFORM...VARYING — в языке Кобол или FOR... — в языке Паскаль. Верхняя левая часть бло- ка предназначена для определения начального значе- ния индексной переменной, нижняя левая часть по- казывает правило изменения этой переменной, а пра- вая задает граничные условия указанного правила изменения. Блок размещается вверху циклической конструкции, для управления которой он предназна- чен, даже в том случае, если изменение индекса и проверка условий окончания цикла при реализации модуля осуществляется не в начале, а в конце цикла. Так, например, цикл может быть осуществлен в виде конструкций while do или repeat until. ния стандартных и нестандартных символов приведены на рис. 6.5. Один тип ситуации, встречающейся при программировании и с трудом поддающейся изображению с помощью стандартных символов структурных схем, представляет собой исключитель- ные состояния, которые нельзя обработать с помощью замкну- той подпрограммы управления прерываниями. Например, усло- вие возникновения состояния конца файла для последователь- ного файла может быть проверено с помощью явного тестиро- вания соответствующего флага, выполняемого отдельным пред- ложением программы. Это предложение изображается в виде блока решения. Если проверка флага встроена в предложение чтения, такой блок не нужен, т. е. подразумевается, что реше- ние не может быть отделено от операций чтения. Необходимость использования блока с несколькими выходами очевидна, а это нестандартный символ структурных схем. В более общем виде та же проблема возникает при переходах обработки от одной группы данных к другой. Тогда используются условия, связан- ные с операциями обмена, ошибками в размерах и типах дан- ных, а также с обработкой ошибочных или необычных выходов из вызываемых процедур. Если изображенный с помощью структурной схемы модуль возвращает вызывающему его модулю флаги, сигнализирующие о нормальном или ошибочном его завершении, применение яв- ной установки флагов и использование единственного блока вы- хода приведут к усложнению структурной схемы. Хотя обычно каждая структура управления и структурная схема модуля должны иметь только один входной и один выходной блок, тем не менее, если возможен раздельный выход по нормальному за- вершению и по ошибке, лучше использовать более одного поме- ченного блока выхода. Следует добавить, что для выхода по
Рис. 6.6. Программа сопровождения файла.
202 Глава 6 нормальному завершению должен использоваться по крайней мере один отдельный блок. Выход по ошибке, когда главный файл не обнаружен, и выход по необычному завершению опера- ций обмена из блоков ввода/вывода показаны на рис. 6.6, где изображена структурная схема программы, записанной на псев- докоде в примере 6.10. И нормальный, и ненормальный выходы обеспечивают передачу управления в вызывающую программу. Таким. образом, в определенном смысле можно считать, что из вызываемого модуля есть только один выход. 6.2.2. Схемы Насси — Шнейдермана Способ изображения модуля с помощью схем Насси — Шней- дермана представляет собой попытку использования требова- ний структурного программирования в структурных схемах мо- Обработка Решение Рис. 6.7. Символы схем < Насси — Шнейдермана. дулей. Он позволяет изобра- жать схему передач управле- ния не с помощью явного ука- зания линий переходов по уп- равлению, а с помощью пред- ставления вложенности струк- тур. Некоторые из используе- мых в этом способе символов соответствуют символам структурных схем; другие сим- волы аналогичны структурам псевдокода. Эти символы по- казаны на рис. 6.7. Каждый блок имеет форму прямо- угольника и может быть впи- сан в любой внутренний пря- моугольник любого другого блока. Блоки помечаются тем же способом, что и блоки структурных схем, т. е. с по мощью предложений на есте- ственном языке или с исполь зованием математической нотации. Этот метод проектирования не зависит от языка программирования, используемого для реализации модуля. Примеры применения описанных блоков приведены на рис. 6.8. Модуль сопровождения файла, структурная схема которого показана на рис. 6.6, изображен на рис. 6.9, а в виде схемы Насси — Шнейдермана. Из рисунка видно, что включение в схе- му обработки исключительных состояний, например проверки конца главного файла, повлечет за собой усложнение схемы. Если использовать символы схем Насси — Шнейдермана одно- временно с дополнительным символом структурных схем для
Проектирование модулей 203 Блок обработки. Каждый символ схем Насси — Шнейдермана является блоком обработки. Каждый прямоугольник внутри любого символа представляет собой также блок обработки. Блок следования. Этот символ объединяет ряд следующих друг за другом процессов обработки. Блок решения. Этот символ применяется для обо- значения конструкций типа if...then...else.... Вопрос располагается в верхнем треугольнике, варианты от- ветов на' этот вопрос — по сторонам треугольника, а процессы обработки обозначаются прямоугольни- ками. Блок case. Этот символ представляет расширение блока решения. Те варианты выхода из этого блока, которые можно точно сформулировать, размещаются слева. Остальные выходы объединяются в один, на- зываемый выходом по несоблюдению условий и рас- положенный справа. Если можно перечислить все воз- можные случаи, правую часть можно оставить неза- полненной или совсем опустить, а выходы разместить по обе стороны блока. Блок dowhile. Этот символ обозначает цикличе- сую конструкцию while do с проверкой условия в на- чале цикла. Условия окончания цикла размещаются в верхней полосе, сливающейся с левой полосой, ука- зывающей границу цикла. Блок repeatuntil. Этот символ аналогичен блоку dowhile, но условия располагаются в конце. Читать данные Печатать данные к <10 Да Нет Печатать А(к) Печатать «на- рушение гра- ницы массива» Печа- тать М «равны» Пока не конец файла Читать карту Печатать каогу Читать карту Печатать карту Пока не конец файла Рис. 6.8. Примеры использования символов Насси — Шнейдермана. изображения множественных выходов и обработки прерываний, то представление рассматриваемого модуля может быть упро- щено, как показано на рис. 6.9, б, где основная схема отражает только режим нормальной обработки. 6.3. Управляющие таблицы Если логика управления программы состоит преимуществен- но из решений или идентификации и обработки вариантов, не зависящих друг от друга и не связанных с каким-либо общим процессом вычислений, для представления такой программы проще всего использовать табличные способы описания. Работа анализатора входных символов в компиляторе состоит в иден- тификации и обработке множества различных типов символов. Поэтому при проектировании анализаторов часто используются управляющие таблицы. Вид обработки зависит от типов сим-
Сортировать файл сообщений Читать главный файл Пока не конец файла (файл сообщений) Читать запись сообщения Печатать «файл пропущен» Проверить сообщение Пока не найдено правильное сообщение Пока ключ_записи_ главного—файла < ключ „сообщения Читать главный файл Печатать «запись пропущена» Обновить запись Обработать остаток главного файла Сортировать файл сообщений Читать главный _____файл_______ Конец файла Пока не конец файла (файл сообщений) Читать запись сообщения Проверить сообщение Пока не найдено правильное сообщение Пока ключ_записи— главного—файла< ключ—сообщения Читать главный файл Конец файла < Установить максималь- ное значе- ние ключа главного Файла Найдена соответствующая запись? Нет Печатать «запись пропущена» Обновить запись Обработать остаток главного файла ^ Возврат б 4 Рис. 6.9. Программа со- провождения файла, представленная с помо- щью схемы Насси — Шнейдермана. а — схема Насси — Шнейдер- мана; б — модифицированная схема Насси — Шнейдермана. а
Проектирование модулей 20 волов. Соответствующая таблица содержит запоминаемые в программе значения, зависящие от символьных данных. Эти значения служат для организации переходов управления между частями программы. К этой же категории программ относится интерпретатор, который анализирует символы, вводимые во вре- мя его работы. В гл. 5 рассмотрены примеры моделирования переходов состояний. 6.3.1. Таблицы данных Данные часто состоят из символов, которые могут быть за- ранее определенным способом классифицированы на основании ряда проверок. Такие случаи встречаются в разнообразных приложениях: при проверке данных на правильность, при рас- чете размеров вычетов из заработной платы, для управления светофором в соответствии с ситуацией на перекрестке, а также при анализе математических выражений. На псевдокоде такие случаи представляются с помощью предложений if или case. В структурных схемах для той же цели применяются блоки ре- шения. Наиболее естественным решением в подобных случаях было бы составить таблицу, перечисляющую ситуации и соответ- ствующие типы данных. Такие таблицы часто используются только для запоминания данных, но их также можно применять и для организации управления программой. Примерами таких таблиц служат таблицы перехода управления в языке ассемб- лера или списки переключателей в языке Алгол. Таблица 6.1. Значения римских цифр Цифра справа от текущей М D с L X V I Пусто м 1000 1000 1000 1000 1000 100G 1000 1000 D 500 500 500 500 500 500 Текущая С —100 —100 100 100 100 100 100 100 цифра L 50 50 50 50 К . —10 —10 10 10 10 10 V 5 5 I —1 —1 1 1 Табл. 6.1 позволяет получить десятичные значения римских цифр. Левая сторона таблицы представляет очередную вычис- ляемую цифру римской системы счисления. Следующая за ней цифра отыскивается по верхнему ряду таблицы. Таким обра- зом, в числе MCMLXXXIX символ С имеет значение —100, а I равно —1. В числе же MDCLXI символ С имеет значение 100, а I равно 1. Пропуски в таблице соответствуют неправильным
206 Глава 6 сочетаниям римских цифр. Такая таблица может храниться в памяти, и для вычисления десятичного значения римской цифры необходимо обращаться к этой таблице. Аналогичная таблица может быть реализована в виде ряда проверочных вопросов. Если учесть упорядоченность римских цифр, представляемую в виде ряда М, D, С, L, X, V, I, таблица может быть переписана в более сжатой форме. Для вычисления значения римской циф- ры в первую очередь осуществляется поиск в таблице с целью определения правильности рассматриваемого сочетания сосед- них символов. Если сочетание правильно, найденное табличное значение прибавляется к накапливаемой сумме. Таблица 6.2. Значения римских цифр Текущая цифра М D с С L X X V I I Цифры справа от теку- щей младше? Да Да Да Нет Да Да Нет Да Да Нет Значение 1000 500 100 -100 50 10 —10 5 1 —1 Сжатая форма таблицы (табл. 6.2) составлена в соответст- вии.с упорядоченностью римских цифр по старшинству. При ис- пользовании этой таблицы сначала осуществляется проверка, является ли следующая цифра за рассматриваемой старше ее. Если да, то числовое значение рассматриваемой цифры вычита- ется из общей суммы, если нет — прибавляется к ней. Ни одна из этих двух таблиц не позволяет провести полную проверку правильности рассматриваемой римской записи числа, так как для этого необходимо установить предел появления каждого из символов. Вычисление арифметического выражения может быть осу- ществлено с помощью таблицы, аналогичной рассмотренной, но содержащей вместо десятичных значений цифр символы, кото- рые могут представлять собой обращения к модулям. Каждо- му символу, найденному в таблице, соответствует определенный модуль. Для вычисления арифметического выражения с помо- щью табл. 6.3 требуется стек, который в начальном состоянии пуст. Арифметическое выражение просматривается слева на- право. В примере ниже приведены вызываемые модули: procedure вст; (Пример 6.11) if (текущий_символ)-1 =значение then верхний—оператор: = текущий_символ; вставить (текущий—символ, стек); читать (текущий-символ) end
Проектирование модулей 207 procedure выт; значением вытолкнут^ (стек); сброс: = вытолкнуть (стек); верхний—оператор: = верх (стек); вставить (значение, стек) „ читать (текущий символ) ь end; procedure выч; значение-2:= вытолкнуть (стек); оператор:=вытолкнуть (стек); значение—1:=вытолкнуть (стек); верхний—оператор:=верх (стек); результата считать (значение—1, оператор, значение-2); ' вставить (результат, стек) end; procedure ошиб; печатать сообщение об ошибке; неисправимая—ошибка:= истина end; procedure конец; return (верх (стек)) , end. Рассматриваемая таблица будет выявлять лишь некоторые син- таксические ошибки. Остальные же можно обнаружить, прове- ряя тип каждого принимаемого из стека элемента, т. е. анали- зируя, является ли он оператором или значением. Кроме того, когда закончены вычисления, в стеке должен остаться только один элемент. Таблица 6.3. Управление вычислением арифметических выражений Текущий символ ( + •— * / ) Значение Пусто Верхний ( вст вст вст вст вст вст вст ошиб оператор + вст выч выч вст вст выч вст выч — вст выч выч вст вст выч вст выч ♦ вст выч выч выч выч выч вст выч / вст выч выч выч выч выч вст выч Пусто вст вст вст вст вст ошиб вст конец
208 Глава 6 Таблица 6.4. Таблица решений для вычисления арифметических выражений Текущий символ «(> Да Нет Нет Нет Нет Текущий символ — значение — Да Нет Нет Нет Текущий символ старше верхнего оператора — — Да Нет Нет Верхний оператор «(» — — — Да Нет Процедура вст вст вст выч выч Так же как и в случае римских цифр, табл. 6.3 может быть сжата и представлена в виде табл. 6.4. Для построения табл. 6.4 использовалась иерархия старшинства операторов: +— ) Эта таблица представляет пример таблицы решений. 6.3.2. Таблицы решений Таблицы решений сочетают в себе характерные черты пред- ложений ветвления if и таблиц передачи управления. Они пред- ставляют собой средство программирования, которое использу- ется в основном при решении экономических задач и предназ- начено для классификации данных и для указания действий, ко- торые необходимо выполнить для каждого класса данных. На- бор таблиц решений может быть использован для организации всей программы. Так как эти таблицы ориентированы на обра- ботку данных, одна из таблиц может определять условия обра- ботки файлов, другая — условия обработки записей, а осталь- ные— описывать условия обработки полей данных. В табл. 4.2' показан пример описания обработки записей для программы сопровождения файла, выполненного с помощью таблиц ре- шений. Таблица решений состоит из четырех частей: столбца усло- вий, входов условий, столбца действий и входов действий. Вза- имное размещение этих частей показано в табл. 6.5. Столбец' условий содержит вопросительные предложения, совокупность ответов на которые позволяет описывать ситуацию. Вход усло- вий представляет собой перечень всех возможных ответов на Таблица 6.5. Таблица решений Столбец условий Вход условий Столбец действий Вход действий
П роектирование модулей 209» указанные вопросы. Столбец действий содержит описания всех действий, которые могут быть выполнены в различных ситуаци- ях. В части таблицы, расположенной под входом условий и на- зываемой входом действий, содержатся пометки, указывающие, какие действия необходимо выполнить при каждой комбинации результатов проверок условий. Совокупность входа условий и. входа действий образует часть таблицы, называемую входом. Каждый столбец входа таблицы называется правилом. В отли- чие от предложений описания состояний условия из столбца ус- ловий не обязательно являются взаимоисключающими. Различают таблицы решений с ограниченным и расширен- ным входами. Таблица решений с ограниченным входом в столбце условий содержит вопросы, на которые можно ответить «да» или «нет», а каждая позиция входа действий содержит по- метку, указывающую на необходимость применения данного действия, или остается незаполненной, если действие не приме- няется. Если правила заполнения части входов отличаются от описанных, тогда говорят, что таблица имеет расширенный вход. Таблица решений может быть полной и неполной. Таблица называется полной, если в ней содержатся столбцы для всех комбинаций ответов «да» и «нет». Табл. 6.4 представляет собой неполную таблицу решений с расширенным входом. Некоторые ответы, соответствующие определенным условиям, исключают друг друга. Столбцы, содержащие комбинации взаимоисклю- чающих ответов, опускаются. Таблица 6.6. Печать наибольшего из А, В и С А>В Да Да Да Да Нет Нет Нет Нет В>С Да Да Нет Нет Да Да Нет Нет А>С Да Нет Да Нет Да Нет Да Нет Печатать А Печатать В Печатать С Ошибка X X X X X X X X Табл. 6.6 является полной таблицей решений с ограниченным входом. Она служит для управления процессом печати наи- большего из трех чисел: А, В и С. Некоторые комбинации (на- пример, А>В, С>А и В>С) невозможны и отмечены во входе действий как ошибочные. Эта таблица имеет ограниченный вход, так как во входе условий содержатся только «да» и «нет», а во входе действий — только пометки и пробелы. Эта 14-399
210 Глава 6 таблица является полной, поскольку на три вопроса, предпола- гающих один из двух ответов, можно ответить восемью (23) способами, и все комбинации ответов в ней представлены. Нужный столбец таблицы решений определяется следующим образом. Сначала сканируется (поэлементно выбирается) слева направо верхняя строка входа условий до того столбца, в кото- ром содержится правильный ответ. Затем по этому столбцу осу- ществляется переход вниз на следующую строку, и сканирование продолжается вправо по этой строке до следующего столбца, содержащего правильный ответ на соответствующий вопрос, и т. д. Движение осуществляется все время вправо и вниз. Стол- бец с последним правильным ответом представляет собой прави- ло, содержащее во входе действий перечень тех действий, кото- рые необходимо выполнить при данной комбинации ответов. В табл. 6.6 при условии А = 1, В = 3 и С=2 первая строка ска- нируется до первого столбца, содержащего «нет». Затем пере- ходим на строку, расположенную ниже и соответствующую срав- нению В и С. Так как для рассматриваемого примера В>С и в этом же столбце для этой строки находится правильный ответ «да», переходим на одну строку вниз и рассматриваем третий вопрос: А>С? Перемещаясь вправо по этой строке, находим правило, соответствующее комбинации ответов нет-да-нет и указывающее на действие «Печатать В». Таблицу 6.6 можно упростить и записать в более компактном виде, если опустить те из столбцов-правил, которые не могут соответствовать реальным условиям. Кроме того, не для всех комбинаций данных необходимо отвечать на все вопросы. Одно- временная истинность первого и третьего условия определяет действие, не зависящее от истинности второго условия (В>С), которое при этих условиях оказывается несущественным. Поэто- му таблицу можно упростить с помощью совмещения первого столбца с третьим, четвертого с восьмым, а также пятого с ше- стым. Несущественное условие в каждом случае отмечается прочерком (—) (табл. 6.7, случай а)). Правила совмещаются только в том случае, если имеют один и тот же набор действий Таблица 6.7. Упрощенные формы таблицы решений для печати наибольшего числа из А, В и С а) Таблица с ограниченным входом б) Таблица со смешанным входом А>В Да Нет А>В Да Нет В>С Нет Да В>С — Нет Да А>С Да Нет — А>С Да Нет — Печатать А Печатать В Печатать С X X X Печатать А С В
Проектирование модулей 211 и различаются только одним входным условием. Использование прочерка упрощает формирование таблицы вручную, но не оз- начает использования расширенного входа вместо исходного ограниченного. Дальнейшее упрощение (табл. 6.7, случай б)} приводит к нарушению ограниченности входа. Табл. 6.7,6 име- ет вход смешанного типа. На стадии подготовки спецификации прикладных программ таблицы решений помогают выявлять пожелания пользовате- лей относительно режимов обработки данных. Таблица может заполняться во время обсуждения свойств системы пользовате- лем и системным аналистом. Если ответы «да» и «нет» простав- лены заранее, они служат для аналиста «сигналами» о режимах обработки нерегулярных данных. Основной недостаток исполь- зования таких таблиц решений заключается в том, что в обыч- ных таблицах, заполняемых при оформлении технического за- дания, содержится гораздо больше различных типов данных,, чем может быть размещено в единственной таблице решений при условии сохранения ее наглядности. В рабочих материалах может рассматриваться намного больше различных комбинаций условий, а также намного больше соответствующих им дей- ствий. Полную таблицу решений, представляющую более пяти или шести условий, уже довольно трудно изобразить. Эффективную же обработку таблицы с числом условий, намного превышаю- щим 8 или 10, трудно выполнить даже с помощью ЭВМ. При числе условий, равном п, число правил составляет 2п, т. е. чис- ло столбцов входа таблицы растет по экспоненциальному зако- ну. При больших значениях п проверка вручную таблицы на из- быточность и противоречивость представляет большие труд- ности. Таблица решений избыточна, если хотя бы два ее столбца перекрывают друг друга по значениям условий и определяют одни и те же действия (например, как в табл. 6.8,а). Таблица решений противоречива, если хотя бы два столбца ее перекры- вают друг друга по значениям условий, но определяют различ- ные действия (как, например, в табл. 6.8,6). Проверка условий таблицы, а также совмещение столбцов путем выявления несущественных условий, отмечаемых прочер- Таблица 6.8. Неправильно а) Избыточная таблица сооставленные таблицы решений б) Противоречивая таблица с, с2 Да Нет С, с2 Да - — Нет X X л2 X X 14*
12 Глава 6 ками, обычно производятся в произвольном порядке, хотя ка- кой-либо один порядок выполнения этих операций может иметь преимущество перед другими, В том случае, когда последова- тельность проверок условий должна подчиняться какому-либо порядку (например, если проверка конца файла производится ^обязательно перед проверкой карты, закрывающей файл, кото- рая в свою очередь выполняется перед проверкой смены дан- ных), легче использовать расширенную форму таблицы решений. Таблицу 4.2 можно было бы использовать совместно с таблицей, описывающей соответствующие действия для случа- ев, когда главный файл пуст или файл сообщений не отсортиро- ван, а также с таблицами, описывающими проверку правильно- сти записей и их обновление. Каждая таблица решений представляет конструкцию выбо- ра, альтернативные ветви которой, возможно, представляют со- бой конструкции следования. Все четыре управляющие базовые конструкции могут быть описаны с помощью таблиц решений. .Для этой цели используются ссылки одних таблиц на другие и на самих себя. Следование определяется порядком перечисления действий в столбце действий. Логика всех таблиц решений в своей основе представляет конструкцию выбора. Цикл while do соответствует повторному использованию таблицы решений. Таблица, ссылающаяся на саму себя, может быть реализована в виде цикла или в форме рекурсивных обращений. Обработке исключительных состояний соответствуют определенные столб- цы таблицы. Вызовы, возвраты, а также выходы из процедур представляются в форме явных действий. Для создания программного модуля, соответствующего таб- лице решений, последнюю сначала следует представить в форме дерева решений. Чтобы эффективно использовать таблицу реше- ний, правила должны быть организованы таким образом, что- бы число проверяемых условий было минимальным. Существу- ют различные методы минимизации числа проверяемых на практике условий. Они отличаются друг от друга затратами на их реализацию и применение какого-либо из них зависит от сложности преобразований таблицы решений и требований к .эффективности проверки. Если условия таблицы являются не- зависимыми и значения условий распределены в таблице нерав- номерно (например, по какому-либо условию ответов «да» боль- ше, чем «нет»), в первую очередь должны проверяться условия, которые разбивают таблицу на части, по возможности одинако- вые по размерам. Если для упрощения таблицы некоторые ее правила могут быть объединены, в первую очередь проверяются условия, соответствующие наименьшему числу прочерков, так как они являются наиболее существенными для определения соответствующего правила. Таблица решения после проверки условия разделяется на две части, и процесс повторяется для
Проектирование модулей 213 г Рис. 6.10, Таблица решений и соответствующие ей деревья решений. а — таблица решений; б — дерево решений для случая, когда в первую очередь прове- ряется условие С\', в — керека решений для случая, когда в первую очередь проверяется условие Сг\ г — дерево решений для случая, когда проверяется условие Сз. каждой половины (таким образом, заранее строится дерево проверок). Порядок проверки условий может влиять и на объем ис- пользуемой памяти, и на время работы модуля, как это следует из примера, приведенного на рис. 6.10. Все три варианта дерева решений получены из единственной таблицы решений с помощью изменения порядка проверки условий. Сбалансированное дере- во (рис. 6.10, г) получено из таблицы решений без выделенных
214 Глава 6 прочерками несущественных условий. Варианты дерева реше- ний, показанные на рис. 6.10, б и в, построены по упрощенной таблице решений. Для реализации последних двух вариантов требуется меньший объем памяти, чем в случае сбалансирован- ного дерева, и выполнение проверок производится в среднем быстрее. 6.4. Документация модулей Документация модулей представляет собой документацию самого нижнего уровня (если не считать уровня кодов про- грамм) и используется в основном при сопровождении и моди- фикации программ. На документацию модулей значительное1 влияние оказывает используемый язык программирования. Для модулей, которые реализуются в виде внутренних процедур, со- стоящих из небольшого количества команд, требуется неболь- шой объем документации. Для модулей же, представляющих со- бой внешние процедуры, т. е. независимо компилируемые под- программы, требуется больший объем документации. Для про- граммиста, занимающегося сопровождением программного обес- печения, наиболее важной является документация, в которой описываются отдельные подпрограммы, поэтому оформлению- документации этого уровня следует уделять большое внимание.. Программная документация должна быть организована та- ким способом, чтобы документацию по отдельным модулям: можно было легко найти. Для этой цели могут служить ог- лавления, описания программ или иерархические схемы, а так- же индексные указатели и основные характеристики для отдель- ных модулей. Аналогичные требования предъявляются к доку- ментации для внутренних подпрограмм. Однако для этих под- программ в общем случае не требуется отдельная документация и может оказаться достаточной идентификация подпрограмм по- имени. Для более удобного оформления документации модули ор- ганизуются определенным образом и, кроме того, для них со- ставляются словари перекрестных ссылок с указанием назначе- ния отдельных ссылок. Для каждого модуля достаточно под- робно описывается логика его работы и указываются виды обработки исключительных состояний, чтобы программист, не принимавший участия в разработке модуля, мог модифици- ровать модуль и вносить корректировку. Каждая подпрограмма обозначается на иерархической схеме, а алгоритм ее работы описывается с помощью каких-либо средств проектирования вы- сокого уровня, таких, как структурные схемы, псевдокод и т. д. Описание каждой подпрограммы должно быть функциональным и содержать следующие части: _
Проектирование модулей 215 Идентификатор модуля. Содержит номер идентификации и имя модуля и, кроме того, расширенное имя, поясняющее его значение. Основные функции модуля. Представляет краткое описание общего функционального назначения подпрограммы и цель ее создания. Этот раздел может включать функциональные описа- ния подмодулей. Описание положения модуля в общей структуре. Содержит описок подпрограмм, вызывающих модуль. Этот раздел входит как часть в словарь перекрестных ссылок. Пример вызова модуля. Поясняет порядок следования и чис- ло параметров. Для некоторых языков программирования необ- ходимо также указать типы и способ передачи параметров. Список вызываемых подпрограмм. Представляет собой часть словаря перекрестных ссылок. Входные параметры и исходные данные. Этот раздел состоит из словаря внешних для модуля данных и словаря информации, используемой при операциях обмена. Здесь представлены типы всех данных, а также допустимые границы их значений. Кроме того, каждый .из параметров классифицируется как входной или выходной и указываются входные или выходные условия, свя- занные с этими параметрами. Словарь внутренней информации. Содержит все флаги, ре- зультаты вычислений, таблицы и другие структуры данных и элементы, определяемые на этапе проектирования. Временные переменные, индексы, указатели и описатели, которые не могут быть определены на указанном этапе, в эти словари не вклю- чаются. Описание основных алгоритмов. Представляет собой доку- ментацию алгоритмов, рассмотренную в гл. 5. Описание внутренних процедур. Включает имена, функции и вызываемые последовательности всех внутренних процедур. Все виды доступа к глобальным данным должны быть представлены в этом разделе. Описание потоков данных и структуры модуля; структурная схема переходов управления в модуле. Все схемы, созданные в процессе разработки модуля, следует включать в состав доку- ментации. Среди них могут быть схемы HIPO для модуля него подмодулей (если этот вид схем применялся при их разработ- ке), схемы потоков данных, псевдокод, структурные схемы мо- дуля, а также таблицы решений. Описание обработки ошибок. В этом разделе приводится перечень ошибок, которые могут быть обнаружены, а также ука- заны действия, предпринимаемые в случае их обнаружения. Данные об эффективности модуля. Содержат тесты для про- верки модуля и результаты, получаемые с их помощью. Все Данные по эффективности и сложности модуля приводятся в
216 Глава 6 этой части документации. Здесь же указываются все возможные ограничения на режимы использования модуля и на требуемый объем памяти. 6.5. Упражнения * 1. Напишите программу печати данных из последовательного входного файла, если данные содержат одно управляющее поле, а печать должна быть организована так, что каждая новая группа записей начинается на новой стра- нице, каждая из которых содержит не более п строк. Для этого используйте: а) структурные схемы; б) схемы Насси — Шнейдермана; в) таблицы реше- ний. 2. С помощью структурных схем или схем Насси — Шнейдермана разра- ботайте систему модулей, осуществляющих чтение из последовательного фай- ла. Файл содержит группы по 51 записи, из которых 50 содержат код «S» и число, а 51-я состоит из кода «Т» и числа, представляющего сумму 50 чисел, содержащихся в предыдущих записях. Система проверяет наличие в каждой группе точно 50 записей типа «S», после которых следует одна-запись типа «Т», допуская при этом, что в последней группе может содержаться меньше 50 записей типа «S». Если число записей группы (или сумма, находящаяся в последней записи) окажется неправильным, группа должна быть распеча- тана. 3. Разработайте систему модулей для решения упражнения 12 гл. 4 с по- мощью структурных схем или схем Насси — Шнейдермана. 4. Разработайте систему модулей для задачи расчета числа треугольников и прямоугольников из упражнения 15 гл. 5 с помощью структурных схем или схем Насси — Шнейдермана. 5. Разработайте систему модулей для решения задачи поиска пути для игры в китайские шашки на одного игрока из упражнения 14 гл. 5 с помощью структурных схем или схем Насси — Шнейдермана. 6. Сколько столбцов содержится в полной таблице решений с ограничен- ным входом, содержащей четыре (пять, шесть) условия? Если все действия отличаются друг от друга, сколько может существовать вариантов дерева решений для таблицы решений с четырьмя (пятью, шестью) условиями? 7. Составьте таблицу решений для определения правильности представле- ния римских цифр и для их вычисления. 8. Разработайте таблицу решений для проверки данных. Данные содержат следующие поля: а) порядковый номер (число); б) имя (алфавитный код); в) код потребителя (цифры 1, 2, 3 или 4); г) месяц (цифры от 1 до 12); д) код поставки (буквы «D» или «W»); е) объем поставки (положительное число); ж) баланс (неотрицательное число, и, если код поставки равен «W», ба- ланс не должен быть меньше объема поставки). Если какое-либо поле пропущено — фиксировать ошибку. Если код по- ставки неправилен, баланс не проверяется. Модуль устанавливает флаг пра- вильности данных и печатает их и, если имели место ошибки, сообщения об ошибках. 9. Разработайте таблицу для сложения римских цифр, позволяющую по- лучать сумму в виде правильной римской цифры, не пользуясь переводом чи- сел в другую систему счисления.
Глава 7 РЕАЛИЗАЦИЯ ПРОГРАММНОГО МОДУЛЯ Кодирование связано, с одной стороны, с грамматикой языка программирования, а с другой — со стилем написания программ. Если заданы язык программирования и проект модуля, послед- ний должен быть адаптирован к смысловым конструкциям язы- ка. Кроме того, надо стремиться к тому, чтобы проект был за- программирован эффективно. Стилистика программирования в настоящее время достигла такого уровня, когда существуют критерии «хорошего» стиля написания сегментов исходного текста программы, так же как существуют критерии «хорошего» стиля написания разделов текста на естественном языке. Многие критерии, используемые для оценки текста на естественном языке, пригодны и для оценки программ. Человек, который пишет программы, должен уметь ясно выражать свои мысли, правильно писать слова, эф- фективно использовать интервал и быть последовательным в стиле написания. 7.1. Подходы к реализации Фазы проектирования и реализации программной системы могут перекрываться, если они выполняются одним и тем же ме- тодом. Когда разработка выполняется по нисходящему прин- ципу, можно начать нисходящее кодирование и тестирование, как только завершается проектирование модуля. То же самое справедливо для разработки по восходящему принципу. Если же- проектирование системы полностью завершено, реализацию можно проводить любым из названных выше методов или двумя одновременно. 7.1.1. Нисходящее кодирование При реализации программы нисходящим методом первым ко- дируется головной модуль. Если он вызывает другие модули, кодируются фиктивные модули, называемые заглушками. С по- мощью заглушек можно организовать непосредственный воз- врат в вызывающий модуль или осуществить передачу значений тестовых данных и печать результатов работы. Если же заглуш-
218 Глава 7 ки имеют параметры, полезно организовать печать этих пара- метров. На рис. 7.1 представлена двухуровневая иерархическая структура, в которой первым кодируется головной модуль О, а заглушки 1 и 2 используются для его отладки. Поток данных между этими модулями представляет собой передачу корректи- рующей записи из модуля 1 в модуль 2 через модуль 0, пере- дачу метки конца файла корректирующих записей из модуля 1 в модуль 0 и метки конца основного файла из модуля 2 в мо- дуль 0. Последняя передача осуществляется при обнаружении конца основного файла до того, как будет обработана последняя корректирующая запись. Модули могут быть оттестированы с о Рис. 7.1. Нисходящее кодирование. помощью заглушки 1, считывающей фиктивные записи, и за- глушки 2, распечатывающей записи основного файла и обнару- живающей метку конца файла. Таким образом проверяется не только логика головного модуля, но также связи с другими мо- дулями. После того как отлажен головной модуль, заглушки после- довательно заменяются на функциональные модули, которые в свою очередь будут вызывающими для следующих заглушек. При завершении реализации программных модулей нижнего уровня иерархической структуры можно гарантировать, что из-за большого числа текстовых прогонов управляющие модули верхнего уровня практически не будут содержать ошибок. 7.1.2. Восходящее кодирование Когда программа проектируется по восходящему принципу, ее кодирование должно выполняться тем же методом. Первыми модулями, подлежащими кодированию, являются вспомогатель- ные подпрограммы и подпрограммы ввода-вывода нижнего уров- ня. Поэтому в процессе создания всей программы модули, ис- пользуемые чаще других, будут оттестированы наиболее тща- тельно. Если при нисходящем кодировании необходимо писать за- глушки. вызываемые разрабатываемым модулем, при восходя-
Реализация программного модуля 219 щем кодировании необходимо использовать драйверы. Драйвер для модуля ввода данных, имеющего параметры, может вызы- вать этот модуль, передавая ему множество различных значе- ний параметров, а получив снова управление, распечатывать результаты ввода. Для модулей вывода данных драйвер может считывать тестовые данные и передавать их на входы этих мо- дулей. Если завершена разработка нескольких модулей нижне- го уровня, которые взаимодействуют с помощью программы вышележащего уровня, все эти модули могут быть оттестиро- ваны совместно общим драйвером. Только когда завершена раз- работка всех модулей, необходимых для вызывающего модуля, можно приступать к его кодированию. Если в работе над проектом планируется использовать не- скольких программистов, при нисходящем кодировании они привлекаются к работе по мере развертывания иерархической схемы проекта сверху вниз. При восходящем кодировании после окончания разработки проекта все программисты могут начать работу одновременно, освобождаясь по мере объединения ча- стей проекта. В процессе разработки по восходящему принципу значительное место во взаимодействии программистов занимает согласование связей между программами. Когда связи будут спроектированы, должна быть уверенность в том, что из виду не упущена ни одна деталь. 7.1.3. Смешанное кодирование Этот метод кодирования соответствует методу расширения ядра, используемому при проектировании программ. Когда про- ект ориентирован на использование структур данных, следует выделить две группы модулей, а именно модули нижнего уровня, в которых определены структуры данных и которые их обраба- тывают, и управляющие модули верхнего уровня, которые коор- динируют ввод, и вывод данных. Эти модули могут кодироваться одновременно в направлении к средним уровням. Вместо напи- сания заглушек полезно иметь работающие подпрограммы вво- да-вывода. Без этих подпрограмм можно было бы говорить о нисходящем кодировании. Этот подход можно также назвать подходом «с двух сторон одновременно». Правое поддерево иерархической схемы модуля реализуется легче, если уже было закодировано левое. В общем случае в левом поддереве заго- тавливаются данные, пригодные для использования в правом поддереве. Для кодирования слева направо (как сверху вниз, так и снизу вверх) требуется одно и то же число людей. Основ- ная проблема состоит в том, что, начиная кодирование с двух концов, программисты могут не встретиться в середине. При та- ком подходе необходим строгий контроль над проектом.
220 Глава 7 7.2. Реализация данных На этапе проектирования модуля уже должны быть готовы описания большинства данных, используемых в каждом модуле. Для их реализации более предпочтительны простые представле- ния данных. Большинство типов данных может храниться в виде скаляров, однородных одномерных массивов или записей. Ин- формация, которая логически представляет собой множество, дерево, стек или какую-либо другую структуру, не поддерживае- мую языком программирования, должна быть описана в от- дельной группе модулей и скрыта для остальных. Тогда доступ к ним может быть организован так, как будто эти данные под- держиваются языком. Значения в зависимости от их применения можно классифи- цировать следующим образом: а) константы, б) системные константы, в) значения ссылок, г) значения времени выполнения, д) временные значения. 7.2.1. Константы Константы, системные константы и значения ссылок обраба- тываются внутри модуля как постоянные величины. Они разли- чаются только сроками использования. Истинными константами являются величины, значения которых никогда не меняются. Они могут быть литералами или поименованными значениями. Истинные константы должны иметь имена в том случае, если их распознавание легче осуществить по имени, чем по значению (например, имя PI более понятно, чем значение 3.145926). В том случае, если истинные константы передаются в качестве аргу- ментов в другие подпрограммы, они также должны иметь имена. Рассмотрим пример INTEGER TEN (Пример 7.1> DATA TEN/10/ CALL GET (LIST, TEN) Если подпрограмма GET изменяет передаваемый ей второй па- раметр, это может быть обнаружено в вызывающей подпрограм- ме путем проверки значения TEN. Если был передан литерал 10, обнаружить ошибку трудно. Имена констант должны соответст- вовать их значениям, как, например ZERO для 0, ONE для 1, FALSE для «FALSE», Т для «Т», причем в той степени, на- сколько позволяет используемый язык. Если константа имеет определенный смысл (например, в случаях обозначения разме- ра массива, идентификации внешнего устройства или выбора
Реализация программного модуля 221 сообщения об ошибке), ей необходимо присвоить имя, соответ- ствующее ее использованию в качестве системной константы или значения ссылки. Системные константы определяются характеристиками вы- числительной среды. Системными константами могут быть внут- ренние коды символов, размер машинного слова и характеристи- ки внешних устройств, такие, как размер дорожки и длина стро- ки печатающего устройства. Этим константам должны быть при- своены мнемонические имена, которые помещаются в отдельный модуль и обрабатываются как описания абстрактных данных. Их значения изменяются только с заменой самих устройств. Имена должны быть присвоены даже таким системным констан- там, как номер логического устройства для АЦПУ и устройства чтения с перфокарт. Использование литеральных констант де- лает программу менее мобильной, поскольку номера логических устройств в разных системах различны. Кроме того, смысл ли- тералов часто бывает неясен. НЕ ЗАБЫВАЙТЕ ПРИСВАИВАТЬ НАЧАЛЬНЫЕ ЗНАЧЕНИЯ ПОИМЕНОВАННЫМ КОНСТАНТАМ Данными типа значения ссылок являются налоговые табли- цы, коды категорий клиентов и списки цен, которые изменяются редко. Если ожидается, что эти данные будут оставаться неиз- менными в течение большого числа рабочих прогонов програм- мы, они могут использоваться в программе как имена с ранее присвоенными начальными значениями. Их следует считать абстрактным описанием данных пользовательской среды и оп- ределять только в одном программном модуле. Если же данные все же меняются от прогона к прогону, значения ссылок следу- ет считывать в память в процессе каждого выполнения програм- мы. Список цен на бакалейные товары, изменяемый ежедневно, может храниться на диске и считываться с него. Число дней в феврале меняется каждые четыре года, поэтому оно определяет- ся системой только один раз. Если значения ссылок не считыва- .ются в память, им присваиваются начальные значения с помо- щью оператора DATA (в Фортране), фразы VALUE (в Коболе) или атрибута INITIAL (в ПЛ/1). В таких языках, как Паскаль и Ада, они должны быть объявлены константами. Если в модуле содержится много скалярных величин, их можно объединить в группы в соответствии с правилами исполь- зования (как в Коболе): 01 STATUS-FLAGS. (Пример 7.2). 05 CARD-EOF PIC X. 05 DISK-EOF PIC X. 05 INVALID-DATA PIC X.
222 Глава 7 01 TRANSACTION-TOTALS. 05 TOTAL-DEPOSIT PIC 9(7)V99. 05 TOTAL-WITHDRAWAL PIC 9(7)V99. 05 TOTAL-SERVICE PIC 9V99. Однако это возможно и в языках (подобных Фортрану), кото- рые не имеют средств описания структур: С —STATUS FLAGS (Пример 7.3) LOGICAL CRDEOF, 7 — DSKEOF, — BADCRD <C TRANSACTION TOTALS REAL TOTDPT, — TOTWDL, — TOTSC В Коболе уровень 01 вводится лишь для организации иерархии данных и целей документирования. В Фортране для тех же це- лей используются комментарии и пунктирные линии. В языках Паскаль и ПЛ/1 структура — обычный способ описания данных. .Аналогичным образом можно сгруппировать нескалярные эле- менты данных. группируйте данные по функциональным признакам Связанные по смыслу списки значений, такие, как макси- мальные и минимальные значения или коэффициенты в табли- це, можно хранить в двумерном массиве, но разумнее разме- щать их в одномерных массивах равной длины. Данные, раз- личные по природе, не следует помещать в один массив. Ис- пользование структур позволяет хранить неоднородные данные в одном массиве, однако работать с такими массивами труднее, чем с массивами, содержащими однородные как по смыслу, так и по типу и размеру элементы. Мнемонические имена для мас- сивов выбирают так, чтобы они давали представление о типе хранящегося элемента. Не следует присваивать таких имен, кай ARRAY или TABLE, которые характеризуют только форму хра- нения данных. Если размер таблицы коэффициентов равен 4, но он может изменяться, следует объявить РАЗМЕР-ТАБЛИЦЫ-КОЭФФИ- ЦИЕНТОВ как константу, которая должна использоваться вся- кий раз, когда размер упоминается в модуле, например INTEGER TBLSIZ (Пример 7.4) DATA TBLSIZ/4/ DIMENSION RATE (TBLSIZ) Такое описание следует включить в модуль, который содержит табличные значения. В этом случае изменения вносятся не бо- лее чем в два места программы: в описание табличных значе- ний и в описание размерности самой таблицы. Кроме того, не потребуется вносить изменения в управляющие операторы цик-
Реализация программного модуля 223 ла и в операторы проверки границ индекса. Если таблица пере- дается в другие модули, следует применять описание ее теку- щей размерности. При таком подходе не только информация о данных размещена в возможно меньшем количестве модулей» но и уменьшается количество описаний и инструкций, что об- легчает сопровождение. ИСПОЛЬЗУЙТЕ СРЕДСТВА ДЛЯ ОПИСАНИЯ ТЕКУЩЕЙ РАЗМЕРНОСТИ ДАННЫХ 7.2.2. Переменные Переменная однозначно определяется именем. Значения пе- ременных связаны с ними не более чем на период одной акти- вации программы. Следует различать врёменные значения и значения времени выполнения. Такое различение необходимо* главным образом для целей документирования программы. Пе- ременные, которые важны для выполнения функций данного мо- дуля и используются большинством его операторов, следует включить в словарь данных и присвоить им соответствующие мнемонические имена. Врёменные переменные, к которым обра- щаются лишь в процессе выполнения нескольких операторов программы (такие, как управляющие переменные циклов, ин- дексы и результаты промежуточных операций), могут быть- включены в словарь данных по желанию программиста. Врё- менные переменные и переменные времени выполнения должны всегда обрабатываться как переменные, а не как константы. Для их инициализации следует использовать выполняемые опе- раторы. Не следует заранее инициализировать такие элементы,, как суммы и счетчики, которые впоследствии изменяются. НЕ ИНИЦИАЛИЗИРУЙТЕ ПЕРЕМЕННЫЕ ЗАРАНЕЕ Если необходимо установить начальное значение таким эле- ментам, как генератор случайных чисел или флаг, указываю- щим состояние файла (открыт или закрыт), — и это не может- быть сделано с помощью выполняемого оператора — соответ- ствующее значение должно быть передано как параметр. Если: язык программирования позволяет продолжать выполнение про- граммы после выявления ошибок ввода-вывода (таких, как по- пытка чтения неоткрытого файла), процесс восстановления пос- ле ошибки предпочтительнее, чем использование состояния фла- га. Передача флага инициализации приводит к размещению данных в одной подпрограмме, в то время как они логически относятся к другой. Следует избегать применения флагов, по- казывающих, является ли текущая активация подпрограммы: первой. Значения флагов должны сохраняться во время много- кратных выполнений программы, иначе программа не будет ре-
224 Глава 7 ентерабельной и повторно используемой. Использование флагов также нежелательно при применении оверлейных структур. ПИШИТЕ ПРОГРАММЫ, КОТОРЫЕ ДОПУСКАЮТ ОДНОВРЕМЕННОЕ МНОГОКРАТНОЕ ВЫПОЛНЕНИЕ Все переменные и поименованные константы должны иметь мнемонические имена, которые отражали бы их смысл. Имена должны записываться аккуратно и не должны быть слишком похожими друг на друга. Не используйте такие имена, как PRNTER и PRINTR. Поскольку они очень похожи, их легко спутать, а также трудно вспомнить, какое из них использова- лось. Наличие связи между переменными можно показать с помощью их имен. Если .буфер ввода-вывода имеет имя BUF, связанным с буфером переменным могут быть присвоены имена BUFPTR и BUFSIZ. Функционально связанные переменные должны иметь похожие имена. Такими именами в случае про- смотра нескольких массивов могут быть BUFPTR, CRDPTR и TBLPTR, а также BUFSIZ, CRDSIZ и TBLSIZ. Следует избе- гать имен, отличающихся только последним символом (как, на- пример, PTRB, PTRC и PTRT). БУДЬТЕ ВНИМАТЕЛЬНЫ ПРИ ВЫБОРЕ ИМЕН В том случае, когда временные переменные не включены в словарь данных, необходимо использовать общепринятые обо- значения, если они существуют; например, лучше использовать A(i, j), чем А(/, i), и А*Х**2 + В*Х+С, чем X*A**2+Y*A+Z. Все имена данных, включая имена временных переменных, должны быть явно отнесены к определенному типу, даже если язык не требует этого. Назначение атрибутов по умолчанию приводит к появлению странных имен данных, таких, как ITALLY. В языке ПЛ/1 это может привести к неожиданным ре- зультатам. Использование в одних случаях объявления по умол- чанию, а в других явного объявления атрибутов, стилистически непоследовательно и затрудняет обнаружение ошибок. Если всем переменным явно назначены атрибуты и даны соответст- вующие комментарии, они играют роль словаря данных в лис- тинге исходного текста программы. ОБЪЯВЛЯЙТЕ ТИПЫ ДАННЫХ В ЯВНОЙ ФОРМЕ 7.3. Реализация ввода-вывода Модули могут вводить и выводить информацию двумя спо- собами. В первом способе данные передаются с помощью меж- модульного интерфейса и общих областей пам’яти, а во вто- .ром — от модулей к внешним устройствам и наоборот.
______________Реализация программного модуля________ 225 7.3.1. Обработка входных данных Необходимо всегда проверять достоверность данных, посту- пающих от внешних устройств. Не доверяйте пользователю или аппаратуре. ПРИ ПРОГРАММИРОВАНИИ ПРЕДУСМАТРИВАЙТЕ ЗАЩИТУ ОТ НЕПРАВИЛЬНЫХ ДАННЫХ Данные, поступающие из других подпрограмм и внутренних областей хранения данных, должны быть проверены модулем, который их использует. Не доверяйте другим программистам. Не доверяйте даже себе. Необходимо проверять тип данных, пропуск полей, знак и принадлежность к известному диапазону значений. Если в данных содержится избыточность (такая, как одновременное присутствие номера счета и фамилии клиента или кода выпуска и описания выпуска), следует проанализиро- вать таблицу перекрестных ссылок, чтобы убедиться, соответст- вуют ли друг другу поступающие элементы. Если в данных со- держатся счетчики записей, контрольные цифры, контрольные суммы и другие средства контроля, их следует использовать для проверки данных. Счетчик записей представляет собой число, равное количест- ву ожидаемых записей. Не используйте такой счетчик для уп- равления обработкой записей. Это может привести к ошибке. Вместо этого сосчитайте количество обработанных записей, сравните результат со значением счетчика записей и запишите отклонение. Контрольная цифра — это цифра, присоединяемая к некото- рому важному значению, такому, как номер счета. В результате формируется число, обладающее определенным математическим свойством. Контрольная цифра используется для проверки чис- ла с целью обнаружения ошибок перезаписи. Например, можно присоединить к числу такую контрольную цифру, что получив- шееся число будет кратно 11. Контрольная цифра может быть подобрана так, чтобы отразить все или чередующиеся цифры числа. Лучшие методы, основанные на использовании контроль- ных цифр, выявляют не только неверные цифры, но и ошибки в порядке их следования. Контрольная сумма представляет собой сумму чисел, нахо- дящихся в определенном поле некоторого множества записей. Например, можно сложить номера счетов в каждой группе, со- стоящей из 50 корректирующих записей. Результат сложения помещается в 51-ю запись. Это не относится к решению задачи, но может быть использовано для обнаружения ошибок. Подоб- ные суммы могут быть вычислены для всех чисел в записи и по- мещены в нее. Как контрольные цифры, так и контрольные сум- мы часто используются для двоичных данных. 15-399
226 Глава 7 Все внешние входные данные должны попасть на экран тер- минала, устройство печати или в регистрационный файл. Следу- ет отмечать неправдоподобные значения, такие, как цены в 1 000 000 долл. При программировании ввода с терминального устройства необходимо предусмотреть подтверждение правиль- ности ввода важных и неправдоподобных входных данных. Ошибки при передаче данных между модулями в основном возникают из-за различий в атрибутах данных. Эти различия обычно не могут быть обнаружены в результате просмотра ис- ходного текста. Большинство из них обнаруживается при отлад- ке модулей путем проверки принадлежности входных данных допустимому диапазону значений. Если при вызове модулю пе- редается действительный аргумент, в то время как он должен быть целым, или наоборот, это, вероятно, будет обнаружено с помощью проверки диапазона значений. Каждый модуль дол- жен быть написан так, чтобы при его отладке могла быть вы- полнена проверка принадлежности входных данных допустимо- му диапазону значений. Операторы проверки входных и выход- ных данных должны постоянно присутствовать в модулях, по- скольку даже при наличии правильной программной связи между модулями существует возможность выхода за пределы диапазона значений вновь созданных или переданных данных. Если подпрограмма контролирует выход за пределы диапазона возможных значений, это должно найти отражение в докумен- тации. проверяйте достоверность данных 7.3.2. Обработка выходных данных Выходные данные перед передачей внешнему устройству должны быть проверены. Для проверки данных с преобладани- ем цифр используются такие методы контроля, как промежуточ- ное и перекрестное суммирование. Для всех видов вывода должна собираться статистика прогонов. Вычисление общей суммы выполняется отдельно от вычисле- ний любых промежуточных сумм. Результат сложения промежу- точных сумм в этом случае можно сравнить с общей суммой с целью обнаружения переполнения или ошибок в логике про- граммы. Если при обработке промежуточных сумм не возникает прерываний, эти суммы можно генерировать обычным путем для целей внутреннего контроля. ДУБЛИРУЙТЕ ПРОВЕРКУ ОТВЕТОВ Перекрестное суммирование представляет собой метод, при котором суммирование проводится по строкам и столбцам, пос- ле чего складываются суммы строк и столбцов и полученные результаты сравниваются между собой с целью выявления оши-
Реализация программного модуля 227 бок. С точки зрения самих данных эти суммы могут иметь .(или не иметь) смысл. Они могут быть (или не быть) частью выход- ных данных. Если значение счетчика записей известно, его мож- но использовать для проверки правильности числа отпечатан- ных заполненных строк. Если значение числа записей неизвест- но и вывод осуществляется на запоминающее устройство типа магнитного диска или магнитной ленты, необходимо подсчитать число записей и поместить его в первую запись вместе с другой информацией о файле. Необходимо также проверять достоверность выходных дан- ных и помечать маловероятные значения. Результаты проверки таких значений, как 1 000 000 долл., помечаются вручную. 7.3.3. Редактирование Важно, чтобы значения данных имели форму, удобную для восприятия, когда они обрабатываются людьми, и машинно-ори- ентированную форму, когда они обрабатываются машиной. Ре- дактирование текста может заключаться в перемещении симво- лов пунктуации, пробелов и разделителей. Не перекладывайте черновую работу на пользователей. Пусть редактирует ЭВМ. ПИШИТЕ ПРОГРАММУ ТАК, ЧТОБЫ ЧЕЛОВЕКУ УДОБНО БЫЛО ЕЮ ПОЛЬЗОВАТЬСЯ Желательно, чтобы данные вводились поименно в свободном формате, как это делается с помощью оператора GET DATA в языке ПЛ/1. Этот оператор идентифицирует данные по имени, а не по занимаемой позиции или порядку следования во входной записи. Такой ввод удобен для восприятия и позволяет избе- жать ошибок в размещении данных. Если это возможно, разре- шайте вводить числа с десятичной точкой и стоящими слева пробелами. Используйте значения, принятые по умолчанию. Пользователь, корректирующий файл, не должен повторно вво- дить информацию, которая не изменяется. Элементы входных данных должны быть отредактированы и проверены на достоверность сразу же после того, как они по- лучены. Значения выходных данных должны быть также прове- рены на достоверность и отредактированы непосредственно пе- ред передачей их устройству вывода. Редактирование выходных данных должно включать присваивание меток ответам, вырав- нивание и разделение элементов данных и печать чисел без стоящих слева нулей, но со знаками, десятичными точками и при необходимости со знаками доллара. Для обработки и хра- нения входных данных в машинно-ориентированной форме их не нужно ни редактировать, ни приводить к определенным фор- мам. В целях эффективной обработки данных, расположенных на диске и ленте, следует использовать двоичный ввод-вывод. 15*
228 Глава 7 7.4. Реализация управления Программные модули, спроектированные с использованием базовых управляющих структур, обычно необходимо в той или иной мере модифицировать, чтобы они соответствовали структу- рам, поддерживаемым языком программирования. При этом должны использоваться только стандартные средства языка. 7.4.1. Модификация управляющих структур Использование языка программирования с блочной структу- рой, такого, как Паскаль или ПЛ/1, потребует небольшой моди- фикации проекта; использование Кобола приведет к несколько большей модификации проекта, а использование Бейсика и Фор- трана, в которых отсутствуют многие управляющие структу- ры,— к весьма существенной модификации. Фортран не поддер- живает никаких базовых структур, за исключением следования. Базовые управляющие структуры, предусмотренные в псевдо- коде, можно смоделировать на любом языке, снабдив их ком- ментариями. В Фортране структуры выбора IF(C> GO ТО 10 (Пример 7.5) GO ТО 20 С THEN 10 action с GO ТО 30 ELSE 20 action GO TO 30 30 CONTINUE и c CASE OF C IF(C1) GOTO 10 IF(C2) GOTO 20 (Пример 7.6) GO TO 90 c WHENC1 10 action 1 GO TO 100 c WHEN C2 20 action 2 GO TO 100
Реализация программного модуля 229 С ' OTHERWISE 90 default action GOTO 100 C ENDCASE 100 CONTINUE и структуры повторения WHILE С DO (Пример 7.7) 10 IF (.NOT. С) GO TO 20 action GOTO.10 20 CONTINUE и C REPEAT (Пример 7.8) 10 action IF (.NOT. C) GOTO 10 C UNTIL C требуют только абзацного отступа и нескольких карт коммента- риев. На Коболе также можно описать управляющие структуры. Оператор PERFORM может использоваться для реализации обеих базовых структур повторения, но при этом управление циклом отделяется от тела цикла. Цикл while do принимает форму PERFORM A UNTIL NOT С. (Пример 7.9) Цикл repeat until принимает форму PERFORM А. (Пример 7.10) PERFORM A UNTIL С. Оба этих цикла неэффективны при реализации на ЭВМ с вир- туальной памятью, если тело цикла и управление циклом разме- щены на разных страницах. Если об этом известно, можно вставить дополнительный параграф: PERFORM WHILE-C-DO-A. (Пример 7.11) WHILE-C-DO-A. PERFORM A UNTIL NOT С. совмещающий управление циклом и тело цикла. В Коболе отсутствуют операторные скобки и разделители для групп вложенных операторов, поэтому невозможно реализовать 16-399
230 Глава 7 Рис. 7.2. Вложенные структуры. а — вложенные операторы IF; б — вложен- ные операторы обработки исключительных состояний. вложенные структуры выбора (типа . приведенных на рис. 7.2) без модификации. Схема на рис. 7.2,а может быть реа- лизована, если использовать вложенные структуры выбора вида IFC1 (Пример 7.12) THEN PERFORM TEST-C2 АЗ ELSE А4 или если несколько раз по- вторить блок АЗ, IFC1 (Пример 7.13) THEN IFC2 THEN Al, АЗ ELSE А2, АЗ ELSE А4. Средства, предусмотренные в Коболе для обработки исклю- чительных состояний с по- мощью условных выражений, делают невозможным исполь- зование составных операторов для непосредственной реали- зации структур типа приведен- ной на рис. 7,2,6, поскольку один из операторов (но не по- следний) содержит переход при появлении исключитель- ной ситуации. Изменение по- рядка следования операторов иногда позволяет устранить это затруднение. В противном случае вводится флаг и проверяется его значение: IF NOT EOF 4 (Пример 7.14] READ CARD-FILE AT END DISPLAY "DONE" MOVE "YES" TO EOF-FLAG. IF NOT EOF WRITE PRINT-LINE FROM CARD RECORD.
Реализация программного модуля 231 Необходимо проявлять осторожность при использовании со- ставных условных выражений для управления циклом или для выбора альтернатив. Некоторые реализации языка при обработ- ке выражения if Cl and С2 не проверяют значение С2, если зна- чение С1 окажется ложным. В других реализациях значение С2 всегда проверяется. Подобным образом в ряде систем при обра- ботке условия if Cl or С2 проверка заканчивается, если первое условие истинно. В некоторых языках не определен даже точ- ный порядок вычисления булевых выражений. Когда важен по- рядок вычислений или нежелательно проводить проверку всех условий, не следует применять составные условные выражения. Аналогично, если условные выражения слишком сложны, их надо записать в более понятной форме. Так, например, услов- ный оператор if Cl and С2, then Р эквивалентен if Cl (Пример 7.15) then if C2 then P а оператор if Cl or C2, then P эквивалентен if Cl (Пример 7.16) thenP else if C2 thenP. Если язык допускает использование имен булевых перемен- ных вместо условных выражений, это позволяет часто значи- тельно лучше определить смысл условия. Такой условный опера- тор, как IF END-OF-FILE, лучше, чем IF EOF-FLAG=«Y», а оператор IF (DIGIT (CHAR)) лучше, чем IF (CHAR.GE.«0>.AND.CHAR.LE.<9>). v старайтесь, чтобы смысл условии был понятен В гл. 6 речь шла о преобразовании таблиц решений в де- ревья. Если имеются таблицы решений со ссылками к самим себе, возможно, желательно преобразовать их сначала в стан- дартные циклические конструкции. Отметим, что таблицы реше- Таблица 7.1. Таблица решений а) Итеративная б) Рекурсивная Таблица 1 Закрыто Таблица 2 Закрыто Ct Да Да Нет Нет С1 Да Да Нет Нет с2 Да Нет Да Нет С2 Да Нет Да Нет At Az Обратиться к таблице 1 Выход X X XXX X At Az Обратиться к таблице 2 Выход X X XXX X 16*
232 Глава 7 ний предполагают всегда сначала принятие решения и только потом выполнение действия. Поэтому табл. 7.1, а, которая пре- дусматривает повторение действий до тех пор, пока не будут удовлетворены условия выхода, имеет форму конструкции while do (рис. 7.3,а). Таблица решений 7.1,6 вызывает саму себя, а не возвращается к начальной точке. Поэтому она имеет форму рекурсивной подпрограммы (рис. 7.3,6). В случае отсутствия Рис. 7.3. Циклические конструкции для табл. 7.1. а — итеративная; б — рекурсивная. передаваемых параметров и возвращаемых значений эта таб- лица может быть преобразована непосредственно в итератив- ную форму. 7.4.2. Устранение рекурсии Если язык не поддерживает рекурсию, необходимо преобра- зовать рекурсивные алгоритмы в итеративные. Некоторые ал- горитмы гораздо проще могут быть описаны рекурсивно, даже если они должны быть реализованы итеративно. Метод преоб- разования зависит от того, выполняются ли вычисления во вре- мя рекурсивного спуска или возврата, используется ли одно- кратная или многократная рекурсия и являются ли функции, предназначенные для вычисления параметров, обратимыми. Рекурсивные определения данных можно классифицировать в зависимости от степени сложности рекурсивной части опи- сания. Однократной рекурсией является рекурсия не более чем с од- ним рекурсивным вызовом в каком-либо одном выражении. Она
Реализация программного модуля 233 имеет вид Iе (/(&(*))) для ~р(х). Два примера однократной рекурсии приведены ниже: GCD (п, ш) = ( “ для п = 0, (Пример 7 17) [ GCD (m, гет (п, т)) для и > 0. factorial (и) = Р ДЛЯ П~ (Пример 7.18) (n* factorial (и—1) для п>0. В примере 7.17 вычисления выполняются во время рекурсивно- го спуска, так как функция вычисления остатка вызывается пе- ред рекурсивным вызовом функции GCD. В примере 7.18 вычис- ления выполняются во время рекурсивного возврата1). Рекур- сивные программы могут,, конечно, содержать вычисления, вы- полняемые в обоих направлениях. Итеративная форма примера 7.17 имеет вид procedure GCD(n,m) (Пример 7.19) while n “1= 0 do k := rem(m,n); m := n; n := k end; return(m) end. и заменяет проверку окончания и рекурсию на цикл while do, в теле которого производится модификация параметров. Если вычисления выполняются во время рекурсивного возврата, ре- курсию можно заменить простым итеративным циклом лишь тогда, когда изменения параметров являются обратимыми: procedure factorial(п); (Пример 7.20) k :=0 fact := 1; while k < n do k := k + 1; fact: = n * fact end; return (fact) end. ° В отечественной литературе термины «рекурсивный спуск» и «рекур- сивный возврат» не используются. — Прим, перев.
234 Глава 7 Здесь k играет роль рекурсивного параметра, возрастающего от О до некоторого значения п. Если вычисления выполняются во время рекурсивного спуска или возврата, итеративная форма содержит два цикла. Если из- менения параметров не являются обратимыми, как, например, в процедуре печати связанного списка в направлении снизу -вверх procedure print_list(link); (Пример 7.21) if link = null return else call print—list(next-link); print(info); end. в итеративном варианте процедуры для построения стека пара- метров и очистки его используются два цикла: procedure print-list(link); (Пример 7.22) while link "1= null do push (info,stack); link := next-link end; white “1 stack-empty do info := pop(stack); print(info) end end. Многократной рекурсией называется рекурсия с несколькими рекурсивными вызовами, например ( "found" search (tree) = \ search (left ^subtree) \ search (right-subtree) (Пример 7.23)' for node.key = item for node.key / item for node.key item Приведенный выше алгоритм осуществляет поиск по неот- сортированному двоичному дереву заданного символа. Если од- нократную рекурсию можно считать спуском по связанному списку функциональных активаций с последующим возвратом, многократную рекурсию можно считать анализом связанного дерева активаций. Преобразование любой многократной рекур- сии в итеративную форму требует реализации поиска по дереву.
Реализация программного модуля 235 Стек требуется не только для локальных переменных и парамет- ров, но должен показывать также направление, выбранное для каждого ветвления, чтобы при возврате можно было выбрать другое направление. Для поиска в лабиринте или для другой задачи, где дерево поиска имеет степень ветвления больше двух, стек должен содержать информацию о всех предыдущих путях для каждой вершины. 7.4.3. Программирование без оператора GO ТО Структурное программирование часто рассматривается как программирование без операторов GO ТО. Очевидно, что такое программирование невозможно на неструктурированных языках. Тем не менее даже в Фортране можно ограничить применение операторов GO ТО при реализации базовых программных струк- тур. На Коболе, в языке ПЛ/1 и в других более структуриро- ванных языках можно писать программы, не содержащие опера- торов GOTO. Уменьшение числа операторов GOTO улучшает программу, однако их полное устранение может привести к то- му, что из-за введения дополнительных флагов программа ста- новится сложнее для восприятия. Наибольшая трудность при исключении операторов GO ТО возникает при обработке исключительных ситуаций и реализа- ции нескольких выходов из цикла. Способы обработки таких ситуаций определяются в зависимости от применяемого языка программирования. В случае отсутствия оператора EXIT и не- обходимости устранения операторов GOTO в теле цикла долж- ны быть установлены и проверены флаги. Фрагмент программы на языке Кобол, приведенный в примере 7.24, содержит не- сколько выходов, проверку правильности данных и обработку исключительной ситуации; все эти действия требуют установки флагов: (Пример 7.24) PERFORM PROCESS-CARDS THRU PROCESS-CARDS-END UNTIL EOF. PROCESS-CARDS. READ CARD-FILE AT END MOVE "YES" TO EOF-FLAG GO TO PROCESS-CARDS-END. IF CARD-VALUE IS NOT NUMERIC GO TO PROCESS-CARDS-END.
236 Глава 7 ADD CARD-VALUE ТО TOTAL ON SIZE ERROR WRITE ERROR-LINE GO TO PROCESS-CARDS-END. PERFORM WRITE-DETAIL-LINE. PROCESS-CARDS-END. EXIT. Использование специального оператора чтения позволяет уда- лить один из операторов GO ТО. Другие операторы GO ТО можно устранить, используя флаги и дополнительные параграфы и изменяя условия в операторе IF. Если изменить структуру параграфа, используя метод анализа исток — преобразова- тель— сток (И—П—С), можно получить PERFORM PROCESS-CARDS UNTIL EOF. (Пример 7.25) PROCESS-CARDS. - MOVE "GOOD" TO ANSWER-STATUS. MOVE "GOOD" TODATA-STATUS. PERFORM GET-DATA UNTIL VALID-DATA OR EOF. IF VALID-DATA ADD CARD-VALUE TO TOTAL ON SIZE ERROR MOVE "BAD" TO ANSWER-STATUS WRITE ERROR-LINE. IF VALID-ANSWERS PERFORM WRITE-DETAIL-LINE. GET-DATA. READ CARD-FILE AT END MOVE "YES" TO EOF-FLAG. IF NOT EOF IF CARD-VALUE IS NOT NUMERIC MOVE "BAD" TO DATA-STATUS. Выбор между двумя рассмотренными реализациями зависит от стиля программирования и привычки. Самый простой стиль яв- ляется безусловно наилучшим. Если операторы GO ТО приме- няются в таких языках, как Кобол, их использование следует ограничить передачей управления на небольшие расстояния в прямом направлении к концу параграфа или секции. ПРИМЕНЯЙТЕ КАК МОЖНО МЕНЬШЕ ОПЕРАТОРОВ GO ТО
Реализация программного модуля 237 » 7.4.4. Вызовы и возвраты Небрежное использование вызовов и возвратов модулей мо- жет привести к определенным трудностям. Если подпрограммы компилируются раздельно, при выполнении вызова обычно не предусматривается автоматическая проверка атрибутов. Невы- явленные ошибки могут возникнуть при задании неверного чис- ла параметров, несоответствии типов или использовании пара- метров для вывода. Параметры могут передаваться по имени, значению, ссылке или значению-результату. Если используется вызов по имени, значения аргументов не анализируются до тех пор, пока не бу- дут использованы в вызванной подпрограмме. Такой вызов не- эффективен для передачи выражений. Если используется вызов по значению, передается только значение каждого аргумента. Такой вызов не позволяет использовать параметры для присваи- вания им значений, защищая, таким образом, аргументы от не- умышленного изменения. Этот вызов неэффективен для переда- чи структур данных, так как последние должны копироваться. При использовании вызова по ссылке передается адрес области памяти каждого аргумента. При вызове по значению-результату значение аргумента передается на вход подпрограммы и возвра- щается на ее выход (возможно, в модифицированной форме). Если рассматривать действительное значение как целое в Фортране или арифметическое значение как данное для вывода в Коболе, могут, очевидно, возникнуть проблемы, поскольку внутреннее представление данных будет отличаться от ожидае- мого. Трудности возникают также из-за несоответствия других атрибутов. Структуру массива нельзя скрыть в некоторых реа- лизациях Фортрана с помощью передачи структуры через про- межуточные модули как скаляра. Скаляры и массивы могут обрабатываться по-разному, причем скаляры передаются как значение-результат, а массивы — с помощью ссылки. При ис- пользовании стандартного компилятора ПЛ/1 константа может быть защищена от непредусмотренного изменения с помощью передачи ее в качестве выражения с дополнительной парой ско- бок. CALLP(3) является вызовом по ссылке, в то время как CALL Р ((3)) — вызовом по значению. В действительности оба вызова являются вызовами по ссылке, однако в случае с допол- нительными скобками создается фиктивная область памяти для размещения значения. Такой прием непригоден при использова- нии оптимизирующего компилятора ПЛ/1. БУДЬТЕ ВНИМАТЕЛЬНЫ ПРИ ПЕРЕДАЧЕ ПАРАМЕТРОВ При описании интерфейсов модулей необходимо указывать, какие параметры используются в качестве входных, выходных и одновременно входных и выходных. Это реализуется при про-
238 Глава 7 граммировании на, языке Ада. Например, программист может записать SUB (X in; Y out; Z in out). В языке Паскаль вызовы по значению тех переменных, которые используются лишь в ка- честве входных [например, SUB (X: integer)], отличаются от вызовов по имени тех переменных, которые используются в ка- честве входных и выходных [например, SUB (varX: integer)]. Неправильное применение аргументов, например обработка кон- станты или выражения как выходного параметра, обычно не об- наруживается компилятором. Наилучший способ избежать таких ошибок заключается в выборе другого имени для каждого значе- ния, если язык не поддерживает вызов параметров по значению. Независимо от используемого способа передачи параметров возникают проблемы с неоднозначностью имен, которая появля- ется, когда доступ к значению некоторой переменной возможен по двум именам и когда переменная используется в качестве двух аргументов или является аргументом и глобальной пере- менной одновременно. В каждом из приведенных ниже приме- ров имена А и X принадлежат одной и той же переменной: COMMON X, Y, Z (Пример 7.26) CALL S (X) SUBROUTINE S (А) COMMON X,Y,Z CALL SUB (X, X) (Пример 7.27) SUBROUTINE SUB (A, X) varX:real; (Пример 7.28) procedure P (A: real); P (X)‘ Если какое-либо одно из двух имен, относящихся к одной и той же переменной, изменяется, воздействие этого изменения на другое имя зависит от реализации и может привести к не- предвиденным результатам. НЕ ИСПОЛЬЗУЙТЕ НЕСКОЛЬКИХ ИМЕН ДЛЯ ОДНОЙ ПЕРЕМЕННОЙ Ссылки на глобальные переменные являются возможным ис- точником ошибок даже тогда, когда неоднозначность имен уст- ранена. Использование таких ссылок приводит к появлению связи между подпрограммами через общие области. СТАРАЙТЕСЬ НЕ ПРИМЕНЯТЬ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ Функция, которая изменяет какие-либо глобальные перемен- ные, параметры или выполняет операции ввода-вывода, называ- ется нестандартной. Стандартная функция выполняет только
_______________Реализация программного модуля_________239 одну вещь: возвращает значение в точку вызова. Поскольку вызов функции может содержаться в некотором выражении, где значение этой функции используется, может возникнуть недора- зумение, когда пользователь обнаружит еще какие-то измене- ния, а не только возврат значения в точку вызова. Поэтому не следует применять нестандартные функции, хотя иногда их мож- но использовать для печати сообщения об ошибке или для воз- врата флага ошибки. Если модуль выполняет ввод-вывод, либо обращаясь к внешним устройствам, либо с помощью парамет- ров, его следует вызывать как подпрограмму, а не как функцию. старайтесь не применять нестандартные функции Существуют два типа параметров, которые следует использо- вать во всех вызовах в любых программных системах: флаги ошибок и флаги отладки. Флаги ошибок являются выходными параметрами, используемыми для сигнализации об ошибках. Поскольку ошибки обычно обрабатываются на самом низком уровне, можно избежать возвращения кодов ошибок. Простого булева флага достаточно для сигнализации о появлении ошиб- ки, приводящей к прерыванию работы программы. Для получе- ния большей информации можно применять булевы флаги с та- кими мнемоническими именами, как EOF, INVALID и NULL. Они гораздо проще в работе, чем коды ошибок. Эти флаги так- же обеспечивают большую независимость модуля. Флаги отладки являются входными переменными, которые используются для получения различных дампов памяти и про- ведения трассировки. Они применяются в процессе отладки всей системы и устанавливаются с помощью тестовых данных или управляющих параметров. В частности, полезно завести флаг отладки, с помощью которого можно управлять печатью имен подпрограмм и значений параметров на входе и выходе моду- лей. Но это необходимо только тогда, когда не существует си- стемной подпрограммы, выполняющей указанные выше дей- ствия. ПЛАНИРУЙТЕ ПРОЦЕСС ОТЛАДКИ Некоторые языки программирования допускают нестандарт- ные возвраты в точки, отличные от точки вызова. В языках блочного типа это соответствует завершению выполнения про- цедуры с помощью оператора GO ТО, передающего управление вне тела процедуры. Нестандартный возврат противоречит прин- ципу модульного проектирования, предусматривающему наличие только одного входа и одного выхода для каждого модуля. Та- кие программы сложнее для понимания, отладки и не удобны Для формальной верификации. t
240 Глава 7 7.5. Сложность программы Одно из основных правил программирования — соблюдение простоты. Более простой считается та программа, которая легче для понимания, отладки, сопровождения и модификации. Одна- ко не существует общепринятого мнения о том, чтб делает про- грамму простой. Проще та программа, которая использует при- вычную систему обозначений. Программа с небольшим числом уровней вложенности проще, чем программа, в которой таких уровней много. Вероятно, проще и та программа, которая тща- тельно спланирована. 7.5.1. Сложность исходного текста и длина модуля Обычно длина модуля измеряется числом строк исходного текста. При таком критерии логично считать модуль, состоящий из 50 строк, более простым, чем модуль из 150 строк. Любой модуль, длиннее 150 строк, вероятно, слишком сложен, и его необходимо заново спроектировать. Если модуль имеет слишком большую длину, могут возникнуть трудности при его компиля- ции и сегментации. Использование длины модуля в качестве меры его сложности подтверждает идею о том, что программа, написанная на языке высокого уровня, проще той же програм- мы, написанной на языке низкого уровня. Длину модуля не всегда можно брать в качестве меры сложности: например, при использовании составных операторов, при структурировании исходного текста и при красивом оформлении листинга длина модуля увеличивается, а сложность программы снижается. ЧЕМ КОРОЧЕ ПРОГРАММА, ТЕМ ЛУЧШЕ Сложность текста часто определяется также числом управ- ляющих связей между секциями. На рис. 7.4 показаны управ- ляющие линии для двух версий одного и того же модуля, при- веденных в примерах 7.24 и 7.25. Сложность каждого равна 4. Преобразование программы из примера 7.24 увеличивает ее дли- ну, но уменьшает число внутренних секций, несмотря на то что используется отдельный выполняемый параграф чтения. В этом случае сложность текста, оцениваемая с помощью двух введен- ных выше критериев, увеличивается. Простой мерой сложности модуля, не зависящей от структу- рирования и формы распечатки, является циклом этическое чис- ло модуля. Предположим, что задан управляющий граф модуля. Тогда цикломатическое число модуля равно числу ребер минус число вершин плюс два, или, проще, на единицу больше числа точек ветвлений в модуле при условии, что все точки ветвлений имеют две выходные ветви. Пользуясь этой мерой, находим, что программы в примерах 7.24 и 7.25 имеют относительную слож- ность, равную 5 и 8 соответственно. A PERFORM... UNTIL счи- тается точкой ветвления. Аналогично точкам ветвления счита-
Реализация программного модуля 241 ___ ~ PERFORM • • • PROCESS-CARDS* — READ AT END ( MOVE -------GO TO IF ----* GOTO ADD ON SIZE ERROR WRITE --- GO TO PERFORM ----PROCESS-CARDS-END. ---- ------- EXIT. ___PERFORM • • • PROCESS-CARDS. MOVE PERFORM IF ADD ON SIZE ERROR MOVE WRITE IF PERFORM >- GET-DATA. READ AT END a Рис. 7.4. Управляющие связи для подпрограмм на Коболе. а — с оператора GO ТО; б — без операторов GO ТО. ются READ... AT END, ON SIZE ERROR и, конечно, IF. Опера- тор PERFORM не содержит ветвления, поскольку после него всегда выполняется следующий оператор. 7.5.2. Меры Холстеда Вместо измерения длины программы с помощью строк исход- ного текста Холстед предложил измерять длины числом симво- лов в модуле. Если модуль на языке Фортран вычисляет синус
242 Глава 7 числа, текст с использованием библиотечной функции может быть записан как Y=SIN(X), или, если значение X мало, его синус может быть вычислен как Y=X—Х**3/6+Х**5/120— —Х**7/5040. Каждый такой оператор может быть записан в одну строку. Вызов библиотечной функции проще для понима- ния, чем приближенные вычисления, поскольку для вызова функции используется стандартное обозначение. Кроме того, в вызове функции содержится меньше различных символов, чем в экспоненте, и вызов короче. При вызове функции используют- ся пять различных символов: Y, +, SIN, (...), X, а при использо- вании выражения — тринадцать: Y, =, X, —,**, /, +, 3, 6, 5, 120, 70, 5040. Общее число различных знаков равно 21. Холстед ввел следующие меры: т)1 число различных операторов, "На число различных операндов, -П = т]1+т)2 словарь модуля, Ni число появлений операторов, N% число появлений операндов, длина модуля. Длина модуля по Холстеду может быть вычислена по прибли- женной формуле V « П1 logi (П1)+П2 log, (Пг)- В программе любого размера tji примерно равно числу операто- ров языка. Следовательно, длина модуля зависит от количества выбранных имен и частоты их использования. Поскольку про- граммист может повторно использовать некоторые имена и ме- нять формы управления циклом, длина модуля, по Холстеду, однозначно не определяется числом операторов, составляющих модуль. Объем модуля определяется выражением V=Ariog2Ti. Минимально возможный объем модуля определяется объемом обращения к библиотечной функции, выполняющей то же, что и модуль, если предположить, что такая функция существует. Минимальная длина модуля на три единицы больше, чем удво- енное число параметров для функции, и на единицу больше удвоенного числа параметров для подпрограммы. Для функции, вычисляющей синус, объем равен V* = 51og2(5) = 11,6 для вызова функции; V = 21 log2(13) = 77,7 для приближенных вычислений. Уровень записи модуля определяется следующим образом: Laa_12 . у » где V* — минимальный объем. Поскольку объем модуля не
Реализация программного модуля 243 меньше V*, уровень записи модуля не может быть больше 1. Уровень записи модуля можно приближенно определить по фор- муле Удивительно то, что величина %=L2V оказывается практически постоянной для всех модулей, написанных на одном и том же языке программирования. Экспериментальные значения величи- ям % приведены в табл. 7.2. Когда модуль спроектирован и вы- Таблица 7.2. Уровни языков (по Холстеду) Язык X Отклонение ПЛ/1 1,53 0,96 Алгол 58 1,21 0,86 Фортран 1,14 0,90 AL(CDC) 0,88 0,65 бран язык реализации, с помощью этих характеристик и вели- чины минимального объема модуля можно вычислить длину ис- ходного текста. Сложность модуля определяется по формуле Величина Е соответствует усилию, затраченному на кодирова- ние и понимание модуля. Опираясь на результаты психологов, Холстед пытается прогнозировать время и усилия, которые по- надобятся для кодирования модуля, а также число ошибок, ожидаемых в конечной версии. В табл. 7.3 приведены значения Таблица 7.3. Меры Холстеда для программных сегментов из примеров 7.24 н 7.25 С операторами GO ТО Без операторов GO ТО 17 15 Л2 10 17 л 26 32 Ni 28 41 N2 17 25 N 44 66 V 206,8 330 L 0,0692 0,0907 Е 2988,3 3638,4 к 0,990 2,715
244 Глава 7 мер Холстеда для программных сегментов из примеров 7.24 и 7.25. Выбор операторов и операндов произволен. Имена данных» имена параграфов и атрибуты считаются операндами. Все ос- тальные символы считаются операторами, за исключением сим- волов, всегда появляющихся вместе (таких, как GO ТО и «...») и считающихся одиночными символами. Значения мер Холстеда указывают на то, что модульная версия программы может счи- таться записанной на языке более высокого уровня, чем немо- дульная версия, но в то же время она может считаться более сложной и трудоемкой. Таблица 7.4. Показатели относительной сложности программы С операторами GO ТО Без операторов GO ТО Строки исходного текста Управляющие связи между 15 18 секциями 4 4 Цикломатическое число 5 8 Мера Е по Холстеду 2988,3 3584,6 Значения четырех мер сложности для модулей из примеров 7.24 и 7.25 приведены в табл. 7.4, из которой видно, что ограни- ченное использование операторов GO ТО в Коболе может при- вести к более простой программе, чем полный отказ от них. 7.6. Оформление программы Программу можно сделать простой, если ее тщательно спро- ектировать, закодировать в соответствии с требованиями струк- турного программирования, представить в удобной для понима- ния форме и снабдить документацией в необходимом объеме. Модуль должен быть по возможности самодокументирован. Внутри текста документация должна использоваться как можно меньше и записываться так, чтобы не отвлекать от самого тек- ста. Она должна дополнять текст, а не прилагаться к нему. Кро- ме того, документирование должно проводиться аккуратно. ДОКУМЕНТАЦИЯ ДОЛЖНА ОТВЕЧАТЬ СОВРЕМЕННЫМ ТРЕБОВАНИЯМ 7.6.1. Пролог Кодирование модуля должно начинаться с общего описания. Напомним, что аналогичная процедура проводится на этапе про- ектирования модуля. Это описание должно содержать: а) номер и имя модуля;
Реализация программного модуля 245- б) фамилию программиста, дату завершения работы над мо- дулем и номера всех версий данного модуля; в) функциональное описание модуля; г) перечень основных используемых алгоритмов со ссыл- ками; д) словарь данных для параметров; е) имена подпрограмм, вызывающих модуль; ж) имена подпрограмм, вызываемых модулем; з) словарь данных для разделяемых областей хранения дан- ных; и) словарь данных для внутренних данных; к) описание ввода-вывода; л) описание процесса обработки ошибок, выполняемого мо- дулем. Пролог следует включать в исходный текст модуля в виде комментария для того, чтобы его можно было получить при каждой распечатке. Все подпрограммы работают с различными типами данных. Эти данные могут быть простыми переменными различных ти- пов, структурами данных, вспомогательными константами и. константами, являющимися параметрами программы. Все пере- менные и структуры данных должны быть отнесены к опреде- ленному типу, даже если язык не требует этого. Имена данных должны быть мнемоническими, т. е. отражать сущность описываемого процесса. Не следует использовать: слова, в которых обычно делаются орфографические ошибки; имена, различающиеся только одной буквой; слова, имеющие более одного очевидного сокращения, а также слова, являющие- ся ключевыми в языках программирования. Операторы, служащие для объявления переменных, должны быть сгруппированы и структурированы, во-первых, в соответст- вии с типами переменных и, во-вторых, в соответствии с их функциями в программе. В Коболе все счетчики, индексные пе- ременные, временные значения и т. п. следует отнести к опреде- ленной категории и сгруппировать. В языке, не имеющем фор- мальных средств группирования данных, тот же эффект можно получить за счет использования пустых строк и комментариев. Если при объявлении используются коды, их следует расшифро- вать в прологе или же предусмотреть комментарии, расположен- ные рядом с объявлениями: type transaction_code= («D> {занести}, (Пример 7.29> «W> {удалить}, «S> {откорректировать}) Если имена условий улучшают восприятие исходного текста, их: следует объявлять явно. 17—399
246 Глава 7 7.6.2. Правила оформления листингов Исходный текст воспринимается лучше, если он удачно рас- положен на странице, выделены поля, строки не перегружены информацией, использованы отступы и пробелы. используйте пробелы В исходном тексте часто имеются базовые структуры — сле- дование, выбор и повторение. Для документирования этих струк- тур в большинстве случаев достаточно правильно их располо-. жить на листинге. Пустые строки могут указывать на окончание группы последовательно выполняющихся операторов. Абзацный -отступ используется для выделения подструктур. До тех пор пока в языках программирования отсутствуют автоматические средства оформления текста, для получения листинга, удобного для восприятия, можно рекомендовать следующие правила: а) оставлять между основными частями подпрограммы три пустые строки, между неосновными частями подпрограммы — две пустые строки и после каждой последовательно выполняю- щейся группы операторов — одну пустую строку; б) использовать базовые управляющие структуры; в) соблюдать абзацный отступ в два — пять пробелов для тел циклов, подчиненных операторов и продолжений опера- торов; г) абзацный отступ для продолжений операторов должен быть больше, чем для подструктур; д) выравнивать альтернативные ветви операторов; е) использовать закрывающую скобку, если структура имеет открывающую скобку; ж) нумеровать строки с шагом, равным 10. 7.6.3. Комментарии Кроме пролога и комментариев, относящихся к данным, тре- буется определенное количество внутренней документации мо- дуля, которое зависит от используемого языка. При программи- ровании на языке низкого уровня довольно часто необходимо снабжать комментариями каждую строку исходного текста, а также давать дополнительные комментарии отдельным частям модуля. В Фортране циклы должны быть всегда документиро- ваны, чтобы можно было определить их назначение и условия завершения. Такие базовые структуры, как выбор и следование, часто не требуют никаких комментариев. Исключительные си- туации обычно обрабатываются отдельно и поэтому требуют предварительных комментариев. Существует хорошее правило, заключающееся в том, что каждой пронумерованной строке должен предшествовать комментарий.
Реализация программного модуля 247 Если удачно выбраны имена данных и параграфов, то для программ, написанных на Коболе, требуется небольшая доку- ментация. Объем внутренней документации модуля зависит от того, как организован и записан программный модуль. В Паска- ле и ПЛ/1 каждая внутренняя процедура, являющаяся отдель- ным модулем, должна быть документирована. Секции исходного- текста, содержащие однотипные операторы, отделяются друг от друга и имеют имена, например ***** ОПИСАНИЯ »»»*** (Пример 7.30> «•»*» ПРОЦЕДУРЫ »»»«♦ • • ОБРАБОТКА ОШИБОК * Любые дальнейшие пояснения, касающиеся этих или других секций текста, группируются и размещаются таким образом» чтобы можно было читать комментарии без текста, а текст беэ комментариев. В Фортране модуль для вычисления наиболь- шего общего делителя двух целых чисел может быть документи- рован и оформлен следующим образом: (Пример 7.31) £ Ж»*»»**** ******** О********************** ***************** ********** С * * С * GCD * С * * С* АВТОР. К. ЗИГЛЕР * С * * С * ДАТА: 6/26/82 * С * ФУНКЦИЯЮПРЕДЕЛЕНИЕ НАИБОЛЬШОГООБЩЕГО ДЕЛИТЕЛЯ* С * ДВУХ ЧИСЕЛ С * С * ПАРАМЕТРЫ * С* I ПЕРВОЕ ЧИСЛО * С* J ВТОРОЕ ЧИСЛО * * С * С* ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ * С* М БОЛЬШЕЕ ИЗ ЧИСЕЛ * С* N МЕНЬШЕЕ ИЗ ЧИСЕЛ * С * * С* ВЫЗЫВАЕМЫЕ ПОДПРОГРАММЫ : IREM, IABS * С * * С FUNCTION GCD(I.J) INTEGER l,J,GCD С С ЦЕЛЫЕ K,L,M,N С N = IABS(I) М = IABS(J) q «»|»*<«*Н*«»***»»*«м*Н»»*»«««*»*»*м С * М = БОЛЬШЕМУ ИЗ ЧИСЕЛ * С * N = МЕНЬШЕМУ ИЗ ЧИСЕЛ * г «#»#-* ****««»#*#«*««**#«#«******««•»**** 17»
248 Глава 7 IF(N .GT. М) GOTO 5 GO TO 10 С ТОГДА ИЗМЕНИТЬN И M 5 К = N N = M M = К С........... ****** *** ***** «0.0. »»тНОО.О«т С . WHILE N>0 DO * ПОВТОРЯТЬ ДО ТЕХ ПОР ПОНА С . * ОСТАТОК НЕ БУДЕТ=О * , 10 1F(N EQ. 0) GO ТО 50 L = MOD(M,N) M = N N = L GOTO 10 C . C . c........... 50 GCD = M RETURN END * ПОСЛЕДНЕЕ HE РАВНОЕ НУЛЮ * * ЧИСЛО ЕСТЬ GCD Объявления, комментарии, относящиеся к данным, и другие комментарии вместе с прологом составляют документацию ис- ходного текста. 7.7. Вспомогательные средства, используемые при реализации Выше уже говорилось об организации бригады программи- стов и о взаимодействии программистов, работающих над моду- лями одной программной системы. На всех стадиях проектиро- вания и реализации каждый член бригады может рассчитывать на помощь своих коллег. На всех уровнях должны быть органи- зованы сквозные просмотры результатов работы, завершающие- ся просмотром текста готового модуля. Наилучший способ об- наружить ошибки в своей программе — объяснить работу про- граммы кому-нибудь другому. ОДНА ГОЛОВА ХОРОШО, А ДВЕ ЛУЧШЕ 7.7.1. Машинные средства Существует много машинных вспомогательных средств, •встроенных в системное математическое обеспечение. Следует максимально использовать возможности, предоставляемые сред- ствами трассировки и отладки, схемами загрузки и таблица- ми перекрестных ссылок. Программист может использовать собственные средства трассировки и дампы участков памяти. Дампы всей памяти должны использоваться только в крайнем случае. Если предусмотрен вспомогательный модуль, позволяю- щий получать отладочные дампы, ему передается идентифика- ционный код для определения этапа выполнения программы в момент выдачи каждого дампа.
Реализация программного модуля 249 Первое зондирование Венеры было неудачным, так как про- граммист сделал ошибку: вместо, оператора DO 3 1 = 1,3 он на- писал DO 3 1 = 1.3. Эта ошибка могла быть выявлена с помо- щью просмотра таблицы перекрестных ссылок или словаря пе- ременных. Поэтому следует использовать все средства контро- ля ошибок и максимальное количество сообщений компилятора. Любой оператор, вызвавший выдачу предупреждающего илика- 1 FUNCTION GCD (I, J) 2 INTEGER I, J, GCD 3 INTEGER K, L, M, N 4 N = IABS(I) 5 M = IABS (J) 6 IF (N .GT M) GO TO 5 7 GO TO 10 8 5 K= N 9 N = M 10 M= К 11 10 IF (N EQ 0) GO TO 50 12 L = IREM (M, N) 13 M = N 14 N = L 15 GO TO 10 16 50 GCD - M 17 RETURN 18 END a GCD 1, 2, 16 I 1, 2, 4 J 1. 2, 5 К 3, 8, 10 L 3, 12, 14 M 3, 5, 6, 9, 10, 12, 13, 18 N 3,-4, 6, 8, 9, 12, 13, 14 6 Рис. 7.5. Вспомогательные средства реализации. « — подпрограмма — функция, написанная на Фортране; лок; в — управляющий граф. б «— таблица перекрестных ссы- кого-либо другого сообщения, должен проверяться особенно тщательно. Схемы загрузки и таблицы перекрестных ссылок можно использовать для проверки правильности имен модулей и переменных, а также для поиска необъявленных имен и пере- менных, которым не присвоено начальное значение. На рис. 7.5 приведена подпрограмма из примера 7.31 с про- нумерованными компилятором операторами, таблица перекрест- ных ссылок и управляющий граф. На этом рисунке специально опущены комментарии и листинг оформлен таким образом, что- бы подчеркнуть структуру, описываемую с помощью управляю- щего графа, а не структуру, подразумеваемую программистом. Строки 6—10 можно было бы удалить из алгоритма без ущерба для выполнения функции, однако это затруднило бы понимание программы.
250 Глава 7 Каждая переменная в таблице перекрестных ссылок иденти- фицируется в соответствии с объявлениями во второй или третьей строке. Необъявленные переменные отсутствуют. Если программа имеет правильную структуру, в первом операторе (не считая объявления), в котором появляется необъявленная переменная, последней должно быть присвоено значение (ис- ключение может быть сделано только для входных параметров). Так, переменным I и J значения присваиваются в строке 1, N — в строке 4, М — в строке 5, К — в строке 8 и L — в строке 12. Функции GCD значение присваивается в строке 16, где находит- ся первая ссылка к имени GCD, отличающаяся от ссылок в строках 1 и 2, где данная переменная объявлена. Управляющий граф, приведенный на рис. 7.5, в, содержит но- мера операторов двух типов: во-первых, операторов, которые снабжены метками (и поэтому ссылка на них может осуществ- ляться с помощью управляющих операторов), во-вторых, опе- раторов IF. Если существуют DO-циклы, номера содержащих их операторов следует также включать в граф. Управляющий граф вместе с таблицей перекрестных ссылок можно использо- вать для контроля области возможных значений переменных и проверки базовых управляющих структур. Из сопоставления таблицы перекрестных ссылок с управляющим графом видно, что переменной К значение присваивается при попадании на одну из ветвей оператора IF и что ссылка к этой переменной имеется только на этой ветви. Так как областью действия пере- менной К является указанная ветвь и значение переменной присвоено до того, как появилась ссылка на эту ветвь, считает- ся, что переменная К определена правильно. Активная область действия переменной начинается с первой строки, в которой эта переменная получила конкретное значение, и заканчивается вы- ходом из последней управляющей конструкции, в которой есть обращение к этой переменной. Области действия переменных программы, изображенной на рис. 7.5, а, указаны в табл. 7.5. Присваивание значения переменной L и ссылки к ней осуществ- Таблица 7.5. Области действий переменных программы, приведенной на рис. 7.5, а Переменная Область действия I 1-19 J 1—19 К 8—10 L 13-16 М 5—19 N 4—16 GCD 17-19
Реализация программного модуля 251 ляются только внутри цикла, начинающегося с оператора 11. В области действия переменных М и N имеются структуры вы- бора и повторения. Отметим, что операторы внутри областей действия переменных должны составлять правильно вложенные управляющие структуры. Управляющий граф показывает, что функция GCD представляет собой последовательность операто- ров, состоящую из начальной подпоследовательности, конструк- ции выбора, цикла while do и завершающей подпоследователь- ности. a б в Рис. 7.6. Управляющие структуры, которые могут содержать ошибки. Если управляющий граф содержит структуры типа приведен- ных на рис. 7.6, а или б, его необходимо дополнительно прове- рить. На рис. 7.6, а приведен цикл с несколькими выходами. При его реализации возможны ошибки. Обнаружив такую управ- ляющую структуру, программист должен проверить свою рабо- ту, чтобы быть уверенным в правильности выполнения програм- мы. На рис. 7.6,6 приведена структура, соответствующая как оператору IF со многими выходами, так и паре неудачно вло- женных друг в друга операторов IF. Хотя может быть, что имен- но такая структура была задумана программистом, это служит предупреждением о возможности возникновения сложной си- туации. Ветвь от оператора 12 к оператору 17 на рис. 7.6, а и ветвь от оператора 19 к оператору 28 на рис. 7.6,6 будем ин- терпретировать как выходы по ошибке. Тогда управляющий граф будет проще и лучше структурирован, если вместо указан- ных ветвей предусмотреть пути к блокам удачного и неудачного завершения (рис. 7.6,в). 7.8. Упражнения 1. На любом языке, кроме Фортрана и Кобола, привести примеры реали- зации базовых управляющих структур выбора и повторения, а также обра- ботки исключительного состояния. 2. Возьмите неструктурированную программу, структурируйте ее, исполь- зуя базовые управляющие структуры, оформите ее с целью получения удоб- ной распечатки и составьте документацию.
252 Глава 7 3. Предусмотрите проверку правильности данных, представляющих мно- жество измерений даты, времени, скорости и направления ветра. Предполагает- ся, что измерения выполнены с интервалом 1 ч в течение нескольких месяцев. Данные наносятся на перфокарту каждый раз, когда выполняются измерения. Все перфокарты обрабатываются одновременно. 4. Для оперирования со стеком разработайте модуль, позволяющий скрыть истинную структуру данных. Выполните это для двух различных представле- ний стеков. 5. Разработайте модуль для работы с информацией из базы данных о сту- дентах курса, рассмотренной в упражнении 9 гл. 3. Используйте оперативную память и определите методы доступа. 6. Напишите подпрограмму на Фортране или Коболе, предназначенную для обнаружения числа, окруженного пробелами. Число может иметь деся- тичную точку и знак. Если используете Кобол, поместите это число в область вывода, если используете Фортран, значение возвратите в форме действитель- ного числа. 7. Реализуйте таблицы решений, приведенные в упражнении 7 гл. 6, для проверки правильности задания римских цифр и оценки их значений. 8. Используя дополнительную таблицу, приведенную в упражнении 4 гл. 6, реализуйте управляемую таблицей подпрограмму для определения сум- мы двух римских цифр. 9. Решите задачу, возникающую при пакетной обработке записей в упраж- нении 2 гл. 6. Для этого используйте такие методы доступа к данным, которые позволяют сначала отладить подпрограмму, имитируя размещение файлов в оперативной памяти, а затем работать с реальными файлами. 10. Определите с помощью вызовов внешних процедур в каком-нибудь языке программирования (кроме Кобола), как передаются константы, выра- жения, скалярные переменные и массивы. 11. Реализуйте программу получения всех перестановок из п элементов. 12. Напишите программу решения задачи поиска путей для игры в китай- ские шашки, приведенную в упражнении 5 гл. 6. 13. Разработайте итеративный вариант программы для определения зна- чения функции Аккермана (упражнение 3 гл. 5): а) без использования массива; б) с одномерным массивом; в) с двумерным массивом. 14. Разработайте итеративный вариант программы для определения бино- миального коэффициента из примера 5.5: а) без использования массива; б) с одномерным массивом; в) с двумерным массивом. 15. Реализуйте алгоритм на нескольких различных языках программиро- вания и сравните меры Холстеда полученных программ. 16. Приведите несколько вариантов управляющих графов для базовых управляющих структур. 17. Нарисуйте управляющий граф любой работающей (но не простой) программы, найдите в нем базовые управляющие структуры и отклонения от них.
Глава 8 ПРОВЕРКА ПРАВИЛЬНОСТИ ПРОГРАММ Программу нельзя использовать до тех пор, пока не будет уверенности в ее надежности. Надежность — это свойство про- граммы, более строгое, чем корректность, поскольку программа может быть корректной, но не быть надежной. Программа явля- ется корректной, если удовлетворяет внешним спецификациям, т. е. выдает ожидаемые ответы на определенные комбинации значений входных данных. Программа является надежной, если она корректна, приемлемо реагирует на неточные входные дан- ные и удовлетворительно функционирует в необычных условиях. В процессе создания программы программист старается пред- видеть все возможные ситуации и написать программу так, чтобы она реагировала на них вполне удовлетворительно. Этап тестирования является последней попыткой определить надеж- ность и корректность программы. Проверка надежности вклю- чает в себя просмотр проектной документации и текста програм- мы, анализ текста программы, тестирование и, наконец, демон- страцию заказчику того, что программа работает надежно. 8.1. Обнаружение ошибок Ошибки могут появиться на выходе программы по трем при- чинам. Во-первых, могут быть неверно заданы входные данные. Такая ситуация получила название «мусор на входе — мусор на выходе». Во-вторых, могут быть допущены логические ошибки в самой программе. Но даже при условии, что входные данные введены верно и программа написана правильно, на выходе все же могут появиться ошибки в результате не совсем удовлетвори- тельного выбора алгоритма или накапливания незначительных числовых погрешностей. 8.1.1. Проверка правильности данных В гл. 7 шла речь о проверке правильности данных. Было Установлено, что проведение четкой диагностики неправильных Данных — важная функция любой надежной программы. Су- ществуют три принципиально различных типа ошибок ввода: ошибки передачи, ошибки перезаписи и неправильные данные.
254 Глава 8 Ошибки передачи возникают в том случае, когда аппаратные средства ввода искажают данные. Неисправности этих средств могут быть как механическими, так и электрическими. Напри* мер, карта может быть отперфорирована со смещением от цент- ра позиции или устройство считывания с перфокарт может не- правильно считать данные. Также могут быть повреждены уча- сток на ленте записи или электрическая цепь. Ошибки перезаписи, появляются, когда данные неверно ко- пируются еще до ввода в вычислительную систему. Здесь сле- дует выделить стандартные ошибки, вносимые человеком (по- добные ошибкам, возникающим при печатании на машинке}. Наиболее часто встречаются следующие ошибки: неправильное размещение данных, появление лишнего символа, пропуск сим- вола, замена одного символа другим, перестановка двух симво- лов. Если результаты ввода контролируются пользователем, ошибки такого рода могут быть легко обнаружены. Эти ошибки можно обнаружить и программным способом: метод цифрового контроля, заключающийся в проверке как позиций цифр, так и их значений, часто позволяет выявить замену одной цифры дру- гой и перестановку двух цифр. Контроль типа и встроенные методы контроля избыточности дают возможность находить вставки, пропуски и ошибки в размещении данных. Иногда дан- ные можно проверить путем сопоставления с элементами зара- нее сформированного словаря. Программист должен сам принимать меры для защиты от ошибок перезаписи. Аппаратное и программное обеспечение по- зволяет находить лишь ошибки передачи. Если исправление ошибок не производится автоматически, программист должен предусмотреть действия, предотвращающие аварийное оконча- ние задания. Такие действия не входят в круг обязанностей про- граммиста по контролю данных. Вычислительная система долж- на обеспечивать целостность информации, которая уже введена. Часто ошибки в данные вносятся неавторизованными пользо- вателями; ограничить доступ таких пользователей лучше всего с помощью физических средств защиты. В самой вычислитель- ной системе могут вводиться пароли для работы с файлами, а доступ к файлам для большинства пользователей может раз- решаться только для чтения. Более надежно данные можно за- щитить, если сделать файлы доступными только для некоторых программ, терминалов и пользователей, что возможно лишь при наличии специального программного обеспечения. Для защиты от несанкционированного доступа могут применяться методы шифрования данных. Данные, расположенные в оперативной памяти, также долж- ны быть проверены, поскольку ошибки в них могут привести к сбою системы. Так, необходимо следить за индексами перед об- ращением к массивам, контролировать значение индекса перед
________________Проверка правильности программ 255 выполнением множественного ветвления, не имеющего альтер- нативной ветви, проверять знаки чисел до выполнения операции возведения в степень, избегать переполнения или потери значи- мости при вычислении экспонент. 8.1.2. Логические ошибки Процедуры контроля, позволяющие обнаружить логические ошибки, должны быть предусмотрены на всех этапах разработ- ки системы. Проект проверяется путем сопоставления со специ- фикациями, относящимися к логике работы всей системы. Текст программы должен быть проверен членами бригады программи- стов, а не самим разработчиком. На этапах проектирования и реализации, просматривая программные модули, необходимо •следить за значениями наиболее характерных данных. Установ- лено, что 70—80% ошибок может быть обнаружено после тща- тельного просмотра текста за рабочим столом. ПРОСМАТРИВАЙТЕ ТЕКСТЫ ПРОГРАММ Не принимая во внимание орфографические и синтаксиче- ские ошибки, можно сказать, что причинами большинства логи- ческих ошибок являются неправильная инициализация перемен- ных, несвоевременный выход из цикла, ошибки в типах данных, пропуск одного или нескольких условий при организации вет- влений. Управляющие графы и таблицы перекрестных ссылок можно использовать для того, чтобы определить, проведена ли янициализация переменной до ее применения. Пометка ветвей управляющего графа позволяет определить, все ли альтернати- вы учтены. При сравнении значений двух переменных возмож- ны три случая: первая переменная больше второй, равна ей или меньше. Так как конструкция if... then... else учитывает только две возможности, программист должен быть внимателен и сле- дить за тем, чтобы случай равенства обрабатывался правильно. Несвоевременный выход из цикла часто возникает при под- счете записей файла. Ниже приведена программа на псевдоко- де, которая будет правильно вести счет только в том случае, если флаг конца файла устанавливается во время считывания последней записи файла: procedure подсчет числа записей; (Пример 8.1) счетчик: = 0; while not конец файла do чтение записи счетчик: = счетчик + 1 end Если же флаг конца файла устанавливается специальной допол-
256 Глава 8 нительной записью, подсчитанное число записей будет больше реально существующего. Эта проблема становится еще более важной, если записи помещаются непосредственно в массив, по- скольку возможны ошибки в определении границ массива. Про- грамма на псевдокоде procedure подсчет числа записей (Пример 8.2) i := 1; while not конец файла and not i > n do чтение записи (A(i)); i := i + 1 end будет заканчивать свою работу со значением индекса на едини- цу большим, чем число записей в массиве, если флаг конца файла устанавливается последней записью файла. Значение ин- декса будет на два больше,' чем число записей в массиве, если флаг конца файла устанавливается специальной записью. Для правильного определения числа записей файла надо точно знать условие установки флага конца файла, завершающее цикл. точно определяйте число записей в файле 8.1.3. Числовые расчеты Использование чисел, в частности нецелых, в качестве дан- ных или в вычислениях является причиной возможных ошибок. В процессе счета могут возникать погрешности разных типов. Некоторые из них, такие, как переполнение или потеря значимо- сти при вычислении экспоненты, отбрасывание значащих цифр можно обнаружить с помощью программных или аппаратных средств. Это фактически ошибки в логике программы. Ошибки других типов не очевидны до тех пор, пока не проанализированы результаты счета или не осуществлены процедуры проверки правильности результатов. Эти погрешности не являются ошиб- ками в обычном смысле. В случае известных чисел они могут быть предсказаны с помощью математических методов. Однако ошибки машины и ошибки программиста не могут быть пред- сказаны. Эти ошибки классифицируются в зависимости от при- чин их появления: • толерантности в измерении данных, • неточности числового представления, • округления или отбрасывания дробных цифр при выпол- нении операций на ЭВМ, • использования алгоритмов аппроксимации. Все эти причины приводят к одним и тем же последствиям: некоторые цифры чисел, подсчитанных ЭВМ, не являются до- стоверными.
Проверка правильности программ 257' Толерантность данных. Предположим, что подсчет количест- ва осадков производится измерительным устройством с ценой- одного деления шкалы 0,01 см. Тогда ошибки в одном измере- нии не превышают 0,005 см. Допустим, что при вычислении среднего числа осадков за 30 дней погрешности измерений вза- имно уничтожаются. Тогда в числе, равном сумме 30 измерений, достоверными можно считать две дробные десятичные цифры. В действительности полученный результат может отличаться от реального и на 0.15 см. Многие статистические характеристики позволяют учесть погрешности такого типа. Если сумма осадков за 30 дней составляет 4.28, то среднее количество осадков за день составит 0.142666... . Число знаков- после десятичной точки определяется длиной разрядной сетки ЭВМ, т. е. результат может быть меньше 0.143 и больше 0.142' или может быть просто равен 0.14 в зависимости от используе- мого выходного формата и от представления числа в памяти. Если сумма состоит из трех значащих цифр и достоверность последней цифры вызывает сомнение, следует признать невер- ным требование обеспечить более двух или трех значащих цифр в среднем. Программист не должен выводить на печать больше- цифр, чем требуется. Некоторые пользователи могут принять на веру эти лишние цифры. В зависимости от использования ре- зультат вычислений должен быть округлен до нужного числа значащих цифр или лишние цифры должны быть отброшены. ВЫВОДИТЕ НА ПЕЧАТЬ ТОЛЬКО ДОСТОВЕРНЫЕ ЦИФРЫ Представление чисел. Аналогичная ситуация может возник- нуть и для других типов погрешностей, но по другим причинам. Задача представления чисел в ЭВМ не связана с тем, в какой- системе счисления — десятичной или двоичной — работает ЭВМ. Такие трудности возникают в обеих системах. Если желательно выполнить цикл так, чтобы управляющая переменная менялась от 1 до 5 с шагом 2/3, управляющая переменная должна прини- мать точно семь значений. Программист может записать это по крайней мере тремя способами: for х : = 1.0 to 5.0 step .66666 for х := 1.0 to 5.0 step .66667 for x : = 1,0 to 5.0 step 2.0/3.0 (Пример 8.3)- (Пример 8.4) (Пример 8.5)- Значения х в десятичной форме будут выглядеть для шага!. 0.66666 так: х=1.0, 1.66666, 2.33332, 2,99998, 3.66664, 4.33330, 4.99996 и для шага 0.66667 так: х=1,0, Г.66667, 3.44445, 3.00001, 3.66668, 4.33335, 5.00002.
258 Глава 8 Таким образом, в первом случае цикл выполнится, как и ожи- далось, семь раз, а во втором — только шесть, так как значение 5.00002 больше верхней границы управляющей переменной. Од- нако второй вариант обеспечивает более точное представление значений х, чем первый. Проблема остается и в случае более точного представления величины шага. Если ЭВМ сама вычисляет величину шага (пример 8.5), возникнет неопределенность: заранее будет неиз- вестно, сколько раз — шесть или семь — выполняется цикл. Для управления циклом с максимальной точностью следует в каче- стве значений новой управляющей переменной использовать це- лые числа, а нужные значения вычислять внутри цикла, на- пример fori := 3 to 15 step 2 do (Пример 8.6) x:««i/3.0 или x := 1.0 (Пример 8.7) fori:= 1 to 7 step 1 do x := x+.66667 СТАРАЙТЕСЬ УПРАВЛЯТЬ ЦИКЛАМИ С ПОМОЩЬЮ ЦЕЛЫХ ЧИСЕЛ Аналогичная проблема возникает, когда сравниваются два действительных числа, например при использовании действи- тельных чисел для управления циклом: сравниваются значения управляющей переменной и ее верхняя граница. Эти значения теоретически должны быть равны, но в действительности они не равны. Можно утверждать, что если числа не были получены •одним и тем же методом, и на некотором этапе одно или оба числа имеют дробные части, они практически никогда не будут точно равны. Если используется двоичная арифметика, числа могут быть неравными даже в том случае, если они представле- ны с помощью одинаковых десятичных: чисел. Некоторые ком- пиляторы языка Фортран, получая программный сегмент Т1= 1 3 READ 5, Y IF (X.EQ.Y)... и значение входного данного 1.3, производят программу, выпол- нение которой дает в результате значение «ложь». Это происхо- дит потому, что разные алгоритмы используются компилятором л программой ввода для преобразования действительных чисел во внутреннюю двоичную форму. Независимо от того, какая (двоичная или десятичная) ариф- метика используется в ЭВМ, некоторые числа не могут быть представлены точно. Запишем 1/4 в десятичной и двоичной си- -стемах счисления. Поскольку дробь 1/4 может быть записана в виде а/bn, где а — целое число, а b — основание системы счисле- ния, то 1/4 представляется в обеих системах счисления точно.
Проверка правильности программ 259 Например в десятичной системе счисления 1/4=25/10®, а в дво- ичной 1/4= 1/22. Любое действительное число может быть пред- ставлено точно в десятичной или двоичной форме, состоящей из бесконечного числа цифр. Так, 2/3 записывается как 0.666666... в десятичной и как 0.10101010... в двоичной системе. Для лю- бых двух действительных чисел, состоящих из конечного коли- чества цифр, существует бесконечно много чисел, меньших пер- вого и больших второго. Однако в вычислительных системах,, допускающих только семь значащих десятичных цифр, сущест- вует только около 107 чисел, которые можно представить точ- но. Если используется форма представления с плавающей точ- кой и на порядок отводятся две цифры, точно можно предста- вить 10106 действительных чисел. Покажем, что только конечное число чисел может быть- представлено в ЭВМ точно на примере изображения четырехби- товых чисел с фиксированной точкой. Считая, что двоичная точка находится слева, получаем, что только ограниченное ко- личество чисел, лежащих между 0.0 и 1.0, а именно 0.0, 0,0625,. 0.125, 0.1875, 0.25, 0.3125, 0.375, 0.4375, 0.5, 0.5625, 0.625, 0.6875, 0.75, 0.8125, 0.875, 0.9375, может быть представлено точно. Де- сятичное число 0.3 представляется с помощью двоичного числа .01012=.3125ю и частное 1.0/3.0 также определяется в виде- .01012. Таким образом, бесконечное число значений должно- быть представлено ограниченным числом двоичных чисел. Де- сятичное число 0.35 может быть представлено как .0101г либо- как .01102 в зависимости от того, округление или отбрасывание младших разрядов используется при преобразовании. Проверка на равенство может показать, что два числа счи- таются ЭВМ равными, когда пользователь и не подозревает об этом. Это происходит из-за близости значений чисел, и ис- пользуемое представление не позволяет их различить. Также может возникнуть ситуация, когда пользователь считает числа равными, а они в действительности не равны, поскольку полу- чаются различными способами. Необходимо устанавливать- лишь приблизительное равенство действительных чисел. Так, оператор If abs (х—- у) < 0.00001 * abs (х) проверяет совпадение первых четырех цифр чисел х и у. НЕ проверяйте равенство действительных чисел Примеры 8.3—8.5, показывающие, как производится управ- ление циклом с шагом 2/3, не только характеризуют погреш- ности представления чисел, но также акцентируют внимание на распространении этих погрешностей. Незначительная погреш- ность увеличивается в результате выполнения ряда операций? сложения.
260 Глава 8 Пусть 8 — значение ошибки в представлении чисел, тогда 2/3 = 0.66666+®, а после шести сложений ошибка становится равной бе. Следовательно, после шести итераций ошибка влия- ет только на значение последнего разряда. Но уже после 15 итераций ошибка распространяется на предпоследний разряд. Если использовать более точное значение 0.66667, ошибка рас- пространится на предпоследний разряд только после 30 итера- ций. Вычисленное ЭВМ значение для 2/3 будет влиять на точ- ность, если оно содержит больше цифр, чем предусматривалось программистом. ЭВМ САМА ДОЛЖНА ПРОИЗВОДИТЬ АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ При анализе ошибок, возникающих в результате вы- числений, полезно обратить внимание на типы чисел, имею- щихся в распоряжении программиста. Каждый язык оперирует с одним или несколькими типами данных: целыми числами, действительными числами с фиксированной точкой, действи- тельными числами с плавающей точкой. Числа могут быть де- сятичными и двоичными в зависимости от используемого языка программирования и аппаратного обеспечения. Для определен- ности предположим, что все числа представляются в десятич- ной системе счисления. Кроме того, будем считать, что целые и действительные числа с фиксированной точкой содержат не более трех цифр и представляются в виде знак-величина. Тогда можно представить любые целые числа, лежащие в диапазоне •от —999 до +999. Арифметические действия с целыми числами будут выполняться точно при условии, что числа остаются внутри указанного выше диапазона возможных значений. Если промежуточные значения в процессе вычислений выходят за пределы диапазона, ошибка обычно не обнаруживается, а это ведет к непредсказуемым результатам. Программист должен пытаться использовать любые средства языка программирова- ния, пригодные для обнаружения ошибок такого рода. Выполним оператор присваивания N: =900+100—200. Ре- зультатом может быть число 800. Но если сложение произво- дится первым, а четвертой цифры для хранения результата не предусмотрено, то в итоге получим —200. Поскольку правило, заключающееся в том, что порядок выполнения операторов од- ного и того же приоритета слева направо не всегда выполняет- ся компилятором, изменения порядка следования операций не будет достаточно для решения этой задачи. В случае когда ва- жен порядок операций, программист должен использовать скоб- ки. В Коболе программист, работающий с большими целыми числами, может контролировать каждую операцию с помощью iSIZE ERROR. В Фортране наилучшим выходом для программи-
Проверка правильности программ 261 ста в таких ситуациях является чередование знаков операций и использование арифметики действительных чис^л. Действительные числа с фиксированной точкой, состоящие из трех цифр, также лежат в диапазоне от —999 до +999, но неравномерно распределены в этом диапазоне. В диапазоне от 0.0 до 1.0 помещается 999 чисел (0.001, 0.002, ..., 0.010, 0.011, 0.012, ..., 0.100, 0.101, 0.000, 0.999), в то время как в диапазоне от 998 до 999 нет ни одного такого числа. Считая, что в про- цессе представления чисел ошибок не вносится, выделим два источника ошибок: выход промежуточных значений за пределы диапазона и потерю значащих цифр при выполнении операций. Выполнение оператора N : = 900.4-100.-200. ' может привести к выходу за пределы диапазона, а выполнение оператора N : = 100.4-10.5—80.0 к потере значащих цифр. В зависимости от порядка выполне- ния операций результат может быть либо 30, либо 31.- Такого рода ошибки возникают, когда программист использует языки типа Кобол и ПЛ/1, которые позволяют задавать положения точки для каждой переменной. Если объявление N приводит к потере значащих цифр, ошибка обнаруживается. Но если наи- меньшие значащие цифры теряются при выполнении промежу- точных вычислений, обнаружить ошибки не удается. Проблема не решается, даже если N имеет один дробный разряд. В по- следнем случае результат зависит от правил выполнения ариф- метических операций, и N может принять любое из следующих трех значений: 30.0, 30.5, 31.0. Программист при работе с числа- ми с фиксированной точкой должен использовать один и тот же формат для всех чисел, чтобы избежать непредсказуемых ре- зультатов. Аналогичные проблемы возникают при работе с числами с плавающей точкой. Предположим, что используются трехраз- рядные десятичные мантиссы, а порядок меняется от —4 до +4. Тогда возможные значения лежат в интервалах от —9990 до —•0.0000999 и от +0.0000999 до +9990. Распределение чисел снова неравномерное, и большинство из 20 млн. возможных чи- сел лежит в диапазоне от —1 до +1. Отметим, что, хотя фор- мально число 0.0 не включено в множество представимых чисел, фактически оно присутствует в виде 0.000X10°. Дополнитель- ные ошибки появляются при работе с числами с плавающей точкой, когда порядок выходит за пределы отведенного ему Интервала. В общем случае такая ситуация может быть обна- ружена, но реакция на нее не всегда одна и та же. Например,
262 Глава 8 в ПЛ/1 программист может задать другой путь вычислений. В Фортране такую ситуацию устранить нельзя. В Бейсике поте- ря порядка приводит к неверному обнулению, и выполнение программы продолжается. ПРИ НАПИСАНИИ ПРОГРАММ СЛЕДИТЕ ЗА ТОЧНОСТЬЮ Отбрасывание дробных разрядов. Вычисление на ЭВМ функций y—f(x) дает результат tf, который отличается от ис- тинного значения у из-за возможных погрешностей и ошибок, возникающих при вычислениях. Пусть V— представление х в ЭВМ; ъх=х—х'— ошибка представле- г _ I ех I I rt I ния; относительная ошибка представления х'; (8-1) г — относительная ошибка, вносимая в процесс выполнения операций на ЭВМ; 8=гх' — абсолютная ошибка, вносимая в процесс выполнения операций на ЭВМ; sx— число достовер- ных цифр в х. Если г/=а+&, то у' = а’ +&'—8- и гу = У~/ = 8я+е6+е. Последнее выражение не требует никаких комментариев. Аб- солютная ошибка 0.1 очень велика, если у' имеет только одну дробную десятичную цифру, и мала, если у7 имеет восемь цифр слева от десятичной точки. Больше пользы принесет ис- пользование относительной ошибки. Если относительная ошиб- ка равна 0.00001, это означает, что ошибка может повлиять на значение шестой слева цифры результата, т. е. в этом слу- чае результат содержит пять достоверных цифр. Относитель- ная ошибка, равная 0.1, влияет на значение второй цифры слева, оставляя достоверной лишь одну цифру. Число досто- верных цифр в представлении можно приблизительно опреде- лить по формуле sv = log10 ± = log10 (| у' |)—logio (| |). Если y=a+6, то sy «log10(|a+b|)—loglo(| 8e+8ft+e|). (8.2)
Проверка правильности программ 263 Предположим, что абсолютные значения а и Ь приблизи- тельно равны и одно из них отрицательно, например а = 0.42598, —0.42406. Тогда, считая, что в представлении чисел используются три значащие цифры и производится отбрасывание остальных дробных цифр, а не округление при преобразованиях и прове- дении арифметических операций, имеем ев= 0.00098, 86 =—0.00006, у = а + Ь = 0.00192, &'=0.425, У = —0.424, у' = 0.425— 0.424 + 8 = 0.001 + 8, 8^ = 0.00092, То есть сумма не будет иметь ни одной достоверной цифры. Если а' — трехразрядное десятичное число с фиксированной точкой, любая ошибка при преобразовании а к а' приводит к уменьшению числа достоверных цифр. Истинное значение а лежит в интервале между а' и 1.01 XX, и относительная ошиб- ка равна 0.01. Поскольку относительная ошибка и число до- стоверных цифр связаны между собой, приблизительная оцен- ка относительной ошибки может быть найдена для значений, получаемых путем измерений, в том случае, если число досто- верных цифр известно. Пусть sx— число достоверных цифр; тогда относительная ошибка /•*<10-4 (8.3) Предельные значения ошибок, вносимых вычислениями, мож- но получить для любой ЭВМ. Относительная ошибка пред- ставления для десятичных чисел задается в виде (8.4) а для двоичных в виде (8.5) где t — число цифр в мантиссе значения, представленного в форме с плавающей точкой при условии, что в процессе пре- образования производится отбрасывание младших разрядов. Если же производится округление, то гх^10|_//2 и гх^.2~*. Относительная ошибка суммы двух чисел может быть найде-
264 Глава 8 на из следующих соотношений: eJ, = 8a4-8z,+e, у' у' ' у' у' y' fa’ I "г y' lb' l “г у'’ Тогда ry=JWr+-T^r- + r (8.6) у 1у I 1у I ' Если действия производятся с числами, состоящими из трех десятичных цифр, максимальная относительная ошибка возникает в процессе сложения а'=100 и Ь'=0.999. Так как абсолютная ошибка е^1, то относительная ошибка г^О.01, т. е. равна относительной ошибке представления чисел. Для трехразрядных десятичных чисел имеем г4<10-2, 10-2 и г ^0,01. Поэтому / _ 0.011^ + У! у “+* I и' I 0,01. В случае представления двоичных чисел в форме с плавающей точкой (в мантиссе 24 разряда) гх^.2~23 «0.00000012. Если у=а+Ь, то rJ/^2“23((|a'| + |Ь/|4-2-23)/у/. Если знаки а и 6 совпадают, то гу^2“23+2-23 = 2-22. Это —максимальное зна- чение ошибки. В большинстве случаев ошибка гораздо мень- ше, а иногда может и не возникнуть. С другой стороны, если абсолютные значения а и b близки, но знаки разные, тогда /«0 и относительная ошибка очень велика. Для разности двух чисел максимальное значение относи- тельной ошибки будет таким же, как и для суммы , _ I Д' I . гь\Ь'\ I a~b 1/1 Ф 1/1 (8.7) Это объясняется тем, что разность двух чисел равна сумме первого и второго числа, взятого с обратным знаком. Если y — ab, а' = а—еа, b' = b—гь, то a'b' = ab—ае.ь— Ьга + еа86, 8г, = аеа+Ьеа—8оей+8, , - lgyl _ lae» + fca —eas& + e[ |д'8(,1 + Р'еа|+|8| v 1/1 1/1 |Д'1 Р'|
Проверка правильности программ 265 Следовательно, для относительной ошибки произведения двух чисел справедливо неравенство гв6<га+Гб+г. (8,8) Если У=-у, а' = а—га, то Ь'=Ь—86, а' а9 ~~ 8д л* — 8д 1 У 6'-86 У 1 -(8Ь/&') _ д'—8Д / . | 8» . et8 , ей3 \______ — Ь' 1 "Т" Ь' ‘ Ь' Ь‘ t_, а* — 8д / « । в/, \ af 8д । аг&ь _ 8^8^ ~ У ( 1 ‘+'У) ~ Ь> Ь' "* (6')а (6')а ’ Следовательно, для относительной ошибки частного двух чисел справедливо неравенство га!ь '|УГ + г*+г r«+r*+ г’ (8-9) т. е. формулы для относительных ошибок произведения и ча- стного одинаковы. Если а и b имеют одни и те же знаки, от- носительные ошибки для операций сложения и вычитания значительно меньше относительных ошибок для операций умножения и деления. Однако, если знаки а и & различаются, операции умножения и деления выполняются значительно точ- нее, чем операции сложения и вычитания. Полученные выше формулы могут быть использованы для определения относительных ошибок, возникающих при вычис- лении более сложных выражений. Если i=i то . / п п \ Г^т^г Ur. (8.Ю) V'=l 1=2 J Если = П Хгг i=i 18-399
266 Глава 8 ТО п Гр<^гх1 + г(п— 1). 1=1 (8.11} Эти формулы позволяют сделать вывод о том, что ошибки при суммировании и умножении нескольких чисел могут быть больше, чем при суммировании двух слагаемых и умно- жении двух сомножителей. Возможность распространения ошибок была показана выше, когда рассматривались приме- ры выполнения оператора цикла с шагом 2/3. Формулы для ошибок суммы и произведения можно использовать совмест- но для определения верхних границ относительных ошибок, возникающих при вычислении значений сложных выражений. Если / (х) = ах24-Ьх+с, то гвХ2< гв4-2гх+2г, rbx<rb+rx+r. Гах*+Ьх < 1^4-hri (।ах*। (гв+2гх+2гЖbxI to4-G + r) ) +г» поэтому rf («) | e№ 4- c | (10x21 (ra+2rx+2r)+ +1 bx] fa4-Г,+Г)-ЯgIG+ |ц. (Ia*21 to + 2^4-2r) + +1 bx I (rb4- r 14- r)) + r < (51 ax21 r 4- 3 ] bx | r4- . . 5r*|«r2l4-3r2|far|4-'2\ । r < + lclr • |a№4-6x| )-tr^ <1^4-1x4-71 (5|«x2|r4-3]fc|r4-|c|r)4-r старайтесь свести к минимуму РАСПРОСТРАНЕНИЕ ОШИБОК Аппроксимация. Ошибки четырех различных типов появ- ляются из-за того, что во многих числовых алгоритмах ис- пользуются методы аппроксимации. Теоретически аппрокси- мация может быть проведена с любой степенью точности. Но на практике вследствие распространения ошибок, возни- кающих в результате использования ЭВМ, всегда существует предел достижимой точности. Ошибки, связанные с аппрокси- мацией (например, при использовании только первых трех
Проверка правильности программ 267 членов ряда при разложении функции в ряд), не могут рас- сматриваться без учета распространения погрешностей. Аппрок- симация осуществляется так, чтобы обеспечить максимальную точность. Используя дополнительные члены ряда, не всегда можно добиться большей точности, поскольку в процессе вы- числения этих членов возникают ошибки, связанные с распрост- ранением погрешностей. Часто самые простые ряды не являют- ся наилучшими для расчетов на ЭВМ. Рассмотрим функции LOG и ЕХР10, которые имеются во многих языках программирования. Теоретически если Х= = LOG(Y) , то Y=EXP10(X). На практике эти функции не яв- ляются взаимно-обратными, поскольку для определения их зна- чений используются приближенные методы. Ниже приведен один из способов определения значений функции LOG; , ч 1 . (х — /То \ . / х-/То \8 log10 (х)- — + с,) + с8) при 1 < х 10, (8.12) где Ci = 0.86304, £3 = 0.36415. Максимальная абсолютная ошибка определения значения логарифма равна 0.000602, а относитель- ная ошибка sg:0.0001. Более точная приближенная формула имеет вид . „ , х 1 ( х — /То \ / х — /То V . Iog10(х) = - С. (^+/гГ ) +^3 J + 4-£5при 1 < хс 10, (8.13) где £1 = 0.8690286, £3 = 0.2773839, с5 = 0.2543275. Максимальная абсолютная ошибка равна 0.0000337, а мак- симальная относительная ошибка составляет 0.00007. Добавле- ние еще одного члена ряда и пересчет коэффициентов умень- шает относительную ошибку до 0.000004. Эти формулы напо- минают первые члены разложения в ряд функции logio (х). Но они не являются членами ряда, поскольку ряд сходится слиш- ком медленно и эффект распространения ошибок значителен. Эти выражения являются полиномами Чебышева. Для каж- дого интервала значений х специально с целью минимизации ошибки вычислены коэффициенты полиномов. Абсолютные ошибки — это максимально возможные отклонения аппроксими- рующих значений от истинных внутри заданного интервала. Чтобы использовать эти выражения для значений аргумента, выходящих за пределы интервала, необходимо представить значения аргумента в виде произведения сомножителей, боль- ших 0 и меньших 10. В некоторых пакетах подпрограмм фир- 18*
268 Глава 8 Таблица 8.1. Методы аппроксимации, используемые в IBM 360 Функция Интервал Максимальная от- носительная ошибка Методы LN (X) SIN (X) EXP (X) SQRT (X) Х>0 Х<2'8л 174.673 Л>0 2.37x10-8 3.4Х10-» 1.8Х10-» 1.59X10-8 Чебышева Чебышева бесконечных дро- бей Ньютона — Раф- сона мы IBM. используются приближенные методы, перечисленные в табл. 8.1. В случае работы с действительными числами вероятность появления максимальной ошибки можно уменьшить, если вы- полнять следующие рекомендации: а) складывать наименьшие члены суммы первыми; б) определять все положительные и все отрицательные сла- гаемые и поочередно их складывать; в) избегать показателей степени, представленных в форме действительных чисел; г) использовать в процессе вычислений двойную точность, а результаты представлять с обычной точностью; д) уменьшать число операций; е) избегать цепочек операций, в которых используются не- точные значения; ж) использовать алгоритмы, для которых известны оценки и границы ошибок. Вычисление функции у—х%х производится более точно, чем вычисление функции у—х2Л, так как в случае действительного показателя степени экспонента вычисляется с применением ло- гарифмов и антилогарифмов по формуле у=ехр[2.01п(х)]. Зна- чения функций ехр и In вычисляются по приближенным форму- лам. Сочетание этих функций приводит к росту ошибки. По той же причине вычисление SQRT (X) может быть выполнено точнее, чем вычисление Х**0.5. 8.2. Тестирование модулей Программные модули должны разрабатываться и реализо- вываться так, чтобы они содержали как можно меньше оши- бок. Машинное время необходимо использовать для тестирова- ния модулей только в том случае, если ручная проверка не позволяет больше обнаруживать ошибки. Задачей тестирования является обнаружение ошибок, а не демонстрация правильности работы программы. Поэтому часто разумнее поручать подго- товку тестовых данных человеку, не принимавшему участия в
Проверка правильности программ 269 реализации программы. Следует начинать с тестирования и отладки отдельных модулей, а потом переходить к их сопряже- нию. Так как модули соединены друг с другом, они тестируют- ся по группам. На заключительном этапе пользователи прово- дят тестирование всей системы. ТЕСТОВЫЕ ДАННЫЕ НЕОБХОДИМЫ ДЛЯ ОБНАРУЖЕНИЯ ОШИБОК При тестировании на уровне модулей каждый оператор дол- жен выполняться по крайней мере один раз и каждый путь в программе должен быть пройден по крайней мере один раз. Кроме того, должна проверяться каждая спецификация. Для выполнения этих проверок разработано несколько подходов. 8.2.1. Тестирование путей Все возможные пути прохождения модуля могут быть выяв- лены с помощью управляющего графа. На рис. 8.1, а показан управляющий граф (см. рис. 7.5, в) для программы из приме- ( Конец ) Рис. 8.1. Схема путей управляющего графа, а — управляющий граф; б — схема путей. ра 7.31. Эта программа используется для вычисления наиболь- шего общего делителя двух чисел. Все возможные ветвления на рисунке помечены. На рис. 8.1,6 вершины управляющего гра- фа, принадлежащие сразу нескольким путям, размножены, что- бы сформировать древовидную структуру. Цикл 11—11 развер- нут, что позволяет показать возможность прохождения через него нескольких путей. Циклы, управляемые счетчиком, разве-
870 Глава 8 рачиваются в единственный путь, а циклы, управляемые собы- тиями (такие, как рассматриваемый нами), имеют неопределен- ное число возможных путей, так как число итераций зависит от начальных значений параметров функции J и I. Существу- ют четыре основных типа путей, проходящих по управляюще- му графу: начало-6-8-11-16-конец; начало-6-8-11)*-16-конец; начало-6-11-16-конец; начало-6-11 - (11) *- 16-конец Первые два объединяют пути, включающие левую ветвь, вы- ходящую из вершины 6, а вторые два объединяют пути, вклю- чающие правую ветвь, выходящую из этой же вершины. Второй и четвертый связаны с путями, идущими через цикл. Звездоч- ка (*) означает повторение. Все возможные пути проверить нельзя, так как их число бесконечно, но можно проверить все четыре типа путей. Ниже приведены процедуры, позволяющие определить значения данных, необходимые для выбора того или иного пути. Путь 1: начало-6-8-11-16-конец. Условие N>M для вершины 6 не противоречит условию N = 0 для вершины 11, поскольку между вершинами 8 и 11 значения N и М могут измениться. Этот путь выбирается, когда начальное значение М = 0. Путь 2: начало 6-8-11-(11)*-16-конец. Он выбирается, когда для начальных значений справедли- вы соотношения M<N и М^=0. Путь 3: начало-6-11-16-конец. Этот путь выбирается, когда начальное значение N=0. Таблица 8.2. Тестовые данные для анализа путей I J GCD Тест Путь 5 0 5 Левая ветвь, цикла нет 1 6 2 2 Левая ветвь, цикл выполняется один раз 2 5 2 1 Левая ветвь, цикл выполняется _ дважды 2 10 6 2 Левая ветвь, цикл выполняется трижды 2 0 5 5 Правая ветвь, цикла нет 3 2 6 2 Правая ветвь, цикл выполняется один раз 4 2 5 1 Правая; ветвь, цикл выполняется дважды 4 6 10 2 Правая ветвь, цикл выполняется трижды 4
Проверка правильности программ 271 Путь 4: начало-6-11-(11)*-16-конец. Он выбирается, когда начальные значения N и М связаны соотношением NsgrM и N^O. Поскольку значения N и М равны модулям параметров I и J соответственно, тестовые данные, необходимые для анализа путей, должны быть следующими: Путь 1: 1^=0, J=0. Путь 2: 0<|J|<|I|. Путь 3: 1=0, J — любое. Путь 4: 0< 11| < | J |. Четырех пар значений достаточно для проверки всех типов пу- тей, но лучше предусмотреть больше значений для проверки механизма работы цикла. В табл. 8.2 приведены наборы тесто- вых данных. В качестве тестовых данных должны использо- ваться простые числа, чтобы ответы могли быть определены заранее. 8.2.2. Тестирование структур управления При тестировании структур управления используются ре- зультаты тестирования путей. Тестирование структур отлича» ется от тестирования путей -только способом проверки работы циклов, управляемых событиями. Поскольку в случае циклов, управляемых событиями, невозможно проверить все пути, их тестирование не снимает вопрос о необходимости проверки путей, содержащих цикл. При тестировании управляющих структур считается, что одного прохода по циклу достаточно. Так как тестирование включает сравнение получаемых резуль- татов с ожидаемыми, для данного управляющего графа одного прохода по циклу достаточно. Если данные, вызывающие од- нократное выполнение цикла при входе в цикл, принимают зна- чения п и т, а на выходе значения п'—О и tn'—GCD (п, т)=» = GCD (/, /), то по индукции можно доказать, что данные, вы- зывающие выполнение цикла k раз, на выходе имеют значения n<ft> = 0 и mw = GCD =...=GCD (п, т). Если в цик- ле предусмотрено больше одного условия завершения, тестиро- вание управляющих структур подразумевает тестирование каж- Таблица 8.3. Тестовые данные для анализа структур I J GCD Тест 0 5 5 Правая ветвь, N<M, цикла нет 0 0 0 Правая ветвь, N=M 6 2 2 Левая ветвь, N>M, цикл выполняет- ся один раз
272 Глава 8 дого условия. Также все структуры выбора с составными усло- >виями необходимо проверять для каждой комбинации условий. В табл. 8.3 даны наборы данных, достаточные для тестирова- ния управляющих структур. Все возможные результаты, полу- чаемые при сравнении N и М, проверяются. Также тестируются варианты обхода цикла и однократного вхождения в него. Трех наборов тестовых данных достаточно для проверки как струк- тур выбора, так и структур повторения. В отличие от тестиро- вания путей здесь не предпринимаются попытки найти все воз- можные комбинации ветвлений и ситуаций, возникающих при выполнении циклов. 8.2.3. Тестирование ветвлений Если в модуле много ветвлений и циклов, нецелесообразно проверять все возможные пути, даже если циклы не относятся к типу циклов, управляемых событиями. При тестировании ветвлений анализируются точки ветвлений управляющего гра- фа, чтобы определить данные, необходимые для выбора каж- дой ветви по крайней мере один раз. Каждая ветвь рассматри- вается отдельно, комбинации ветвей не анализируются. Тесто- вые данные для управляющего графа, показанного на рис. 8.1, должны удовлетворять следующим четырем условиям: M<N, N^M, N = 0, N 0. Эти условия могут быть учтены с помощью двух наборов тестовых данных, приведенных в табл. 8.4. Эти данные не пред- Таблица 8.4. Тестовые данные для анализа ветвей I J GCD Тест 3 о 1 1 M<N, N=/=0 N^M, N=0 назначены для тестирования только отдельных ветвей. Тест N=#0 приводит к выполнению цикла, причем число итераций в данном случае не имеет значения. 8.2.4. Тестирование утверждений Если в программе встречаются блоки on, для проверки всех операторов будет недостаточно тестирования только ветвлений и путей. Работа блоков on также должна проверяться. Тестиро- вание необходимо проводить так, чтобы каждое утверждение блока on выполнялось хотя бы один раз. Для этого надо за-
Проверка правильности программ 273 давать данные, вызывающие возникновение необычных ситуа- ций. Так как в программе GCD нет операторов обработки ис- ключительных состояний, то при тестировании путей, структур или условий будут выполняться все операторы программы. Таблица 8.5. Тестовые данные для классов значений I J GCD Класс 0 1 1 0=N^M 1 2 1 0<N^M 2 0 2 0=M<N 2 1 1 0<M<N В более сложных программах, где трудно проанализировать условия, необходимые для выполнения каждого оператора, сле- дует выделять классы значений данных для анализа точек вет- влений и исключительных состояний. В табл. 8.5 представлены наборы тестовых данных для различных классов значений N и М. СТАРАЙТЕСЬ избегать использования операторов ОБРАБОТКИ ИСКЛЮЧИТЕЛЬНЫХ СОСТОЯНИИ 8.2.5. Тестирование специальных значений Тестирование специальных значений дает больше возможно- стей, чем тестирование операторов не только при поиске та- ких значений данных, которые приводят к активации операто- ров обработки исключительных состояний, но также при выбо- ре значений, которые позволят выявить логические ошибки, ес- ли они есть. Поскольку наиболее часто встречается логическая ошибка, заключающаяся в неправильном применении равенст- ва для организации ветвления, для рассматриваемого приме- ра должны быть предусмотрены тесты, в которых N = M, при- чем значения N и М следует выбрать в одном случае очень большими, а в другом — очень маленькими. Для проверки ра- боты функции, вычисляющей абсолютное значение числа, надо использовать положительные, отрицательные и .нулевые значе- ния I и J. Для тестирования функции, вычисляющей остаток от деления, желательно предусмотреть вариант, когда N = 0. В табл. 8.6 приведены данные, предназначенные для тестирова- ния как обычных, так и специальных значений. Поскольку программа не содержит логических ошибок, функция, вычисля- ющая остаток, не будет работать, когда N = 0. Успешное ис- пользование оператора N = 0 показывает, что «подступы к этой Функции надежно охраняются».
274 Глава 8 Таблица 8.6. Данные для тестирования специальных значений Рассматриваемый подход к анализу нестандартных ситуа- ций заключается в использовании тестов, позволяющих выде- лить классы данных, которые могут привести к серьезным си- стемным ошибкам. Например, делаются попытки найти данные, которые вызывают деление на ноль, выход индекса за границы, приводят к возведению отрицательных чисел в действительные степени, потере значимости, неопределенным значениям. На рис. 8.2 показан простейший вариант записи функции GCD. Анализ нестандартных ситуаций, которые могут возни- кать при выполнении этой программы, дает следующие резуль- 1 FUNCTION GCD (N. М) 2 10R = M-M/N* N 3 IF (R .EQ. 0) GO TO 20 4 M= N 5 N= R 6 GO TO 10 7 20 GCD = N 8 RETURN . 9 END Рис. 8.2. Неправильная версия программы. таты. Во второй строке используется операция деления нацело, которая может являться источником ошибок. При выполнении операции деления всегда возможен также вариант деления на ноль. Если N = 0, выполнение операции деления нацело приво- дит к неудаче. В третьей строке сравниваются действительные и целые числа. Отметим, что R явно не объявлено действитель- ным; на самом деле оно должно быть объявлено целым. Такая
Проверка правильности программ 275 операция сравнения является потенциальным источником оши- бок, поскольку относится к операциям, проводимым с операн- дами разных типов. Также возможны ошибки при назначении новых значений параметрам в строках 4 и 5. Присваивание но- вых значений переменным было бы верным для подпрограммы, в которой явно объявлено, что эти переменные являются одно- временно как выходными, так и входными параметрами. Но для функции такие действия являются если не ошибочными, то по крайней мере спорными. Результаты анализа нестандартных ситуаций используются в основном не для разработки тестов, а для решения вопросов, связанных с проверкой правильности программ. Эти вопросы возникают после просмотра текста программы и поиска значе- ний переменных, которые могут привести к возникновению ис- ключительных ситуаций и ошибок. Еще один важный момент не учитывается программой, при- веденной на рис. 8.2: наибольший общий делитель должен быть положительным. Для отрицательных значений N и М возможен вариант, когда возвращается отрицательное значение. Если М равно нулю,в качестве GCD возвращается значением. Хотя та- кой ответ в принципе является верным, отчасти теряется смысл, связываемый с именем функции. Если оба входных значения равны нулю, то значение GCD считается теоретически неопреде- ленным. В данном случае возвращается значение, равное нулю. Это меньше вводит в заблуждение, чем возврат любого друго- го значения. Кроме того, возникают трудности, если значение функции GCD используется в качестве делителя. Следует пом- нить, что, если имя функции не точно отражает сущность вы- полняемого действия, необходимо использовать спецификации, вносящие недостающую ясность. В спецификации модуля долж- на быть включена информация об обработке ошибок и о при- сваивании значений по умолчанию. В спецификациях для дан- ного примера также должно быть отражено, во-первых, следует ли параметрам быть положительными и, во-вторых, стандарт- ным ли образом программа выполняет свои функции. 8.2.6. Фиктивное выполнение программы Сквозной структурный контроль программы позволяет про- моделировать процесс выполнения программы. Его можно осу- ществить либо с реальными, либо с фиктивными данными. Если просмотр проводится с реальными данными, он соответствует тестовому прогону на ЭВМ. Для представления различных ка- тегорий данных можно использовать фиктивные данные, пред- ставляющие собой математические выражения в символьной форме. Модуль считается фиктивно выполненным, если пере- менные или выражения преобразуются, проходя через модуль.
276 Глава 8 в соответствии с правилами символьных преобразований. Если имеются языки для работы с алгебраическими выражениями, прогон программы с фиктивными данными может быть осуще- ствлен на ЭВМ. Для данного примера предпочтительнее начинать с простых переменных N и М или 1 и J. Затем уже проще определить вы- ражения, которые легко могут быть использованы для фиктив- ного выполнения программы. Например, если g — наибольший общий делитель для I и J, выбираем I = g и J = i*g, где i — це- лое. Если i не равно нулю, эти выражения могут быть исполь- Таблица 8.7. Наборы фиктивных данных I J g i*g i* g g g g g 0 0 g i* g i *g+g i* g + g i* g i* g + g i* k* g+k*g + i* g зованы для проверки однократного прохождения цикла. Для двух итераций надо положить I=i*g и J = i*g+g, а для трех итераций — I = i*g+g и J = i*k*g+k*g+i*g, где к — целое чис- ло. Меняя значения I и J и выбирая различные значения i, в том числе отрицательные и положительные, ноль и единицу, можно проводить тестирование ветвлений и путей. В табл. 8.7 приведены некоторые варианты значений I и J. 8.3. Формальные методы доказательства правильности программ Формальный подход к доказательству правильности прог- рамм заключается в использовании логики для демонстрации корректности программного модуля. Пользуясь этим подходом, можно показать, что модуль удовлетворяет заданным специфи- кациям, но нельзя определить, полон ли набор спецификаций и верны ли они. Считается, что модуль выполняется не на ре- альной машине, для которой характерно распространение оши- бок и возникновение нестандартных ситуаций, а на гипотети- ческой. Другими словами, текст программного модуля рассмат- ривается как математическая модель его спецификаций, а не реальная программа для ЭВМ. Для формального доказатель- ства корректности программист должен второй раз описать ло-
Проверка правильности программ 277 гику программы (первым описанием является текст самой программы). Это требует формального задания спецификаций. Таким путем осуществляется дополнительный контроль оши- бок. Доказательство правильности состоит из двух частей. Сна- чала показывается, что выполнение модуля обязательно завер- шится; затем с помощью определенных утверждений, связан- ных с входными переменными модуля, доказывается истин- ность других утверждений в момент завершения модуля. Для этого обычно используются формальные методы доказательст- ва или неформальная аргументация. При использовании фор- мальных методов удается избежать ошибок, свойственных не- формальной аргументации, но эти методы менее удобны в том случае, если нужно убедить людей в корректности программы. Кроме того, проведение формальных доказательств достаточно утомительно. 8.3.1. Утверждения Первым шагом в процессе доказательства корректности мо- дуля является задание формальных утверждений, касающихся значений переменных в различных точках структурной схемы программы, в частности в начале, конце, до и после точек вет- ;p{F}S (Р Л Q) {F} R (РЛ~О) {G}S v (Р Л R) {F} S Рис. 8.3. Правила вывода утверждений для основных конструкций программы. -в — следование; б — развилка; в — узел слияния, вления и слияния. На рис. 8.3 показаны основные способы вывода утверждений для последовательностей операторов F и G и утверждений Р, Q, R и S. Утверждение Q считается ис- тинным, если логическая проверка q дает положительный ре- зультат. Запись P{F}Q означает, что, если утверждение Р ис- тинно и выполнена последовательность операторов F, утверж- дение Q истинно.
278 Глава 8 Утверждения могут быть сформулированы на любой стадии разработки программы. Если принят подход, основанный на пошаговой детализации проекта, неформальные доказательст- ва утверждений могут проводиться на каждой стадии детали- зации. Формальные же методы доказательства можно исполь- зовать только тогда, когда полностью готов текст программно- го модуля. Если в программе, вычисляющей наибольший общий дели- тель (пример 7.31), предусмотрены входные и выходные специ- фикации, они могут считаться утверждениями для всей функ- ции. На псевдокоде это записывается так: Р: утверждается, что i, j имеют наибольший делитель, равный g (Пример 8.8} F: вычисляется GCD (I, J) для I=i, J=j Q: утверждается, что GCD (i,j)=g Здесь прописными буквами обозначены переменные програм- мы, а строчными — значения переменных. Все утверждения ка- саются значений, а не самих переменных. Утверждения долж- ны быть сформулированы таким образом, что если Р истинна и выполнена программа F, то Q также истинно. Для приме- ра 8.8 Р не является истинным, если i и j равны нулю. Но в ут- верждениях не содержится ничего относящегося к этому слу- чаю. Поэтому не считается неверным переход от Р к Q с по- мощью F. Таким образом, набор спецификаций неполон, и не- обходимо его расширить так, чтобы учесть вариант, когда оп- ределяется GCD (0, 0). Расширим текст программы на псевдокоде, введя дополни- тельные утверждения, следующим образом: (Пример 8.9) Р: утверждается, что i, j имеют наибольший общий делитель, равный g F: N := min (|I|, |J|); M:= max (|I|, |J|); R: утверждается, что g=gcd (i, j), g=gcd (n, m), Ocncm G: вычисляется GCD (N, M) для N=n, M=m Q: утверждается, что GCD (i, j) = g Тогда задача определения наибольшего общего делителя двух чисел может быть сведена к более простой задаче определения наибольшего общего делителя двух упорядоченных по возра- станию неотрицательных чисел, т. е. P{F}Q может быть рас- ширено до выражения P{F}R{G}Q, обе части которого имеют свои собственные спецификации. Чтобы показать корректность всей программы, необходимо показать, во-первых, что если Р истинно и F выполнено, то R истинно, и, во-вторых, что Q сле- дует из R и G. Описания для F и окаймляющих его утверждений могут быть расширены в соответствии с текстом программы в при- мере 7.31:
Проверка правильности программ 279 (Пример 8.10) Р: утверждается, что i, j имеют наибольший общий делитель, равный g F: N := abs (I); М := abs (J); утверждается g=gcd (i, j), g=gcd (n, m), n>0, m>0 if N>M then поменять местами значения N и M R: утверждается, что g=gcd (i, j), g=gcd (n', m'), Ocn'cm' Отметим, что значения M и N в R могут отличаться от значе- ний, приписываемых этим переменным раньше. Так как пит использовались для представления первоначальных значений N и М, то для представления этих переменных в R используются п' и гл7, причём либо п' = п и m' = m, либо n' = m и m' — n. Для детализации описания процедуры G введем цикл: (Пример 8.11) R: утверждается, что g=gcd (i, j), g=gcd (n', m'), Ocn'cm G: while N=/=0 do утверждается, что g=gcd (i, j), g=gcd (n', m'), OCn'cm' L := remainder (M, N) ; M:= N; N:= L; утверждается, что g=gcd (i, j), g=gcd (n", m"), 0<n"<m" endwhile утверждается, что g=gcd (i, j),m"=g GCD:= m" Q: утверждается, что GCD (i, j)=g Утверждения, стоящие в начале и в конце тела цикла, очень похожи. Новые значения и" и ш" в конце цикла становятся п' и т' для следующей итерации. Утверждения, связанные с цик- лом, должны отражать отношения, которые являются истинны- ми, независимо от того, сколько раз цикл выполняется. Эти неизменные отношения получили название инварианты цикла. Инвариант — это выражение, аналогичное выражению, приме- няемому для рекурсивного определения функций, которое вы- ражает текущие значения переменных через их предыдущие значения. Инвариант служит для описания связей значений, принимаемых переменными в течение одной произвольно взя- той итерации, с предыдущими значениями тех же переменных. Отметим, что в этом случае для доказательства можно исполь- зовать индукцию. На рис. 8.4 показана структурная схема функции GCD, до- полненная полученными утверждениями. Формальная запись программы принимает вид Ах {FJ А2 (((п > ш) {FJ)V((n < m) { })) А3 ((п' * 0) A1J{F3}]A5)* (п' = 0) Ae {FJ А7 Корректность модуля подтверждается тем, что последова- тельное выполнение сегментов программы позволяет перехо-
280 . Глава 8 Рис. 8.4. Утверждения для структурной схемы программы нахождения GCIX дить от одного утверждения к следующему. Для наиболее глу- боко лежащих структур (рис. 8.4) порядок следования утверж- дений меняется на обратный. Правильность структуры выбора на рис. 8.4 может быть установлена, если удастся показать, что справедливо Аа («п > m) {F2})V((n < m) { })) А„ т. е. сегмент F2 позволяет перейти от А2 к А3 при условии, что n>m, и Аз следует непосредственно из n^m. Для цикла необходимо проверить следующие утверждениям А3(п'==0)Ав, А3(п"=йО)А4> А4{Рз}А6, Ав (п' ф 0) А4 и А6 (п' — 0) А*.
Проверка правильности программ 281 После этого формальная запись текста программы примет вид {Fx} A2{Gx) А3 {G3} (n' = 0) Ae {F4} А7, причем известно, что Gi, G2 — правильные сегменты. Тогда должны быть проверены только утверждения AJFjAj и A6{F4}A7. Неформальная аргументация проводится следующим обра- зом: 1) А, (((п > m) {F2}) V((n С т) { })) А,. А2 есть g=gcd (i, j) = gcd (n, m), n > 0, m > 0, и A3 есть g = gcd(i, j) = gcd(n',m'), 0<n'<m'. Если n' = n, m' = m и n < m, то A3 сразу же следует из А2. Если n > ш, то п' = т ит' = п, поэтому п' < т' и А3 справедливо» 2) А3(п' = 0)Ав. Из А3 следует, что g = gcd(n',m'); поэтому g = gcd(0,m') = = m', поскольку нуль делится нацело на т'. Считая, что m* = n\ приходим к А,. 3) А3(п'^=0)А4. А4 есть g = gcd (i, j) = gcd(n',m'), 0<n'^[m'. 4) A4{F3}A5. A5 есть g = gcd(i, j) = gcd (n", m"), 0^n"<m". Выполнив операторы присваивания, получим m" = n и п" = гепв (m',nz), так как п'#=0. Поскольку остаток всегда меньше делите- ля, имеем n" < n' = т", но так как остаток может быть нулем, то» 0<п"<т". Согласно утверждению А4, существуют такие целые числа а и Ь, что n' = a*g, a m' = b*g. Тогда n" = b*g—(b*g/(a*g))* *(a*g) = b*g—(b/a)*(a*g) = g*(b—(b/a)*a); отметим, что здесь бе- рется целая часть частного. Тогда m" = n' и п" делятся нацело на g. Кроме того, а и b—(Ь/а)*а для п' и ш' выполняют те же функ- ции, что а и b для п' и ш'. Следовательно, g = gcd (п",т*)-1Это и доказывает утверждение А5. 5) А5(п' = 0)Ав. Из As следует, что g = gcd (n", m"). Отождествим n" из A5 с n из условия завершения цикла. Тогда g = gcd(0, m") и m" = g. 6) A5(n'#=0)A4. Из А5 следует, что п"< ш'. Отождествим п" и т" в ниж- ней части цикла с п' и па' в его верхней части. Тогда 0 < п' < т' и g=gcd(n".m") = gcd (п ,т ). 7)A1(F1|A2. Так как g = gcd(i,j), то существуют а и Ь, такие, что i = a*g и j = b*g. Поэтому n = |a*g| и m = |b*g|. Таким образом, g = gcd (n,m) и п > 0, m > 0. 19—399
282 Глава 8 8) Ae{F4)A7. Так как m" = g и значение М назначается функции GCD, то GCD(i,j) = g. Таким образом, доказано, что GCD вычисляет значение наи- большего общего делителя чисел I и J в том случае, если он существует. 8.3.2. Завершение выполнения модуля Чтобы показать, что выполнение программного модуля дает верные результаты, недостаточно продемонстрировать его кор- ректность. Необходимо также показать, что выполнение моду- ля приведет в то место (точку) программы, где эти результа- ты получаются. Выполнение будет достигать заранее опреде- ленной точки, если, во-первых, точка принадлежит всем путям, проходящим через модуль, во-вторых, все циклы заканчивают- ся и, в-третьих, в этом модуле или в других, вызываемых им, не возникает исключительных ситуаций, попадание в которые приводит к нестандартному завершению выполнения модуля. В случае программы GCD существует только одна такая ситуа- ция: имеется в виду вызов функции, вычисляющей остаток от деления двух чисел с вторым аргументом, равным нулю. Дру- гих подобных функций модуль не использует. Все пути прохо- дят через ту точку программы, где формируется результат. Ос- тается проверить только условие завершения цикла. То, что цикл завершается, может быть показано с помощью анализа убывающей последовательности положительных целых чисел, связанных с циклом: п', п", п'", ..., Поскольку для некоторого k n(ft) = 0, обязательно произойдет выход из цикла. Процесс формального доказательства правильности прог- рамм очень сложен, в первую очередь для записи. Хотя утверж- дения можно записать так, что их правильность сможет прове- рить ЭВМ, эта процедура потребует очень много времени. Поиск утверждений, соответствующих каждой структурной единице схемы, труден и не может быть полностью автоматизирован. Любые ошибки при формировании утверждений приводят к ошибкам в доказательстве. Разработка методов формального доказательства — еще один способ проверки соответствия между текстом программы и спецификациями. Способы, позволяющие математически выра- жать значения переменных, дают возможность обнаруживать логические ошибки. Они также помогают проверять специфика- ции на полноту и согласованность. Запись формальных утвер- ждений для входа и выхода модуля может быть использована при подготовке документации для этого модуля.
Проверка правильности программ 283я 8.4. Оценки ошибок Тестирование предназначено для поиска ошибок, а не для демонстрации того, что для некоторых данных программа ра- ботает правильно. Тем не менее, чем больше ошибок найдено, тем меньше уверенности в надежной работе модуля, так как если найдено много ошибок, то, во-первых, их было много и, во-вторых, возможно, многие ошибки остались необнаруженны- ми. Поэтому необходимо разработать метод, позволяющий оце- нивать количество ошибок в модуле. Если приблизительное число ошибок известно, то, чем больше их найдено и исправле- но, тем больше уверенности в корректности модуля. 8.4.1. Намеренное внесение ошибок в текст Один из способов оценки числа ошибок заключается в пред- намеренном внесении ошибок в программу; при этом считается, что во время тестирования будет обнаружен один и тот же процент намеренно внесенных ошибок и реальных ошибок, име- ющихся в программе. Поэтому, когда проводится тестирование, необходимо уметь определять, сколько реальных и намеренно внесенных ошибок обнаружено. Пусть N — неизвестное число ошибок в модуле, S — известное число намеренно внесенных ошибок, п — количество найденных и исправленных ошибок и з — количество найденных и исправленных намеренно внесен- ных ошибок. Будем считать, что s/S=n/N. Тогда первоначаль- ное количество ошибок в модуле можно приблизительно оце- нить по формуле N—nS/s. После тестирования остается N—п = = n(S—s)/s ошибок, т. е., если все намеренно внесенные ошиб- ки найдены, имеется надежда, что и все первоначально суще- ствующие ошибки будут найдены. Степень уверенности в том, что существует не больше N ошибок в модуле, в который было намеренно внесено S оши- бок, а п и s ошибок соответственно того и другого типа было обнаружено при тестировании, определяется выражением W+1 conf (< N) = 2 W + S-H 44-s— 1 (ЛГ + $.|.2— k — s)' (8.14) Когда s=S, то n = N, и приходим к выражению ' conf N) — /л^ + s+i \ W4-S (8.15) которое представляет степень уверенности в том, что все ошиб- 19*
284 Глава 8 Таблица 8.8. Степень уверенности в обнаружении всех ошибок n=N con f (<W) 5 0 0,83 10 0 0,91 20 0 0,95 5 1 0,71 10 1 0,83 20 1 0,91 5 2 0,625 10 2 0,77 20 2 0,87 ки найдены. В табл. 8.8 приведены значения степеней уверен- ности, вычисленные по формуле (8.15), для разных значений намеренно внесенных и первоначально существующих'ошибок. Чем больше ошибок из числа намеренно внесенных было об- наружено, тем выше уверенность в надежности модуля. Но ’чем больше реально существующих ошибок обнаружено, тем вероятнее, что есть еще другие, которые пока обнаружить не удалось. Уравнение с__(N -|~ 1) conf N) /л । /.к 5'“ 1—conf(^A) ’ -полученное из уравнения (8.15), позволяет определить количе- ство ошибок, которые должны быть намеренно внесены в мо- дуль, чтобы получить необходимую степень уверенности в его надежности. Причем считается, что тестирование продолжается до тех пор, пока все намеренно внесенные ошибки не будут об- наружены. Например, можно считать, что обнаружение 5 оши- бок дает 90% уверенности в том, что все ошибки найдены, если в модуль было намеренно внесено 60 ошибок и в процессе тес- тирования все были обнаружены. Следует отметить, что необходимо вносить в модуль ошибки всех возможных типов, а именно: орфографические, пунктуаци- онные, синтаксические, логические, ошибки в размещении, в ти- пах данных и т. п. Можно ввести таблицу, в которой ошибки размещены в соответствии с их типами, и пользоваться ею. Можно случайно вносить ошибки в произвольным образом вы- бранные операторы модуля. ЕСЛИ В МОДУЛЕ ОБНАРУЖЕНЫ ОШИБКИ, .НЕОБХОДИМО ПРОВЕРИТЬ ЕГО ЕЩЕ РАЗ
Проверка правильности программ 285 8.4.2. Ошибки объединения модулей Фирмой IBM установлено, что в среднем одна ранее не об- наруженная ошибка появляется в каждых 100 операторах про- граммы. Большинство ошибок содержимся в сопряжениях мо- дулей и операторах ввода-вывода. Такого рода ошибки затра- гивают обычно сразу несколько модулей. После того как про- ведено тестирование отдельно каждого модуля, последние объ- единяются в систему. Экспериментально установлено, что в 90% всех новых модулей и в 15% старых необходимо вносить изме- нения. Приблизительно в 15% новых модулей и 6% старых сле- дует внести более 10 изменений. Если осталось D старых мо- дулей и сформировано М новых, число необходимых измене- ний N определяется выражением N = 2 (0.9/И+0,15D) + 23 (0.15М + 0.06D). (8.17) 8.4.3. Другие оценки ошибок Эксперименты показали, что число ошибок в неоттестиро- ванных программах пропорционально Е2/3, где Е — мера Хол- стеда. Коэффициент пропорциональности равен примерно 1/3200. В программах, прошедших стадии тестирования и от- ладки, это отношение сохраняется, но коэффициент пропорцио- нальности уменьшается. Различные формулы оценки количества ошибок не учитыва- ют вероятность внесения k новых ошибок при исправлении п старых. ТЩАТЕЛЬНО ОПИСЫВАЙТЕ ИЗМЕНЕНИЯ, ВНОСИМЫЕ В РАБОЧИЕ ПРОГРАММЫ Вероятность того, что новый тестовый прогон позволит об- наруживать ошибки, будет зависеть от процента оставшихся ошибок. Последние ошибки обнаружить труднее. Формула avg(7’„) = -l-2TLt <818> Л=0 позволяет установить среднее количество проверок, необходи- мых для обнаружения п ошибок. Здесь 0 — неизвестное значе- ние, которое может быть определено экспериментально. Исходя из формулы (8.18), получаем формулу п— 1 2 1/(АГ — А) Р aVS (Т’п) _ fe—о______ /О 1 Q\ “ avg(^) “ Л£1 ’ *-0
286 Глава 8 которая показывает, какой процент Р тестовых прогонов, необ- ходимых для обнаружения всех N ошибок, был сделан, когда в процессе этих прогонов было найдено п ошибок. Если га =10 по формуле (8.19) получим, что половина всех ошибок может быть обнаружена в первых 22% тестовых прогонов, необходи- мых для обнаружения всех ошибок. Процент прогонов возра- стает до 37, если требуется найти 7 из 10 ошибок, и до 66, если требуется обнаружить 9 ошибок из 10, т. е. третья часть време- ни затрачивается на обнаружение одной последней ошибки. Если в программе имеется 100 ошибок, то 90 обнаруживается за первые 44% тестовых прогонов. Этот закон, устанавливаю- щий, что эффект от тестирования уменьшается со временем,, позволяет сделать вывод о необходимости прекращать тестиро- вание в тот момент, когда оно становится экономически невы- годным. Кроме того, можно утверждать, что в отлаженном мо- дуле почти всегда остаются ошибки. НЕТ ПРОГРАММ БЕЗ ОШИБОК 8.5. Средства защиты программных систем С целью повышения надежности реализация системы про- водится так, чтобы ошибки во время ее эксплуатации легко об- наруживались. Кроме того, следует стремиться к полному уст- ранению ошибок. Несанкционированный доступ к аппаратному и программному обеспечению и данным может привести к сбою системы. Защита того места, где находится система, — наибо- лее важная предохранительная мера. Для защиты программно- го обеспечения наиболее часто используются, во-первых, паро- ли, позволяющие ограничить доступ к системе при работе в ин- терактивном режиме и доступ к файлам, во-вторых, возмож- ности назначения прав доступа к ресурсам системы пользовате- лям и программам пользователей и, в-третьих, методы шифро- вания тех данных, которые не имеют средств защиты от несанк- ционированного доступа или должны передаваться по незащи- щенным линиям связи. 8.5.1. Пароли Пароли — это секретные слова, которые либо выбираются пользователем, либо назначаются системой. Они используются для того, чтобы показать, чтб имеет право делать пользова- тель, которому присвоен определенный учетный номер. В отли- чие от других данных пароли не могут появляться на устрой- ствах вывода информации. Их нельзя хранить в системном жур- нале для того, чтобы различать пользователей, или в системе учета пользователей. В целях идентификации пользователю
Проверка правильности программ 287 должен быть присвоен несекретный учетный номер, который можно выводить на печать. Множество авторизованных активных паролей должно на- ходиться в таблице защиты, хранящейся в ЭВМ. Она исполь- зуется для сравнения паролей в процессе регистрации пользо- вателей. Таблица должна быть защищена от копирования, вы- вода на печать и неавторизованного доступа. Пароли необхо- димо часто менять. Каждый пароль должен состоять по край- ней мере из восьми символов. Если пароль содержит меньше восьми символов, его легко установить методом проб и ошибок. Пользователи, которые выбирают свои пароли самостоятельно, имеют склонность использовать фамилию, номер телефона и другие удобные для запоминания комбинации. Этого делать не следует, поскольку тот, кто знает пользователя и хочет узнать его пароль, имеет все основания начать поиск. 8.5.2. Права пользователей Пароли позволяют ограничить доступ к системе как к еди- ному целому. Их можно также использовать для ограничения доступа к файлам и другим ресурсам системы. Кроме того, дан- ные можно защитить, сделав их доступными только с помощью специального программного обеспечения, предназначенного для доступа. Программы пользователей по отношению к этому про- граммному обеспечению должны обладать определенными пра- вами. Наиболее ранней формой этого было использование спе- циальных команд супервизора, которые имели право применять либо некоторые пользователи, либо пользователи, находящиеся за определенными терминалами. Современные системы управ- ления информацией и СУБД снабжены специальными средства- ми доступа для упрощения доступа к данным. Они могут быть использованы как для защиты данных, так и для ограничения доступа к ним. Пользователь может иметь право на доступ к некоторым ре- сурсам системы для выполнения определенных действий. На- пример, существуют права на чтение, модификацию, копирова- ние, уничтожение данных или права на вызов и изменение прог- раммы, ее копирование, передачу прав на доступ к программе другому пользователю. Пользователю могут быть даны права наделять полномочиями других пользователей. Обычно сущест- вуют правила, регулирующие предоставление и отмену прав. Считается, что пользователями являются не только люди, но и программы. Информация о правах каждого пользователя хра- нится вместе с информацией о нем самом. Система назначения и передачи прав должна быть достаточно надежной и гибкой.
288 Глава 8 8.5.3. Шифрование Данные, передаваемые по обычным линиям связи, могут быть перехвачены. В этом случае защита доступа к данным невозможна, но данные сами по себе должны быть защищены. Так как все больше и больше фирм использует электронные средства связи, вероятность перехвата растет. Шифрование не- обходимо как для предотвращения доступа к данным, так и для их защиты от намеренного искажения. Шифрование или кодирование сообщений используется уже в течение нескольких столетий для Защиты от перехвата и расшифровки злоумышлен- никами. Для кодирования обычных текстов были разработаны многочисленные методы. Простейшим является метод, заклю- чающийся в замене каждого символа сообщения одним или не- сколькими символами. Если адресат имеет список, состоящий из пар «символ — заменяющая его последовательность», сооб- щение может быть легко расшифровано. Необходимость иметь кодовые списки, содержащие ключи к кодам, ставит под сом- нение защищенность кодов такого типа. Даже если кодирование и декодирование производит ЭВМ, ключи не могут быть защи- щены надежно. Для шифрования можно также применять методы хэширо- вания, если их использование не приводит к коллизиям и до- пустимо обратное преобразование. Если для замены символов обычного текста использовать числа, а затем возводить их в квадрат, декодирование не будет состоять только из обратной замены, а будет также включать математическую операцию извлечения квадратного корня. Процессы кодирования и деко- дирования являются основой криптографии. За достаточно большое время с помощью ЭВМ можно по- добрать ключ практически к любому коду. Предположим, что закодированное сообщение достаточно длинно. Тогда оно мо- жет быть проанализировано, и, если использовались методы .замены или хэширования, ключ почти наверняка будет опре- делен. ЭВМ позволяет перебрать все подходящие методы деко- дирования. Часто для предотвращения декодирования прихо- дится менять код. Для использования в вычислительной технике было предло- жено два основных метода: шифрование с общим ключом и стандарт шифрования данных (DES) Национального бюро стандартов США. Оба используют такие процедуры кодирова- ния, что невозможно осуществить декодирование за разумное время с привлечением ограниченных ресурсов. Обычно функ- ция для кодирования и обратная ей функция для декодирова- ния разрабатываются одновременно. Ключ, используемый для построения этих функций, не легко найти даже в том случае,
Проверка правильности программ 289 если есть как закодированное сообщение, так и исходный текст. Основное различие между двумя методами заключается в том, что стандартные коды создаются с единственной целью — защитить сообщения от декодирования людьми, не имеющими на это права. Правда, они позволяют также защищать сооб- щения от намеренного искажения. При шифровании с общим ключом защита сообщений осуществляется с помощью своевре- менного изменения процессов кодирования и декодирования. Так же как в других традиционных методах, при использова- нии DES управление кодированием осуществляет отправитель сообщения, который разрабатывает ключ и сообщает его поль- зователям. При использовании кодов с общим ключом управ- ление кодированием осуществляет тот, кто принимает сообще- ние. Он извещает других о способе кодирования сообщения. Оба метода используют две функции (процессы): Е и D. Пусть М и С — соответственно сообщение и его код. Тогда С=Е(М) и M=D(C). Сообщение кодируется с помощью функ- ции Е и декодируется с помощью функции D. Если сообщение сначала кодируется, а затем декодируется, первоначальный текст сообщения должен восстанавливаться, т. е. M=D(E(M)). Выбрав подходящие функции, можно сначала декодировать сообщение, а затем кодировать его. Тогда M=E(D(M)), и по- лучен другой вариант шифрования. Следовательно, возможна защита данных, передаваемых между двумя абонентами, когда одному известна функция Е, а другому D. При использовании стандартов шифрования данных ключ требуется для выполне- ния обоих процессов: Е и D; если один из процессов известен, можно определить ключ. При шифровании с общим ключом ключ нельзя определить, если известен только один из процес- сов: D или Е; также один процесс не может быть получен на основании знания другого. Стандарт шифрования данных. При преобразовании исполь- зуется 64-битовый ключ, включающий биты четности. Этот ключ применяется для кодирования блоков, состоящих из 64 символов. Процесс кодирования блока состоит в примене- нии определенным образом чередующихся операций переста- новки символов с использованием ключа и замены символов. Весь процесс повторяется определенное число раз. Возврат к исходному тексту производится с помощью обратных операций. Выполняемые операции не обязательно должны быть секрет- ными. В действительности они являются основными операция- ми ЭВМ, но, не зная ключа, декодирование осуществить прак- тически невозможно. Как и в обычных криптографических сис- темах, и отправитель, и адресат должны знать ключ. Аппаратные средства можно применять для того, чтобы со- кратить время кодирования и декодирования. Так как устрой-
290 Глава 8 ства, предназначенные для работы с шифрами, являются ча- стью ЭВМ, для их защиты надо использовать методы ограни- чения доступа и защиту местонахождения. Устройства кодиро- вания сообщений обычно предназначены для эксплуатации в течение длительного времени. Если защита на физическом уровне обеспечена, дополнительные меры защиты при смене кодов не требуются. Существуют некоторые сомнения, касаю- щиеся использования рассматриваемых кодов. Они связаны с тем, что, возможно, 64-битовый ключ не является достаточно длинным, чтобы не быть разгаданным злоумышленниками. Шифрование с общим ключом. Метод шифрования с общим ключом основан на построении специальных функций, значе- ния которых легко вычислить, но построить обратную функцию для которых достаточно сложно. В то же время задача фор- мирования как самой функции, так и обратной к ней из мно- жества простых функций должна быть разрешимой. Этот про* цесс должен быть секретным. Если функция кодирования Е та- кова, что функция декодирования D не может быть определе- на, если Е известна, то нет необходимости организовывать за- щиту Е. Если функции кодирования двух абонентов А и В не- секретны, то А может послать В сообщение в форме Вв(М), которое В сможет декодировать, используя DB. Кроме абонента В, никто не может декодировать это сообще- ние, поскольку никто не знает функции DB. Послать ответ або- нент В может в форме ЕА(М). Далее, если все функции имеют ту же область значений, то А может послать сообщение в ви- де C = EbDa(M), а расшифровать его с помощью M=EADB(C). Использование абонентом А секретного процесса при кодиро- вании сообщения, направляемого В, служит гарантией того, что сообщение не будет искажено. Идентификация исходного текста необходима тому, кто принимает закодированное сообщение. Абонент А может также послать сообщение в виде C=DAEB(M), которое декодируется с помощью M=DBEA (С). Этот процесс будет подтверждать подлинность сообщения и фактически слу- жит доказательством его подлинности уже на третьем этапе, поскольку может быть показано, что ЕА (С) =ЕВ(М). Сообще- ние С может быть закодировано только абонентом А и может быть предназначено только абоненту В. При использовании DES и метода шифрования с общим ключом остаются нерешенными следующие проблемы: во-пер- вых, неизвестен метод определения, является ли данный код сложным для раскрытия; во-вторых, неизвестен метод генерации группы кодов, все элементы которой сложны для раскрытия.
Проверка правильности программ 291 8.6. Качество документации Тестирование проводится как во время разработки програм- мы, так и в процессе ее модификации. В обоих случаях необ- ходимо, чтобы документация отражала текущее состояние прог- раммы. При документировании должна быть учтена возмож- ность существования более чем одной версии программы. В до- кументацию необходимо вводить тестовые данные и результа- ты тестирования для каждой версии программы, причем тестовые прогоны должны быть осуществлены заново, если в программу внесены изменения. ПРИ ПОДГОТОВКЕ ДОКУМЕНТАЦИИ УЧИТЫВАЙТЕ РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ Для удобства сопровождения может оказаться полезным учет всех изменений, которые вносятся в систему. Тогда, если во время модификации в систему вносятся новые ошибки, их локализация облегчается. Как уже говорилось в гл. 5, аргументация, касающаяся вы- бора алгоритма решения задачи, должна найти отражение в документации. Кроме того, следует приводить данные, оказы- вающие влияние на надежность алгоритма, а именно допусти- мые диапазоны значений входных переменных, уровни возмож- ных ошибок и т. п. Если применяются фиктивное выполнение программы или формальные методы проверки правильности, их описание долж- но стать частью документации. При сопровождении программы очень важным является знание тех категорий данных, для об- работки которых предназначена программа, и соотношений, связывающих эти данные. 8.7. Упражнения 1. С помощью ЭВМ вычислите корни квадратного уравнения, используя следующие соотношения: а) х = (—b 4- УЬ2 — 4ас)/(2а) ; б) х — —Ь/(2а) 4- УЬ2/(4а2) — с/а, в) х = — 2c/(b 4- VЪ2 — 4ас). Сравните ответы и объясните, почему они разные. ^3 2. Используя ЭВМ, вычислите значения sin(x)=x— ~—F~+~+... для 3| 5! 7| х3 , х5 3! ' 51 10, 100, 1000. Какими должны быть ответы? Почему они не такие? 3. Используя ЭВМ, вычислите сумму четырех чисел по следующим двум формулам: а) ((а + с) 4" б) а + (Ь 4- (с 4~ d)) Для различных действительных чисел, больших и малых, положительных и от- рицательных. Сравните точность методов вычислений.
292 Глава 8 4. Напишите программу, определяющую, могут ли три числа являться сто- ронами треугольника. Прогнать ее со следующими входными данными: 3.0 4.0 5.0 0.3 0.4 0.5 2.0 3.0 5.0 0.2 0.3 0.5 1.0 1.33333 1.66667 5. Проанализируйте изложенные в конце разд. 8.1.3 правила, предназна- ченные для уменьшения эффекта распространения ошибок. Объясните, почему эти правила действительно помогают. 6. Предположим, что десятичные числа преобразуются в двоичные с по- мощью восьмибитовой ЭВМ и хранятся в виде чисел с фиксированной точкой (точка находится слева от числа). Запишите, как в ЭВМ будут представ- лены десятичные числа 0.1; 0.3 и 0.4, если используются округление и отбра- сывание младших разрядов. Каковы десятичные значения ошибки в случае округления и отбрасывания при вычислении 0.34-0.1—0.4? Определите верх- нюю границу ошибки и относительную ошибку при вычислении значения вы- ражения х+у—z в обоих случаях. 7. Для неверной программной функции, представленной на рис. 8.2, раз- работайте тестовые данные для анализа: а) путей, б) структур управления, в) ветвлений, г) специальных значений. Определите, какой способ тестирования не позволит обнаружить ошибки, допущенные при программировании функции. Если существуют ошибки, не обнаруженные после применения всех вышеописанных способов тестирования, предложите метод их поиска. 8. Разработайте тестовые данные для задач: а) о треугольниках и квадратах (пример 5.15); б) о нумерации с использованием римских чисел (пример 7.7 или 7.8). Аргументируйте свой выбор тестовых данных. 9. Используйте метод фиктивного выполнения для тестирования функции, программа которой приведена на рис. 8.2. Проследите за всеми шагами этого процесса. Оцените эффективность метода для обнаружения ошибок. 10. Проанализируйте методы, используемые вашей ЭВМ для вычисления значений основных математических функций.
Глава 9 ОПТИМИЗАЦИЯ ПРОГРАММ Большинство методов проектирования и реализации прог- рамм предназначено для повышения эффективности работы! программиста. Тщательное планирование программы уменьша- ет суммарное время, затрачиваемое на программирование. Разбиение на модули приводит к уменьшению времени отладки. Хорошая документация и структурирование программ и данных позволяют экономить время, затрачиваемое на модификацию программы. Качественная документация в конечном счете всег- да приводит к экономии времени. Современные системы управ- ления базами данных и редактирования предназначены для.- повышения эффективности работы пользователей. Это достига- ется с помощью автоматизации многих этапов процесса прог- раммирования. Эффективность работы программиста и пользователя непо- средственно связана с эффективностью программы. Чаще всего- повышение эффективности работы ЭВМ достигается за счет усо- вершенствований, вносимых в аппаратное и базовое програм- мное обеспечения. Однако и проектировщик, и программист тоже имеют возможности для улучшения программы. Эффек- тивность программы может трактоваться по-разному. Напри- мер, можно потребовать, чтобы программа работала быстро, или занимала как можно меньше памяти, или как можно реже- обращалась к данным, расположенным на дисках, или, нако- нец, быстро обслуживала терминальные устройства. Одновре- менно выполнить все эти требования нельзя. В общем случае экономия памяти приводит к увеличению времени работы про- граммы, и, наоборот, уменьшение времени работы приводит к использованию дополнительной памяти. Главным средством, позволяющим проводить оптимизацию, является системное программное обеспечение. Каждый компилятор проводит оптимизацию, руководствуясь определенными критериями. Так, компиляторы, предназначен- ные для использования студентами и снабженные дополнитель- ными средствами отладки и контроля ошибок, позволяют умень- шить время, затрачиваемое на отладку^ за счет увеличения вре- мени компиляции. Стандартные компиляторы работают быстрее, поскольку меньше времени тратят на контроль оши-
294 Глава 9 бок и оптимизацию кода. Оптимизирующие компиляторы тре- буют больше памяти и работают медленнее, чем стандартные, но создают объектную программу, занимающую меньше места и работающую быстрее и эффективнее. Многие возможности, предусмотренные в компиляторе (например, контроль данных во время выполнения программы), могут использоваться или не использоваться в зависимости от желания пользователя. Если оптимизирующий компилятор не предусмотрен, прог- раммист может вставить в исходный текст программы операто- ры, позволяющие провести необходимую оптимизацию. Так же как и в случае любых других изменений программы, измене- ния, направленные на повышение эффективности, либо должны быть частью первоначального проекта, либо должны выпол- няться после того, как разработка программы закончена и про- ведена ее отладка. Следует отметить, что, хотя первый вариант реализовать легче, чем второй, все-таки эффективность прог- раммы на этапах проектирования и реализации не следует рассматривать в качестве главной цели. Главная цель — это решение задачи, а не поиск наилучшего решения. СТРЕМИТЕСЬ СДЕЛАТЬ ПРАВИЛЬНО РАБОТАЮЩУЮ ПРОГРАММУ ДО ТОГО, КАК ВНОСИТЬ В НЕЕ ИЗМЕНЕНИЯ 9.1. Экономия памяти В программном модуле используются три типа памяти: внешняя память (магнитные диски или ленты) для хранения данных, внутренняя память для данных (массивы и буферы) и память для размещения программы. 9.1.1. Данные на внешних носителях Память, которая требуется для размещения последователь- ных файлов, можно сэкономить либо делая записи короче, ли- бо объединяя их в блоки. Для хранения чисел в двоичной фор- ме требуется меньше памяти, чем для хранения их в символь- ной форме. Если ЭВМ работает с числами разной длины, для экономии памяти их следует представлять в упакованном виде. Большинство часто используемых целых чисел лежит в диапа- зоне 0—4095, поэтому для их хранения достаточно небольшого участка памяти. Символы при размещении следует упаковы- вать. Нельзя форматировать и редактировать данные, а также использовать заполнители, если память, где они размещены, предназначена только для чтения. ЭКОНОМЬТЕ ПАМЯТЬ ПРИ РАЗМЕЩЕНИИ ДАННЫХ После каждой физической записи на магнитной ленте пред- усмотрен свободный участок. Если информация размещается
Оптимизация программ 29- на ленте с плотностью ~300 бит на 1 см, 80 символов займут только ~0,3 см. Но длина свободного участка может состав- лять ~1,3 см. В этом случае можно сказать, что магнитная лента используется в основном для хранения свободных участ- ков, а не данных. Если данные сблокировать так, чтобы одна физическая запись состояла из 10 логических, то информатив- ные участки будут занимать ~2,5 см. При использовании памяти на магнитных дисках физические записи размещаются начиная с границ секторов. Если запись занимает только часть сектора, оставшаяся часть не исполь- зуется; если же размер записи больше размера одного сектора, запись занимает следующий сектор. Для эффективного блоки- рования записей программист должен определить оптимальный размер блока, исходя из того, сколько информации помещается на одной дорожке данного устройства. Если планируется использовать непоследовательные файлы, надо предусмотреть дополнительное пространство для их рас- ширения. Уменьшение размера записи ведет к уменьшению этого пространства. Единственным способом уменьшения необ- ходимого для размещения файла объема памяти (включая память для расширения файла) является либо более точная оценка требуемой памяти, либо усовершенствование алгоритма хэширования. Если файлы созданы, попытки сэкономить память для их размещения приведут к перестройке структуры файлов. Если файлы не были сблокированы, их блокирование позволит раз- местить информацию на диске или ленте с более высокой плот- ностью. Это позволит не только сэкономить внешнюю память, но и приведет к уменьшению времени доступа, поскольку блок считывается и записывается целиком. Правда, при этом потре- буется дополнительная память для размещения буферов. В слу- чае применения дисковой памяти наименьшие потери будут тогда, когда один блок занимает целиком всю дорожку. Если разработаны модули, позволяющие изолировать структуры дан- ных, изменение структуры файла потребует минимальных из- менений программы. Если файл используется несколькими программными модулями, структуру файла не следует изме- нять до тех пор, пока все применения файла не будут хорошо документированы. ПРОВОДИТЕ БЛОКИРОВАНИЕ ДАННЫХ 9.1.2. Данные в оперативной памяти Некоторые из описанных выше идей можно применить к данным, находящимся в оперативной памяти. Хранение симво- лов в упакованном виде и чисел в виде двоичных полуслов при-
296 Глава 9 ведет к экономии памяти, но эта экономия будет слишком ма- ла, если речь идет о небольших областях памяти, занимаемых данными. При использовании больших разряженных массивов данных необходимо разработать специальные методы, предна- значенные для хранения таких массивов. Если программа рабо- тает со стеками, последние должны размещаться так, чтобы их концы не перекрывались. Выбор эффективной дисциплины за- полнения стеков приводит к экономии памяти. Сбалансирован- ные двоичные деревья можно хранить, не указывая связей между вершинами. Изменение структур хранения эквивалентно ^выбору нового алгоритма. Если программа правильно разделе- на на модули, такого рода изменения отразятся только на не- скольких модулях, т. е. будут в той или иной мере локализо- ваны. В общем случае нецелесообразно использовать одну и ту же область памяти для размещения значений нескольких эле- ментов данных. Модули, где это все же используется, сложнее для сопровождения. Исключение может быть сделано лишь для буферов файла. Если логические записи достаточно велики или сблокированы в физические записи большого размера, буферы требуют значительно больше памяти, чем может быть сэконом- лено с помощью методов оптимального размещения данных в оперативной памяти. В этом случае полезно, чтобы несколько -файлов использовали один и тот же буфер. Но это возможно не во всех языках программирования. Поскольку такая ситуа- ция порождает ошибки, она должна быть подробно описана при составлении документации. Кроме того, необходимо тщательно следить за тем, чтобы файлы, работающие с одним и тем же (буфером, не использовались одновременно. 9.1.3. Текст программы Создание виртуальных операционных систем способствовало снижению требований к экономии оперативной памяти, зани- маемой программой. В любой ЭВМ может оказаться недоста- точно быстродействующей памяти, предназначенной для разме- щения программы. В виртуальных операционных системах . большие программы автоматически сегментируются. Сегменты либо находятся в оперативной памяти, либо передаются в нее из внешней памяти по мере необходимости. Если нужно осво- бодить память для очередного запрошенного сегмента, один или несколько сегментов передаются из оперативной памяти во внешнюю. Неудачная сегментация может привести к «пробук- совке». Например, если управление циклом располагается на одной странице, а тело цикла — на другой, возможен вариант, когда эти страницы поочередно считываются из внешней памяти л сбрасываются в нее. Содержимое дорожки страничных пре-
Оптимизация программ 297 рываний показывает, насколько в каждом конкретном случае важна эта проблема. «Пробуксовку» можно уменьшить, если, во-первых, размеры модулей сделать равными размеру страни- цы и, во-вторых, размещать модули в порядке их использова- ния. Для улучшения страничной организации памяти в вирту- альной системе необходимо: • программные модули делать небольшими; • операторы управления циклом и тело цикла располагать ближе друг к другу; • располагать вместе модули, часто вызывающие друг Друга; • программы, которые вызываются одной и той же груп- пой модулей, размещать вместе; • данные размещать вместе с модулями, которые их ис- пользуют; * не использовать общих областей; • большие области данных располагать в том порядке, в котором они обрабатываются. В невиртуальных ЭВМ обычно предусматриваются средст- ва, пользуясь которыми программист может сегментировать программу и организовать оверлейную структуру. Использова- ние оверлейной организации позволяет нескольким сегментам поочередно занимать одну и ту же область памяти: новый сег- мент загружается в эту область памяти только в случае его вызова. Основой оверлейной структуры является иерархическая схема модулей, которая сначала разделяется на неперекрываю- щиеся поддеревья. Затем главный модуль, разделяемые обла- сти данных, используемые несколькими модулями вспомога- тельные программы и, возможно, подмодули высшего уровня помещаются в управляющий оверлейный сегмент, который ос- тается в оперативной памяти в течение всего времени выполне- ния программы. Группы модулей, представленных различными поддеревьями, поочередно занимают одну и ту же память. Различные операционные системы поддерживают разное число уровней поддеревьев. Секции управления оверлейной структу- рой (рис. 9.1, а) можно описать в управляющей части програм- мы следующим образом: MAIN (А, В, D) LEVEL ONE (С, Е, F, Н) LEVEL ONE (G, I, J) На рис. 9.1,6 приведена схема сегментации еще одной програм- мы. Отметим, что модуль Н вызывается как модулем Е, так и модулем F, а модуль I вызывается модулями Е и G. Тогда либо копии модулей Н и I следует разместить в обоих сегмен- тах, либо модули Н и 1 следует продвинуть вверх по дереву, так чтобы они оказались в оверлейном сегменте, который по- 20—399
Й98 Глава 9 б Рис. 9.1. Сегментация программы. а — двухуровневое дерево сегментов: корневой сегмент и оверлейные сегменты; б — трех* уровневое дерево сегментов: корневой сегмент н два уровня оверлейных сегментов. крывает оба сегмента, использующие модули Н и I. Следова- тельно, Н можно разместить вместе с С, а I вместе с Л. Тогда оверлейные секции могут быть расположены следующим обра- зом: MAIN (А, В, D, I) LEVEL ONE (С, Н)
Оптимизация программ 299 LEVEL TWO (Е, J) LEVEL TWO (F) LEVEL ONE (G, K) Схема расположения приведена на рис. 9.2. Наименование оверлейных сегментов производится операционной системой и зависит от числа допускаемых уровней. Расположение сегмен- тов всегда согласуется с их положением в оверлейном дереве. Поддеревья необходимо выбирать таким образом, чтобы число взаимных ссылок было минимальным. Если из тела цик- ла, содержащегося в программе X, вызывается программа Y, то по возможности X и Y следует поместить в один и тот же сегмент. Если программа X вызывает программы Y, Z и W, то программы Y, Z и W должны быть в одном и том же сегменте. Рис. 9.2. Оверлейная структура. Рис. 9.3. Распределение памяти для оверлейной программы. а — корневой сегмент; б — первый уровень, вызов моду- ля С; в — второй уровень, вызов модуля Е; г — второй уровень, вызов модуля F; д — первый уровень, вызов мо- дуля G. Программы, которые замещают друг друга, будут попадать в одну и ту.же область памяти. Поэтому следует стремиться к тому, чтобы размеры этих программ были приблизительно равными. На рис. 9.3 показано распределение памяти для овер- лейной структуры, изображенной на рис. 9.2. Каждый сегмент помещается в память в момент его вызова и остается там до тех пор, пока управление не будет возвращено вызывающему модулю и память не потребуется другому сегменту. Только под- деревья, которые располагаются в параллельных ветвях иерар- хической схемы программы (рис. 9.1), могут находиться в па- мяти одновременно. Поэтому модуль, находящийся в памяти, может вызвать другие модули, сам оставаясь в памяти, только если эти другие модули, во-первых, принадлежат сегменту, на- ходящемуся в дереве сегментов выше искомого модуля, и, во- вторых, находятся в следующих лежащих ниже по отношению 20*
800 Глава 9 к искомому модулю сегментах. Если вызываемых модулей нет в памяти, они сразу же в нее помещаются. Существует лишь одно ограничение при использовании овер- лейных структур: программы, не входящие в главный сегмент, должны быть повторно используемыми, поскольку каждый раз, когда они помещаются в память, они будут находиться в пер- воначальном состоянии. Любые изменения значений, происшед- шие во время выполнения программы, не сохранятся до ее сле- дующего вызова, если в промежутке между двумя вызовами этой программы на ее место загружалась другая программа. Виртуальные ЭВМ не поддерживают повторно используемых программ, так как программист не участвует в управлении сег- ментацией. Это может считаться преимуществом, но фактичес- ки это снижает скорость обработки, так как кроме загрузки модулей из внешней памяти в оперативную требуется обратный процесс. При использовании оверлейных структур надо помнить, что в различных языках программирования области данных в па- мяти размещаются по-разному. В Коболе все области данных размещаются в управляющем сегменте. В Фортране локальные данные помещаются вместе с каждым модулем, поэтому в мо- мент очередной загрузки модуля значения локальных перемен- ных будут не определены. Общие области данных в соответст- вии с утверждениями типа COMMON могут быть размещены в сегменте самого высокого уровня (из тех сегментов, что со- держат модули, которые используют эту область данных). По- этому важно, чтобы COMMON-область, разделяемая двумя мо- дулями, относящимися к разным поддеревьям, была определе- на в модуле, который покрывает оба первых модуля, даже если эта область там и не нужна. В некоторых версиях Фортрана требуется, чтобы все COMMON-области были определены в главном модуле. Для получения эффективной оверлейной структуры необхо- димо следующее: • использовать большие по объему сегменты и небольшое число сегментов; • группировать модули так, чтобы минимизировать коли- чество межсегментных связей; • помещать в один сегмент модули, которые часто вызы- вают друг друга; * группировать модули в сегменты в соответствии со струк- турой иерархических поддеревьев; • размещать вспомогательные программы в сегменте, кото- рый покрывает все модули, вызывающие эти программы; • располагать модули, которые обрабатывают ошибки, ини- циируют или завершают выполнение, в отдельных сегментах, расположенных на нижних уровнях иерархии;
Оптимизация программ 301 • размещать разделяемые области данных или модули, ко- торые не являются повторно используемыми, в сегментах, по- крывающих все модули, в которых используются эти области или вызываются модули, не являющиеся повторно используе- мыми. Страничная организация и оверлейные структуры позволяют экономить быстродействующую память, а удачное распределе- ние модулей по сегментам или страницам — процессорное время. В Алголе используется динамическая активация процедур, во многом аналогичная страничной организации и оверлейному управлению. Процедуры размещаются в памяти только тогда, когда они нужны. Каждая процедура загружается столько раз, сколько раз она вызывается. Если процедуру необходимо записать обратно на диск, не возвращая в исходное состояние, при описании переменных этой процедуры программист должен использовать атрибут own. Этим атрибутом должны быть снабжены переменные, значения которых сохраняются между вызовами. В некоторых версиях языка ПЛ/1 используется ана- логичный метод динамической активации для блоков. Если бло- ки имеют локальные переменные, после выполнения эти блоки вновь записываются на диск, т. е. в программах, написанных на ПЛ/1, экономия памяти достигается путем использования блоков. Если же время для данной задачи является более важ- ной характеристикой, рекомендуется применять DO-группы вместо блоков. 9.2. Экономия времени Использование несблокированных файлов и страничной ор- ганизации для экономии памяти может привести к увеличению времени выполнения программы из-за низкого быстродействия устройств ввода-вывода. Если переопределить модули для предотвращения «пробуксовки» страниц и сблокировать дан- ные, можно уменьшить время выполнения программы. К эконо- мии времени приводит также размещение программ и данных на различных дисках. Если язык программирования допускает одновременное выполнение программы и операций доступа к данным, это также ведет к ускорению счета. 9.2.1. Доступ к данным При работе с магнитными лентами много времени тратится впустую на остановку и повторный запуск ленты. Так как ско- рость устройств ввода-вывода ниже скорости выполнения вы-
302 Глава 9 числений, лента может постоянно находиться в движении. Воз- врат и перемотку ленты следует исключать всегда, когда это возможно. Если информация с ленты должна считываться не- сколько раз или сначала должна производиться запись, а по- том чтение, обработка ленты сначала в прямом, а затем в об- ратном направлении эффективнее перемотки. Если информа- ция на ленте должна быть изменена, копирование содержимого ленты с включением изменений более эффективно, чем модифи- кация записи в старом наборе. При работе с дисковой памятью основные задержки связаны с необходимостью передвигать головку записи-считывания на другую дорожку. Если под файл удается выделить один или несколько цилиндров, перемещения головки записи-считывания минимальны. Увеличивая размеры блоков, можно добиться уменьшения числа обращений к разным дорожкам при условии, что нужные записи находятся в одном и том же блоке. Орга- низуя доступ к записям в соответствии с порядком их располо- жения на диске, удается минимизировать общее время доступа, которое определяется в этом случае только скоростью враще- ния диска. Для эффективного проведения модификации файла с пря- мым доступом .вспомогательные записи надо упорядочить так, чтобы одного обращения к диску было достаточно, когда на каждую запись основного файла приходится несколько вспомо- гательных, и одного обращения к диску хватило бы для чтения сблокированных записей главного файла для группы вспомога- тельных записей. Если записи не сблокированы или корректи- рующие записи поступают в случайном порядке, система снача- ла использует одно обращение к диску для определения нуж- ной дорожки, а затем по крайней мере еще одно обращение для обнаружения искомой записи на этой дорожке. В том слу- чае, когда системные средства не позволяют осуществлять бло- кировку файлов с прямым доступом, время, затрачиваемое на доступ, можно уменьшить, если использовать программы бло- кирования и разблокирования записей. Если мультипрограммный режим работы не поддерживается ЭВМ, определение местонахождения записи перед ее. считыва- нием дает возможность сэкономить время. В мультипрограм- мной среде это может привести к лишним затратам времени, поскольку в промежутке между тактами выполнения команд определения местонахождения и чтения другой пользователь может обратиться к тому же пакету дисков. По той же причи- не в мультипрограммной среде размещение файла на одном или нескольких цилиндрах и разделение программ управления дисками могут не привести к уменьшению времени доступа.
Оптимизация программ 303 9.2.2. Организация доступа к программам Время, необходимое для доступа к программе оверлейной структуры или при страничной организации памяти, зависит от размеров модулей и от того, как они упорядочены. Желатель- но, чтобы модуль располагался на одной странице. Данные должны размещаться вместе с программами, которые их ис- пользуют. Если массивы слишком велики и не помещаются на одной странице, обращение к их элементам должно осуще- ствляться в том порядке, в котором они размещаются в памя- ти (для Фортрана — по столбцам, для других языков — по строкам). Оверлейные структуры и страничная организация памяти предназначены для экономии оперативной памяти. Но чтобы не привести к дополнительным затратам времени, они должны быть тщательно разработаны. 9.3. Повышение эффективности программ Анализируя все множество средств, используемых для опти- мизации программы, можно сказать, что наибольшей экономии времени можно достичь, эффективно используя внешние уст- ройства, поскольку именно небольшое быстродействие устройств ввода-вывода часто ведет к увеличению времени 'выполнения программы. Это связано с тем, что механические устройства работают значительно медленнее электронных. Однако, если программу надо сделать более эффективной, более вероятно, что к успеху приведет либо модификация алгоритма, либо вы- бор другого алгоритма, а не совершенствование самой програм- мы. Программы для научных расчетов в большей степени, чем любые другие программы, зависят от быстродействия процес- сора. В случаях когда все средства уже использованы, но нуж- ный результат не получен, может оказаться необходимой опти- мизация текста программы. 9.3.1. Выбор алгоритма Как показано в гл. 5, часто для решения одной и той же задачи можно использовать несколько алгоритмов, которые мо- гут иметь разную математическую сложность и требовать раз- личных вычислительных ресурсов. Если, например, выполняет- ся сортировка, ее можно быстрее выполнить в оперативной па- мяти, а не на ленте или диске. Также можно помещать данные порциями в оперативную память и там их сортировать. Эти операции требуют достаточно большого объема памяти для хранения данных. Разработано много алгоритмов для проведе- ния сортировки в оперативной памяти. В книге Кнута (см. ссылку на стр. 154 гл. 5) показано, что одни алгоритмы эко-
304 Глава 9 номят время за счет памяти, а другие, наоборот, экономят па- мять, но увеличивают время. Сложность такого рода алгорит- мов сортировки различна. Даже если два алгоритма имеют одну и ту же сложность, один может быть предпочтительнее другого из-за того, что в большей степени соответствует дан- ным, имеет меньший коэффициент пропорциональности в фор- муле, описывающей сложность, или снабжен большим числом эвристик, позволяющих ускорять сортировку. Выполнение наи- более простого алгоритма часто занимает очень много времени. СОВЕРШЕНСТВУЙТЕ алгоритм Если программа работает в течение долгого времени и не выдает результатов, она, вероятно, зациклилась. Возможно также, что выбранный алгоритм не соответствует конкретным данным. Так, один алгоритм сортировки может работать быст- рее другого, если данные частично упорядочены. В пакетах программ, предназначенных для научных расчетов, для вычис- ления функции sin(x) используются полиномы Чебышева, а не разложение в ряд, поскольку при больших значениях углов ря- ды сходятся слишком медленно и не только затрачивается много времени на вычисления, но результирующая погрешность больше из-за эффекта распространения ошибок. Для малых значений углов использование членов разложения в ряд дает удовлетворительные результаты. Для вычисления квадратных корней применяется итерационный метод, который и по точ- ности, и по времени реализации предпочтительнее метода, ис- пользующего логарифмы и экспоненты. Выбрав удачное на- чальное приближение, можно уменьшить общее число итера- ций. Можно найти хорошее решение задачи преобразования таблицы решений в дерево решений, затратив на это меньше времени, чем необходимо для поиска оптимального решения. Экономия времени возможна и тогда, когда алгоритм для большинства входных данных работает лучше, чем предполага- лось, даже если в некоторых случаях его производительность ниже ожидаемой. То, как запрограммирован алгоритм, в мень- шей степени влияет на эффективность программы. В современ- ных ЭВМ вызов подпрограмм и возврат из них осуществляется неэффективно, поскольку для проведения этих операций необ- ходимо выполнить много машинных команд. Если функция должна вызываться много раз, можно сэкономить время, выз- вав ее только один раз и используя первое значение этой функ- ции для определения всех остальных ее значений. Поэтому итерационная форма алгоритма предпочтительнее рекурсивной формы. Значительно проще организована работа с процедурами в ЭВМ, снабженных аппаратно-реализованными стеками. В таких ЭВМ вызовы и возвраты осуществляются аналогично обычным вычислениям, использующим стеки для аргументов и
Оптимизация программ 305 промежуточных результатов. Выгоднее включать тексты под- программ в циклы и ветви условных операторов, чем вызывать их. Но, выбирая итерационный метод, а не рекурсивный, даже тогда, когда язык программирования допускает использование рекурсий, можно сделать программу менее понятной и сложной для сопровождения. Аналогично изменение программы с целью уменьшения числа вызовов может сделать ее слишком длинной и сложной для понимания. Процедура выбора алгоритма — это, по существу, определе- ние того, что следует и чего не следует включать в алгоритм. В результате определяются общие контуры алгоритма. Пред- положим, что программа в процессе выполнения часто опериру- ет с календарем. В частности, она тратит время на то, чтобы определить, какие годы, делящиеся на 100 и не делящиеся на 400, являются високосными. На практике эффективнее было бы использовать системный параметр, позволяющий через каждые четыре года добавлять один день к февралю, чем каждый раз проверять, делится ли номер года на 4. В процессе выбора ал- горитма следует принимать во внимание его сложность и сте- пень общности. 9.3.2. Оптимизация текста программы Поскольку быстродействие современных ЭВМ очень велико, обычно не имеет смысла экономить время путем оптимизации текста программы. Перед тем как изменить рабочую програм- му, необходимо тщательно проверить, не приведет ли уменьше- ние времени выполнения программы на несколько миллисекунд к значительному увеличению средств, необходимых для отлад- ки новой программы. Используя информацию операторов прог- раммы, обращающихся к таймеру, или данные о времени вы- полнения различных частей программы, можно выделить наи- более часто выполняемые части программы. После этого легко оценить, какая часть времени может быть сэкономлена. В пер- вую очередь следует оптимизировать наиболее часто выполняе- мые части программы. ОПТИМИЗИРУЙТЕ НАИБОЛЕЕ ЧАСТО ВЫПОЛНЯЕМЫЕ ЧАСТИ ПРОГРАММ При написании программы часто нужно найти компромисс между временем, памятью и ясностью представления. Нельзя жертвовать ясностью программы, для того чтобы сэкономить не- сколько секунд на выполнение. Даже несмотря на то, что го- товая программа редко оптимизируется программистом, необхо- димо хорошо знать методы оптимизации, поскольку это позво- лит лучше понять работу оптимизирующих компиляторов. Мно- гие преобразования, выполняемые оптимизирующими компиля-
306 Глава 9 Таблица 9.1. Эффективность выражений Выражения Мера Хол- стеда E Число операторов ♦ ,/ • • 1) D = A-f-B; C=D*D—D-f-D 237,2 2 3 1 0 2) С = А»А4-А»В-+-А*В-|-В*В 197,7 1 3 4 0 3) С = А » (А + В) + В * (А + В) 196,5 1 3 2 0 4) D = A-f-B;E = A + B;C = D»E 194,0 3 2 1 0 5) D = A4-B;C = A*D+B.D 180,0 2 2 2 0 €) С ~= А ♦ A —j— 2 * А * В "4“ В ♦ В 126,3 1 2 4 0 7) С = А + В; С = С*С 123,5 2 1 1 0 8) С = (А + В).(А+В) 102,9 1 2 1 0 9) D = A + B; C = D.D 99,0 2 1 1 0 10) D = A -j- В; С — D 2 83,7 2 1 0 1 П) C = (A-f-B)»»2 48,0 1 1 0 1 торами, могут быть испбльзованы программистом, применяю- щим язык высокого уровня, для того, чтобы создать более эф- фективную программу. Арифметические выражения. В табл. 9.1 приведено несколь- ко групп операторов присваивания, вычисляющих одно и то же значение. Эти операторы располагаются в порядке убывания сложности, определяемой мерой Холстеда Е. В таблице также указано число базовых операций, используемых в каждой груп- пе. Мера Холстеда Е служит характеристикой ясности програм- мы, причем значения этой меры довольно хорошо согласуются с интуитивно определяемой относительной ясностью выраже- ний. Скорость вычисления выражения зависит от числа содер- жащихся в нем операций каждого типа. Для присваивания значения переменной необходимо один раз обратиться к памя- ти, в то время как для выполнения арифметической операции может потребоваться одно-два или ни одного обращения к па- мяти. В среднем операция присваивания выполняется быстрее, чем сложение или вычитание. В свою очередь сложение и вы- читание осуществляются быстрее, чем умножение и деление. Самой медленной является операция возведения в степень. Воз- ведение в целую степень выполняется путем нескольких умно- жений. Поэтому можно сказать, что возведение в степень реализуется с помощью программы, включающей одно умно- жение, наращивание значения переменной цикла и проверку этого значения. Выражения 1—5 в меньшей степени похожи на выражения, предназначенные для вычисления (а+6)2, чем выражения 6— 11. Характерно, что наиболее эффективные выражения 6—10 имеют самые низкие значения меры Холстеда Е. Как показы- вают приведенные выше примеры, выражения можно сделать
Оптимизация программ 307 более эффективными, не жертвуя в то же время их ясностью, с помощью следующих приемов: объединения подобных членов, факторизации, устранения общих подвыражений, уменьшения числа сложных операторов, исключения различных типов дан- ных, использования целых чисел в качестве показателей степе- ни и изменения порядка следования членов в выражении. Даже частичная факторизация, проводимая по схеме Горнера, повышает эффективность вычисления полинома ax3 + bx3 + cx+d, поскольку выражение, используемое для непосредственного вы- числения полинома А*Х**3 4- В*Х**2 4-С*Х 4-D, может быть преобразовано к виду Х*(Х*(А*Х -|- В) 4- С) + D. Первое выражение содержит две операции возведения в сте- пень, три операции умножения и три операции сложения. Вто- рое выражение содержит три операции умножения и три опера- ции сложения. Поскольку первая форма записи полинома яв- ляется наиболее распространенной, то, несмотря на большую эффективность второй формы, первая форма все же более предпочтительна. Устранение общих подвыражений, появляющихся в одном или соседних операторах, в настоящее время выполняется оп- тимизирующими компиляторами. Все остальные случаи появле- ния общих подвыражений могут быть учтены самим программи- стом. Предположим, что задано выражение С(*4-1)-Л(<4-1)+В(Ж). Более предпочтительная запись будет включать новый опера- тор, вычисляющий индекс только один раз: k — I 4" I» С(Л)=А(£)4-В(£). В примере 9.1.а показано, как обычно используются формулы для определения корней квадратичных уравнений: (a) if Ь*»2 -4.0 * а * с >0.0 r ' then root-1 := (-b + sqrt(b ** 2 - 4.0 * a * с)) / (2.0 « a); root-2 := (-b - sqrt(b •» 2 - 4.0 * a * c)) / (2.0 * a) (b) discr := b * b - 4. » a • c; if discr > 0.0 then d := sqrt(discr);
308 Глава 9 two-а := а + а; root-1 := (-b + d) / two-а; root-2 := (-b - d) / two-a Видно, что в операторах содержится много общих подвыраже- ний. В примере 9.1.6 дан второй вариант программы вычисле- ния корней квадратичного уравнения. В нем устранены все об- щие подвыражения, и функция, вычисляющая значение квадрат- ного корня, вызывается только один раз. Также уменьшено число сложных операций за счет замены операции возведения в степень умножением b*b и умножение сложением а+а. Логические выражения можно оптимизировать, изменив по- рядок следования в них условий. Если р и q — условия и р принимает значение «истина» чаще, чем q, то if р or q выполнится быстрее, чем if q or р при условии, что ЭВМ прекращает вычисление составного ус- ловного выражения, когда результат уже ясен, и что ЭВМ про- сматривает выражение слева направо, т. е. в дизъюнктивном выражении простые условия должны стоять в порядке убыва- ния вероятностей их появления. В конъюнктивном же выраже- нии простые условия должны стоять в порядке возрастания вероятности их появления. Если р принимает значение «исти- на» чаще, чем q, то if q and p выполнится быстрее, чем if р and q поскольку, если q принимает значение «ложь», значения р уже можно не определять, a q с большей вероятностью, чем р, принимает значение «ложь». Следует учесть, что оптимальный порядок следования условий не всегда ускоряет вычисления. Повторения. Как уже говорилось выше, оптимизировать сле- дует только часто выполняемые фрагменты программ, напри- мер циклы. При оптимизации циклов не следует жертвовать ясностью ради эффективности. Языковые конструкции должны использоваться только тогда, когда они являются наиболее подходящими. Если подходящих конструкций не существует, программист может использовать более эффективный способ управления циклом. Для любых способов управления циклом все стоящие внут- ри цикла операторы, выполнение которых не зависит от числа повторений цикла, должны быть выполнены до входа в цикл.
Оптимизация программ 309 При программировании на языке Кобол часто встречаются та- кие. ошибки, как заполнение пробелами строки, выводимой на АЦПУ, перед тем как записать в нее информацию в теле цик- ла. Но если все строки, выводимые на печать, имеют один и тот же формат, удобнее было бы заполнять пробелами область, состоящую из нескольких строк, один раз до входа в цикл. Примеры 9.2, а и б представляют собой два фрагмента прог- рамм, причем второй получен из первого с помощью удаления из цикла выражений 2*к и к—1. Вычисление значений этих выражений можно проводить за пределами цикла, поскольку они не зависят от управляющей переменной цикла: "for i := 1 to 100 do j := 2 * k + i; A(j) := k - 1 а) Цикл n := k + k; (Пример 9.2) m := k - 1; for i := 1 to 100 do j := n + i; A(j) := m б) Оптимизированный цикл Пример 9.3 показывает, как можно уменьшить число сложных операторов, стоящих внутри цикла, с помощью введения новой переменной: for i := 1 to 100 do A(i) := 5 *i а) Цикл j := 5 (Пример 9.3) for i := 1 to 100 do A(i) :=j; i := j + 5 б) Оптимизированный цикл Если цикл с управляющей переменной выполняется 1000 раз, столько же раз повторяется не только выполнение операций из тела цикла, но и наращивание управляющей пере- менной и проверка условия завершения. Число итераций мож- но существенно уменьшить, если воспользоваться методом, из- вестным как метод развертывания цикла. Он заключается в том, что один и тот же оператор, стоящий в теле цикла, запи- сывается для нескольких различных значений управляющей переменной и, таким образом, число итераций удается сокра- тить в несколько раз. Этот метод иллюстрируется ниже: for i := 1 to 1000 do for i := 1 to 1000 step 2 do .(Пример 9.4) A(i) := 0 A(i) := 0; A(i + 1) := 0 а) Цикл б) Оптимизированный цикл
310 Глава 9 В примере 9.4, а неявно присутствуют 1000 операций сложе- ния, проводимых с управляющей переменной i. В примере 9.4,6 также проводится 1000 операций сложения с управляющей пе- ременной (500 неявных и 500 явных). Но число проверок ус- ловия завершения цикла в примере 9.4,5 в два раза меньше, чем в примере 9.4, а. Если тело цикла должно повторяться лишь несколько раз, когда цикл выполняется, более эффектив- но полностью развернуть цикл, т. е. записать непосредственно все выполняемые операторы. Для программ, большая часть времени выполнения которых приходится на циклы, оптимизация циклов может привести к значительной экономии времени. Хорошим примером является стандартный алгоритм умножения матриц. Если матрицы име- ют размерность 50X50 (пример 9.5), внутренний цикл выполня- ется 125000 раз. Развертывание цикла приводит к тому, что тело цикла усложняется, но выполнение цикла будет осуще- ствляться только 15625 раз, а число проверок условия завер- шения цикла уменьшится на 90%. for i := 1 to 50 do (Пример 9.5)> for j := 1 to 50 do C(i,j) :=0; for k := 1 to 50 do C(i,j) := C(i,j) + A(i,k) * B(k,j) а) Умножение матриц for i := 1 to 50 step 2 do . ' ii := i + 1; for j := 1 to 50 step 2 do jj:=j + 1; C(i,j):=O; C(i,jj):=O; C(ii.j) := 0; C(ii,jj):=O; for k := 1 to 50 step 2 do kk:=k+1; C(i,j) := C(i,j) + A(i,k) * B(k,j) + A(i,kk) * B(kk,j); C(i,jj) := C(i,jj) + A(i,k) * B(k,jj) + A(i,kk) * В(kkJj); C(ii,j) := C(iiJ) + A(ii,k) » B(k,j) + A(ii,kk) * B(kkJ); C(ii,jj) := C(ii,jj) + A(ii,k) * B(kJj) + A(ii,kk) * B(kkjj) б) Оптимизированное умножение матриц Если для выполнения программ использовать интерпретатор, а
Оптимизация программ 311 не компилятор, то первая программа будет выполняться быст- рее второй, поскольку более компактная запись цикла позволит минимизировать время, требуемое для интерпретации операто- ров. К экономии времени приводит также переупорядочивание вложенных циклов таким образом, чтобы внешний цикл вы- полнялся как можно меньшее число раз. В примере 9.6. б внут- ренний цикл инициализируется 10 раз, а в примере 9.6, а — 20 раз: for j := 1 to 20 do for i := 1 to 10 do A(i,j) :=0 а) Цикл for i := 1 tQ 10 do (Пример 9.6) for j := 1 to 20 do A(i,j) :=0 б) Оптимизированный цикл Покажем, как можно уменьшить число инициализаций цикла на примере следующих друг за другом циклов. Слияние двух внешних циклов в примере 9.7 позволит уменьшить длину про- граммы и снизить время выполнения второго внешнего цикла: for i := 1 to 50 do for j := 1 to 50 do A(i,j) := 0; for k := 1 to 50 do A(k,k) := 1 а) Цикл for i := 1 to 50 do (Пример 9.7) for j := 1 to 50 do A(i,j) :=0; A(i,i) := 1 ' б) Оптимизированный цикл Выбор метода оптимизации зависит от вычислительной сре- ды. Так, замена возведения в степень умножением оправдана, если компилятор не делает этого. При определенных способах представления чисел и аппаратной реализации умножение мо- жет выполняться так же быстро, как и сложение. Дублирование операторов в теле цикла для уменьшения числа итераций мо- жет использоваться только в тех случаях, когда программа ком- пилируется; если же применяется интерпретатор, дублирование может привести к замедлению счета. Переупорядочивание вло- женных циклов с целью уменьшения общего числа инициализа- ций циклов не всегда уменьшает время выполнения. Дело в том, что переупорядочивание может привести к тому, что эле- менты большого массива будут запрашиваться не в том поряд- ке, в котором они хранятся. Тогда в виртуальной среде время выполнения может возрасти из-за дополнительных обращений к страницам памяти.
812 Глава 9 9.3.3. Повышение эффективности алгоритмов Предположим, что имеются два алгоритма. Время работы одного пропорционально полиномиальной функции, а время ра- боты второго — линейной функции времени. В этом случае не- оправданными будут попытки выиграть несколько миллисекунд путем изменения программы, реализующей первый алгоритм. Если лучшего алгоритма нет, целесообразно использовать эв- ристики для повышения эффективности работы алгоритма. Если и эта возможность уже исчерпана, можно проводить моди- фикацию исходного текста программы (а не объектного). Если нет настоятельной необходимости в повышении эффективности программы, модификацию текста не следует проводить. Целью модификации должна быть экономия времени выполнения наи- более часто используемых частей программы без ущерба для ясности и общепринятых стандартов. Бессмысленно оптимизи- ровать редко используемые части программы, поскольку удает- ся сэкономить очень мало времени, но зато в текст программы могут быть внесены дополнительные ошибки. СТАРАЙТЕСЬ БЕЗ КРАЙНЕЙ НЕОБХОДИМОСТИ НЕ ИЗМЕНЯТЬ РАБОТАЮЩУЮ ПРОГРАММУ Изменения, вносимые в структуру данных или в алгоритм, могут существенно улучшить программу. Переупорядочивая вло- женные циклы, можно уменьшить число проверок условий за- вершения внутренних циклов. Этого можно достичь также пу- тем изменения структуры данных и алгоритма. Для уменьше- ния числа проверок внутренних циклов в некоторых задачах можно использовать различные методы поиска. Линейный поиск в таблице заканчивается, когда нужный элемент найден или вся таблица просмотрена. Следовательно, необходимо по- стоянно проверять два условия: во-первых, является ли теку- щий элемент искомым и, во-вторых, есть ли еще элементы в таблице. Размещая искомый элемент непосредственно за по- следним элементом таблицы, можно гарантировать, что он бу- дет найден, и отпадает необходимость проверять, все ли элемен- ты таблицы просмотрены. В этом случае следует различать две ситуации, которые могут возникнуть после завершения выполне- ния цикла. Чтобы учесть их, необходимо сделать небольшие изменения в первоначальной программе, которая завершается при нескольких условиях. Если должно быть считано не более 100 значений и искомое значение ожидается, но может быть пропущено, все три условия можно использовать для присваи- вания значения 100 (или 101) управляющей переменной, поэто- му в процессе контроля завершения выполнения цикла потребу- ется только одна проверка.
Оптимизация программ 31& В играх, использующих игральные доски с полями, подоб- ная ситуация может возникнуть при проверке выхода за пре- делы доски. Если игровая часть доски обрамлена буферными полями, выход за пределы игровой части может обрабатывать- ся подобно обычному движению по доске. На рис. 9.4, а пока- зан лабиринт, движение по которому должно контролироваться' (например, путем проверки значений индексов), чтобы предот- вратить выход за пределы доски. В лабиринте, показанном на рис. 9.4,6 выход за пределы контролируется так же, как и лю- бое другое движение по лабиринту, т. е. очередная позиция анализируется и устанавливается, принадлежит ли она возмож- ному пути, является ли целевой или нет. Рис. 9.4. Обрамление доски дополнительными полями. а — лабиринт; б — расширенный лабиринт. Изменив алгоритм таким образом, чтобы как можно боль- шая часть работы выполнялась на стадии компиляции, можно сэкономить время. Однако, перекладывая на компилятор рабо- ту по инициализации переменных, удается сэкономить очень мало времени, так как инициализация является однократно выполняемой операцией. Если и константы, и переменные ини- циализируются во время компиляции, это приводит к достаточ- но запутанному коду. Кроме того, это является причиной труд- ностей, возникающих при реализации оверлейных структур. Разбиение программы на модули оказывает влияние на ее эффективность. Структура модулей и способ управления ими отражаются на эффективности страничной и оверлейной орга- низации. От способа разбиения текста программы на модули зависит время выполнения. Если программа написана на языке Ассемблер, ее можно разбить на модули так, что дополнитель- ного времени на выполнение не потребуется. Это достигается за счет использования макроопределений или вызовов модулей, не выполняющих вспомогательные функции. Последнее приво- дит к слабой модульной независимости. Большинство языков высокого уровня не имеет макросредств. Для передачи или приема управления частью программы, трактуемой как отдель- 21-399
314 Глава 9 ный модуль, желательно не использовать оператор GOTO. В Коболе оператора GOTO нет. Поэтому ссылки к совместно используемым или независимым частям программы производят- ся с помощью оператора PERFORM. Параграф языка Кобол, который приводится в действие оператором PERFORM, можно считать, специальным типом внутренней процедуры. В зависи- мости от реализации использование внутренних процедур вмес- то внешних позволяет сэкономить время, требуемое на связь между модулями, не нарушая при этом модульности исходной программы. Правда, ошибки во внутренних процедурах более вероятны, поскольку в них обычно есть ссылки к глобальным данным. Если процедура вызывается из тела цикла, наилучший способ экономии времени заключается в организации цикла внутри процедуры, как показано в примере 9.8: procedure P(k); procedure Р; (Пример 9.8) ... for i := 1 to 100 do end P; , , , for i := 1 to 100 do end P; call P(t) call P а) Цикл . б) Оптимизированный цикл 9.4. Средства оптимизации Перед тем как начать проводить изменения файлов или текста программы, программист должен убедиться в том, что эти изменения действительно необходимы. Иногда лучшим вы- ходом является изменение вычислительной среды. Обычные статистические характеристики, которые можно получить с по- мощью компиляторов и операционных систем, показывают, эф- фективно ли работает программа в данной вычислительной среде. К таким характеристикам относятся не только время, объем памяти, но и информация об использовании периферий- ных устройств. Если любые из этих величин оказываются слиш- ком большими, следует проводить оптимизацию. Программные средства, позволяющие определять время выполнения программ и подсчитывать количество страничных прерываний, можно ис- пользовать для определения типа необходимой оптимизации. Глобальная оптимизация необходима в том случае, если программа, подготовленная для работы в режиме интерпрета- ции, будет использоваться в режиме компиляции, поскольку в этом случае существенно уменьшается время выполнения и уве- личивается объем требуемой оперативной и внешней памяти. Если используется виртуальная память или оверлейная орга- низация, увеличение размера области памяти, выделяемой за-
Оптимизация программ 315 даче, и новая сегментация могут позволить уменьшить число обращений к дисковой памяти. Помещая все наборы данных и программу в один и тот же пакет дисков и выделяя память для них целыми цилиндрами, можно уменьшить сумму перемеще- ний головок записи-считывания и, следовательно, уменьшить общее время доступа. Наилучшим способом повышения эффективности исходного текста программы является использование оптимизирующего компилятора. Наибольший выигрыш получается в результате оптимизации отдельных операторов исходной программы. Мно- гие компиляторы стремятся получить такой объектный код, что- бы большая часть операций проводилась с быстродействующи- ми регистрами, а не с оперативной памятью. Некоторые ком- пиляторы проводят оптимизацию целиком всей программы. Они могут обнаруживать наличие неиспользуемых, но объявленных имен и удалять их, освобождая тем самым оперативную па- мять. старайтесь не оптимизировать текст программы СВОИМИ СИЛАМИ 9.5. Документирование показателей эффективности программы Оптимизация является попыткой повысить эффективность программы, т. е. уменьшить количество требуемых для ее вы- полнения ресурсов. Эти требования должны найти отражение в документации. Если для определения показателей эффектив- ности выполняются контрольные прогоны, их результаты долж- ны также быть включены в документацию. Если эффектив- ность программы зависит от типа компилятора, системного программного обеспечения, описания и размещения наборов данных на магнитных носителях, это должно быть отражено в документации. Описание операционной среды, используемой для контрольных прогонов, должно быть включено в описание процедуры тестирования. На рис. 1.2 (гл. 1) для описания требований к операцион- ной среде используется отдельный блок. Такие характеристи- ки рабочей программы, как время, объем памяти, требуемое системное программное обеспечение, говорят о том, удовлетво- ряет ли программа этим требованиям. На рис. 1.2 приведен также блок, характеризующий надежность системы. Здесь под надежностью понимается в первую очередь нечувствительность к отказам, а также степень общности системы. Если система предназначена только для определенных данных, это должно быть отражено при описании характеристик надежности. В документацию следует включать описания возможных спо- 21*
316 Глава 9 •собов оптимизации программы только тогда, когда программа используется неэффективно или из-за неудач может возникнуть неверное представление о проекте в целом. Если структура дан- ных и алгоритмы ориентированы на конкретные данные, описа- ние системы должно быть достаточно подробным, чтобы систе- ма могла легко адаптироваться к изменениям данных. 9.6. Упражнения 1. Если у вас нет виртуальной ЭВМ, возьмите программу, разбитую на модули, организуйте оверлейную структуру и прогоните программу. Сколько памяти удалось сэкономить? Как много времени затрачено на выполнение программы? 2. Если у вас виртуальная ЭВМ, измените модульную структуру уже су- ществующей программы таким образом, чтобы уменьшить число страничных прерываний. Сколько времени удается сэкономить таким способом? 3. Напишите программу размещения 1000 записей, в последовательном файле. Сравните время, необходимое для выполнения программы, и память, необходимую для хранения как программ, так и файла, если записи а) не сблокированы, б) сблокированы с разными характеристиками блокировки. 4. Выполните упражнение 3 для файла с прямым доступом. Считайте, что записи могут поступать в произвольном порядке. Если ваша операционная система не позволяет блокировать файлы с прямым доступом, осуществите блокирование программным путем. 5. Проведите компиляцию программы с помощью обычного и оптимизиру- ющего компиляторов. Используйте несколько типов оптимизации. Сравните память, необходимую полученным программам, а также время их выполнения. 6. Проведите компиляцию программы с помощью оптимизирующего ком- пилятора. После этого проведите оптимизацию программы самостоятельно и откомпилируйте ее с помощью стандартного компилятора. Сравните резуль- таты. 7. Приведите документацию, относящуюся к показателям эффективности «акой-нибудь программы.
СПИСОК ТЕРМИНОВ Абстрактная структура данных: структура данных, определенная функ- ционально, т. е. посредством выполняемых над ней операций. Подобная струк- тура не зависит от способа ее реализации. Агрегирование: группирование данных, при котором не обязательно нали- чие какой бы то ни было логической взаимосвязи данных. Ада: созданный в последние годы универсальный алгоритмический язык, применяемый при разработке как системного, так и прикладного программно- го обеспечения. Алгол: алгоритмический процедурно-ориентированный язык программиро- вания. Получил более широкое распространение в европейских странах, чем в США. Алгоритм: формализованное описание содержания и последовательности выполнения операций, реализующих некоторый метод решения задачи. Дан- ное описание может быть представлено в виде набора математических зави- симостей, текста на псевдокоде или одном из языков программирования и т. д. База данных: совокупность структурированных данных, используемых в разнообразных приложениях. Бейсик: алгоритмический язык для научных расчетов, ориентированный на использование индивидуальных терминалов. Получил широкое распростра- нение на малых ЭВМ. Виртуальная машина: вычислительная система, обеспечивающая выполне- ние крупных заданий при небольшом объеме оперативной памяти. Программы автоматически расчленяются на помещающиеся в памяти страницы, которые загружаются в нее по мере необходимости. Восходящий метод: метод разработки, реализации и тестирования про- грамм, опирающийся на первоначальное изучение самых конкретных, частных деталей и последовательный переход к более общим аспектам. Граф-диаграмма: схема, на которой с помощью кружков и стрелок изо- бражаются процессы прохождения данных через систему. Графическая схема: древоподобная схема, иллюстрирующая структуру до- кумента или набора данных. Диаграмма Варнье — Орра: иерархическая система контурных изображе- ний, опирающаяся на структуры входных и выходных данных. Каждое изо- бражение детализирует предыдущее. Диаграмма потоков данных: схема, на которой указаны входные и вы- ходные данные программного модуля, наборы данных и потоки данных. Драйвер: фиктивная управляющая программа, используемая при восхо- дящем кодировании. Осуществляет вызовы тестируемых подпрограмм. Заглушка: фиктивная подпрограмма, используемая при нисходящем коди- ровании программного модуля. Ее вызов осуществляет тестируемый модуль. Защищенность: способность программы сохранять работоспособность при наличии системных отказов. Иерархическая диаграмма: древоподобная схема, показывающая, какие программные модули осуществляют вызовы других модулей, и каких именно. Интерпретатор: системная программа, осуществляющая пооператорное
318 , Список терминов выполнение программы на языке высокого уровня без перевода ее в машинный код. Исключительное состояние: ошибка обработки или необычное событие, которое должно быть опознано и соответствующим образом обработано. Исходный код: закодированный текст на языке высокого уровня, служа- щий входной информацией для компилятора или интерпретатора. Итерации: способ решения задачи, основанный на последовательном при- менении одного и того же алгоритма к ряду элементов данных. Для формаль- ного описания этого способа могут использоваться любые стандартные цикли- ческие конструкции. Индексированный файл: система хранения файлов на дисках, в которой применяются справочники, позволяющие реализовать как последовательный» так и прямой доступ к данным. При прямом доступе информация в поле дан- ных используется в качестве ключа. Кобол: наиболее широко применяемый для решения экономических и ком- мерческих задач язык программирования. Приобрел популярность благодаря использованию упрощенных конструкций английского языка. Компилятор: обрабатывающая программа, осуществляющая перевод про- грамм с языка высокого уровня в машинный код. Конструкция case: стандартная логическая конструкция, предназначенная для одной из некоторой совокупности альтернатив. Конструкция if...then...else: стандартная условная конструкция, предназна- ченная для выбора одной из двух возможных альтернатив. Конструкция repeat...until: стандартная циклическая конструкция с про- веркой условия на выходе. Последнее означает, что тело цикла должно вы- полниться по меньшей мере один раз. Конструкция while...do: стандартная циклическая конструкция с провер- кой условия на входе. Последнее означает, что тело цикла может ни разу не выполниться. Модуль: базовый функциональный элемент программы. Модули разраба- тываются, кодируются и испытываются по отдельности. Мультипрограммная система: вычислительная система, осуществляющая параллельную обработку нескольких заданий одновременно. Надежность: показатель, характеризующий степень повторяемости резуль- татов, выдаваемых программой, при различных условиях функционирования. Нестандартная функция: функция, порождающая побочные эффекты. Оиа не только возвращает значение в точку вызова, но может также выполнять операции ввода/вывода, изменять значения некоторых внутренних параметров или глобальных данных. Нисходящий метод: метод разработки, реализации и тестирования про- грамм, опирающийся на первоначальное изучение всей проблемы в целом и последовательный переход к более частным аспектам. Объектный код: обработанная компилятором программа, представленная в машинном коде. Оверлеи: указанные пользователем сегменты программы, загружаемые по мере необходимости из дисковой в быстродействующую оперативную па- мять. Отладка: процесс локализации и идентификации ошибок в программе. Паскаль: компактный высокоструктурированный язык программирования, являющийся развитием Алгола. В основном используется на малых машинах и в сфере обучения. ПЕРТ-диаграмма: схема, состоящая из кружков и стрелок. Отображает временные связи между отдельными этапами обработки. ПЛ!1: мощный многоцелевой язык программирования, в котором сочета*
Список терминов 319 йотся и развиваются средства Кобола, Фортрана и Алгола. В основном при- меняется на вычислительных установках, выпускаемых фирмой IBM0. Повторно используемый код: программный код, в котором предусмотрено ^выполнение операций присваивания, обеспечивающее неизменность результа- тов при каждом вызове кода с одним и тем же набором входных данных. Погрешность: показатель, характеризующий отклонение числового значе- ния результата от истинной величины. Подобное отклонение может быть об- условлено как особенностями используемого вычислительного алгоритма, так и ошибками, связанными с ограниченной разрядностью ЭВМ. Подпрограмма: отдельно компилируемая программа, содержащая один •или несколько модулей. Последовательный доступ: способ обращения к отдельным элементам фай- ла или массива, основанный на последовательном просмотре всех предшеству- ющих элементов. Последовательный файл: система хранения записей, обеспечивающая об- ращение к ним только с помощью последовательного доступа. Права: наличие разрешения на использование системных ресурсов для выполнения определенных действий. Правильность: показатель, характеризующий степень соответствия про- граммы требованиям технического задания. Предсказуемость: модуль называют предсказуемым, если его функциони- рование определяется только параметрами, в то время как внешняя програм- мная среда не оказывает на него влияния. Прямой доступ: способ обращения к отдельным элементам файла или мас- сива не путем последовательного просмотра, а непосредственно, с помощью соответствующих ключей. Псевдокод: подробное описание алгоритма на структурированном и ча- стично формализованном подмножестве английского языка. Программа: функционально законченная совокупность модулей или под- программ. Реентерабельный код: программный код, не содержащий никаких локаль- ных переменных или любых запоминаемых внутри него данных. Рекурсия: способ решения задачи, основанный на применении алгоритма к данным, полученным с помощью того же алгоритма. Для формального опи- сания этого способа могут использоваться либо рекурсивные функции, либо вызовы программных модулей, в которых в качестве параметров фигурируют имена тех же модулей. Связность: мера внутренней целостности программного модуля. Показа- тель, характеризующий степень взаимосвязи отдельных частей программы. Сеть Петри: схема, позволяющая путем перемещения специальных знач- ков показать, как синхронизованы между собой различные этапы обработки. Система: совокупность связанных друг с другом программ и наборов данных. Словарь данных: таблица, содержащая такие атрибуты элементов данных, как их имена, типы, размерности, форматы, диапазоны изменения значений, допустимые способы использования. Совместимость: показатель, характеризующий возможность правильной работы модуля в качестве составной части программной системы. Страничный обмен: автоматическое перемещение необходимых сегментов программы из дисковой памяти в оперативную. Страничное прерывание: в виртуальных машинах — вызов сегмента про- граммы, который в данный момент отсутствует в оперативной памяти. Структурная схема: схема, на которой с помощью стрелок и блоков раз- личной формы отображаются потоки данных, проходящих через систему, или передачи управления внутри программы. ° Язык ПЛ/1 входит в состав программного обеспечения ЭВМ Единой Серии, производимых в странах СЭВ. — Прим, перев.
320 Список терминов Схема «исток — преобразование — сток»: построенная по иерархическому принципу схема, получаемая на основе анализа процессов, в результате кото- рых входные данные преобразуются в выходные. Схема Джексона: построенная по иерархическому принципу схема, получае- мая на основе анализа структур входных и выходных данных. Схема Насси — Шнейдермана: схема, иллюстрирующая структуру пере- дач управления внутри модуля с помощью вложенных друг в друга блоков. Сцепление: мера взаимонезависимости программных модулей. Показатель, определяемый характером межмодульных связей. Таблица решений: таблица, отображающая зависимость действий, подле- жащих выполнению, от условий, при которых они могут выполняться. Точность: показатель, характеризующий возможность разделения близ-’ ких числовых значений. Часто он определяется как количество значащих цифр. Универсальность: показатель, характеризующий возможность правильной работы программы при обработке различных вариантов исходных данных. Управляющее поле: поле входных данных, используемое для группирова- ния данных. Изменение содержимого этого поля вызывает управляющее пре- рывание. Управляющее прерывание: временное прерывание процесса обработки, об- условленное изменением значения определенного элемента данных. Файл с относительной нумерацией: система хранения файлов на дисках, обеспечивающая как последовательный, так и прямой доступ к данным. При прямом доступе ключом служит порядковый номер записи в файле. Фортран: язык программирования, получивший наибольшее распростра- нение в области научно-технических расчетов. В этом языке предусмотрен ши- рокий набор разнообразных математических функций. Черный ящик: программный модуль, набор данных или устройство, о ко- торых не известно ничего, кроме спецификаций входной и выходной информа- ции. Чистый код: программный код, в котором после его вызова не запомина- ются никакие числовые значения. Шифрование: метод кодирования сообщений, позволяющий предотвратить их перехват пользователями, не обладающими соответствующими правами. Эвристика: неформальный метод преобразования алгоритма с целью по- вышения его эффективности. Эффективность: показатель, характеризующий быстродействие и объем используемой памяти.
ЛИТЕРАТУРА ПРЕДИСЛОВИЕ Brooks F. Р., Jr., The Mythical Man-Month. Reading, Mass.: Addison-Wesley, 1975. [Имеется перевод: Брукс Ф. П., Как проектируются и создаются программные комплексы: мифический человеко-месяц. Очерки по систем- ному программированию. — М.: Наука, 1979.] Maly К., Hanson A. R., Fundamentals of the Computing Sciences, Englewood Cliffs, N. J.: Prentice-Hall, 1978. Weinberg G. M., The Psychology of Computer Programming, New York: Van Nostrand Reinhold, 1971. Глава 1 Bohl M., A Guide for Programmers, Englewood Cliffs, N. J.: Prentice-Hall, 1978. Glass R. L., Software Reliability Guidebook, Englewood Cliffs, N. J.: Prentice- Hall, 1979. [Имеется перевод: Гласс P., Руководство по надежному про- граммированию. — М.: Финансы и статистика, 1982.] Myers G. J., Software Reliability: Principles and Practices, New York: Wiley, 1976. [Имеется перевод: Майерс Г., Надежность программного обеспече- ния.— М.: Мир, 1980.] Tausworthe R. С., Standardized Development of Computer Software, Parts I and II. Englewood Cliffs, N. J.: Prentice-Hall, 1979. Van Duyn J., Documentation Manual, Philadelphia: Auerbach, 1972. Глава 2 Atwood J. W., The Systems Analyst, Rochelle Park, N. J.: Hayden, 1977. Bauer F. L., ed., Advanced Course of Software Engineering, New York: Sprin- ger-Verlag, 1973. Bergland G. D., Gordon R. D., eds., Software Design Strategies, New York: IEEE Computer Society, 1979. Freeman P., Wasserman A. L, Tutorial on Software Design Techniques, New York: IEEE Computer Society, 1980. Gane Ch., Sarson T., Structured Systems Analysis: Tools and Techniques, Engle- wood Cliffs, N. J.: Prentice-Hall, 1979. Yeh R. T., ed., Current Trends in Programming Methodology, Vols. I and II, Englewood Cliffs, N. J.: Prentice-Hall, 1977. Глава 3 Gane Ch., Sarson T., Structured Systems Analysis: Tools and Techniques, Engle- wood Cliffs, N. J.: Prentice-Hall, 1979. Hoare C. A. R„ Notes on Data Structuring, in Structured Programming, O. J. Dahl, E. W. Dijkstra and C. A. R. Hoare, eds., New York: Academic Press, 1972. Kaimann R. A., Structured Information Files, Los Angeles: Melville, 1973. Preliminary ADA Reference Manual, SIGPLAN Notices, 14, № 6 (1979). Wirth N., Jensen K., Pascal — User Manual and Report, New York: Springer- Verlag, 1975. [Имеется перевод: Йенсен К., Вирт Н., Паскаль: Руководст- во для пользователей и описание языка. — М.: Финансы и статистика, 1982.] Глава 4 Bergland G. D., Gordon R. D., eds., Tutorial on Software Design Strategies, New York: IEEE Computer Society, 1979. Freeman P., Wasserman A. I., eds., Tutorial on Software Design Techniques, New York: IEEE Computer Society, 1980. Jackson M., Principles of Program Design, London: Academic Press, 1975.
322 Литература Kernighan В. W., Plauger P. L., Software Tools, Reading, Mass.: Addison-Wes* ley, 1976. . Maynard J., Modular Programming, Princeton, N. J.: Auerbach, 1972. Myers G. J., Reliable Software through Composite Design, New York: Petro* celli/Charter, 1975. Myers G. J., Software Reliability: Principles and Practices, New York: Wiley» 1976. [Имеется перевод: Майерс Г., Надежность программного обеспече* ния. — М.: Мир, 1980.] Nam Р., Programming by action clusters, BIT, 9 (1969), pp. 250—258. Parnas D. L., On the criteria used in decomposing systems into modules, Com- munications of the ACM, 15, № 12 (1972), pp. 1053—1058. Parnas D. L., A Technique for software module specification with examples» Communications of the ACM, 15, № 5 (1972), pp. 330—336. Yourdon E., Techniques of Program Structure gnd Design, Englewood Cliffs, N. J.: Prentice-Hall, 1975. Yourdon E., Constantine L. L„ Structured Design, Englewood Cliffs, N. J.: Pren- tice-Hall, 1979. Wirth N., Program development by stepwise refinement, Communications of the ACM, 14, № 4 (1971), pp. 221—227. Глава 5 Aho A. V., Hopcroft J. E., Ullman J. D., The Design and Analysis of Computer Algorithms, Reading, Mass.: Addison-Wesley, 1974. [Имеется перевод: Ахо А. и др. Построение и анализ вычислительных алгоритмов. — М^:. Мир, 1979.] Ernst G. W., Newell A., GPS: A Case Study in Generality and Problem Solvings New York: Academic Press, 1969. Floyd R. W., The paradigms of programming, Communications of the ACM, 22» № 8 (1979). Goodman S. E., Hedetniemi S. T., Introduction to the Design and Analysis of Algorithms, New York: McGraw-Hill, 1977. [Имеется перевод: Гудман С.» Хидетниеми С., Введение в разработку и анализ алгоритмов. — М.: Мир» 1981.] Jackson Р., Introduction to Artificial Intelligence, New York: Petrocelli Books* 1974. Knuth D. E., The Art of Computer Programming, Vols. I—III, Reading, Mass.: Addison-Wesley, 1969. [Имеется перевод: Кнут Д., Искусство программиро- вания для ЭВМ, т. 1. — М.: Мир, 1976; т. 2, Получисленные алгоритмы» 1977; т. 3, Сортировка и поиск, 1978.] Polya G., How to Solve It, Garden City, N. Y.: Doubleday, 1957. Трахтенброт Б. А., Алгоритмы и машинное решение задач; Под ред. С. В. Яб- лонского— 2-е изд. — М.: Физматгиз, 1960. Wetherell Ch., Etudes for Programmers, Englewood Cliffs, N. J.: Prentice-Hall» 1978. Глава 6 American National Standard Flowchart Symbols, ANSI-X 3.5-1970, New York: American National Standards Institute, 1970. Bohl M., Flowcharting Techniques, Chicago: Science Research Associates, 197L Boillot M. H., Gary M. G., Horn L. W., Essentials of Flowcharting, Dubuque, Iowa: William C. Brown, 1975. Farina M. V., Flowcharting, Englewood Cliffs, N. J.: Prentice-Hall, 1970. Ganapathy S., Rajaraman V., Information theory applied to the conversion of decision tables to computer programs, Communications of the ACM, 16r № 9 (1973), pp. 532—539. Humby E., Programs from Decision Tables, New York: American Elsevier» 1973. Kernighan B. W., Plauger P. J., Programming styles, examples and counter- examples, ACM Computing Surveys, 6, № 4 (1974), pp. 303—319.
Литература 323 Kernighan В. W., The Elements of Programming Style, New York, McGraw-Hill, 1974. London K. R., Decision Tables, Princeton, N. J.: Auerbach, 1972. McDanie H., An Introduction to Decision Logic Tables, New York, Wiley, 1968. Nassi, L, Schneiderman B., Flowchart techniques for structured programming, SIGPLAN Notices, 8, № 8 (1973), pp. 12—26. Pooch U. W., Translation of decision tables, ACM Computing Surveys, 6, № 2 (1974), pp. 125—151. Wirth N., On the Composition of well-structured programs, ACM Computing Surveys, 6, № 4 (1979), pp. 247—259. Глава 7 Barron D. W., Recursive Techniques in Programming, New York, American El- sevier, 1968. [Имеется перевод: Баррон Д., Рекурсивные методы в про- граммировании.— М.: Мир, 1974.] Gilb Т., Software Metrics, Englewood Cliffs, N. J.: Prentice-Hall, 1972. Dijkstra E. W., GOTO statement considered harmful, Communications of the ACM, 11, № 3 (1968), pp. 147—148. v Fitzsimmons A., Love T., A review and evaluation of soitware science, ACM Computing Surveys, 10, № 1 (1978), pp. 3—18. Halstead M. H., Elements of Software Science, New York: Elsevier North-Hol- land, 1977. [Имеется перевод: Холстед M. X., Начало науки о програм- мах.— М.: Финансы и статистика, 1981.] Kernighan В. W., Plauger Р. J., The Elements of Programming Style, New York: McGraw-Hill, 1974. Knuth D. E., Structured programming with GOTO statements, ACM Computing Surveys, 6, № 4 (1974). Ledgard H. F., Programming Proverbs, Rochelle Park, N. J.: Hayden, 1975. Глава 8 Dodes I. A., Numerical Analysis for Computer Science, New York: North-Hol- land, 1978. Hantier S. L., King J. C., An Introduction to proving the correctness of pro- grams, ACM Computing Surveys, 8, № 3 (1976), pp. 331—353. Hastings C., Jr., Approximations for Digital Computers, Princeton, N. J.: Prin- ceton University Press, 1955. Hetzel W. C., ed., Program Test Methods, Englewood Cliffs, N. J.: Prentice- Hall, 1973. Hildebrand F. B., Introduction to Numerical Analysis, New York: McGraw-Hill, 1956. IBM System/360 Operating System FORTRAN IV Library: Mathematical and Service Subprograms, White Plains, N. J.: IBM. Lempel A., Cryptology in transition, ACM Computing Surveys, 11, № 4 (1979), pp. 285—303. Myers G. J., Reliable Software through Composite Design, New York: Petrocelli/ /Charter, 1975. Myers G. J., Software Reliability: Principles and Practices, New York: Wiley, 1976. Myers G. J., The Art of Software Testing, New York: Wiley, 1979. [Имеется пе- ревод: Майерс Г., Искусство тестирования программ. — М.: Финансы и ста- тистика, 1982.] «Глава 9 Allen F. Е., Cocke J., A catalogue of optimizing transformations, in Design and Optimizations of Compilers, Randall Rustin, ed. Englewood Cliffs, N. J.: Prentice-Hall, 1972. Penning P., Virtual memory, ACM Computing Surveys, 2, № 3 (1970).
ПРЕДМЕТНЫЙ УКАЗАТЕЛЬ Абстрактная структура данных 317 Агрегирование 317 Ада 16, 81, 237, 317 Адаптация процедур ручной обработки 17 Адаптируемость 13 Алгол 301, 317 Алгоритм 317 — выбор 303—305 — детерминированный 140—143 ч — недетерминированный 140—143 — повышение эффективности 312—316 — по природе недетерминированный 141 — эффективность 143 Анализ сообщений 107—110, 117 Аппроксимация 266—268 Архив данных 13 База данных 63—64, 317 Бейсик 79, 92, 189, 317 Блокирование 96, 153, 295 Ведущая группа 30 Виртуальные операционные системы 296, 297, 317 Внутренние процедуры 314 Возврат 191, 237—238 Восходящий метод 317 — кодирования 218 — проектирования 125, 126 Вспомогательные средства реализации 248—251 Вызов модуля 237—239 — по значению 237 — по значению-результату 237 — по имени 237 — по ссылке 237 Граф-диаграмма 43, 71, 123, 317 Графическая схема задания 25, 31, 77, 317 Граф потоков данных 43 Данные глобальные 126—127 — доступ 301, 302 — иерархическая схема 119—121 — информационные связи 132—134 — коллективный доступ 42 — оптимизация 303, 304 — проверка правильности 216, 225—226, 253-254 — разработка 42 — рекурсивное определение 59—60 — словарь 74, 75, 319 — структуры 58 — типы 83—87 — толерантность 256—257 Дейкстра 103 Дерево решений 131 Джексон 118 Диаграмма Варнье — Орра 43, 131, 317 Диаграмма потоков данных 317 Диск 97, 301, 302 Документирование алгоритмов 179 — данных 98 — исходных текстов 244—248 — модулей 99, 214—216 — показателей эффективности 315 — программ 99, 135—137 — проекта 55 — пролог 244, 245 — системы 30 Заглушка 218, 317 Задачи (см. Примеры): библиотечная св« стема регистрации 33, 56, 100 — вычисление синуса 156, 158, 180 ---среднего 180 ---функции Аккермана 180 ---числа перестановок 142—149, 252 — генерация случайных чисел 180 — десятичные значения римских цифр 205, 206, 216, 292 — задача о рюкзаке 180 — зондирование Венеры 249 — китайские шашки 137, 180, 252 — кратчайший путь через лабиринт 157— 159, 163, 180 — печать таблицы треугольников 138 — печать частот появления слов 138 — программы спулинга 56 — разделение на страницы 77, 78, 123— 125, 180 — система ведения документации 44, 56 — система предварительного заказа теннис- ных кортов 33, 100 — система регистрации избирателей 20—22, 100, 138 — система регистрации студентов 100 Защита информации 28, 50 Защищенность 12, 317 Значения ссылок 221 Иерархическая диаграмма 317 Изменение глобальных переменных 238 Инварианты цикла 279 Инвертирование программы 124, 125 Интерпретатор 317 Информационный поток 18 Исключительные состояния 181, 191, 200 Исходный код 318 Итерации 147, 151, 318 Качество программного обеспечения 10 — программной системы 11 Ключ доступа К файлу 93—95, 98 Кобол 79, 91, 187, 221, 228—229, 235—236. 247, 300, 309, 318 Комментарии 246—248 Компилятор 318 Константы 220—222 Конструкция следования 183—185, 196, 211 — case 184-191, 195, 212, 318 —’if ... then ... else 184, 318 — repeat ... until 184, 318 — while ... do 184, 212, 318 Контрольная сумма 225, 226 Контрольная цифра 225 Координация считывания/записи 51 Критический путь 29 Линия перехода 199 Магнитная лента 46, 91, 97 Машина Тьюринга 177
Предметный указатель 32S Машинные средства 248—261 Меры Холстеда 241—244, 252, 285 Метод выделения подцелей 172—175 — динамического программирования 165— 168 — моделирования 175—179 — наискорейшего спуска 160—164 — Ньютона — Рафсона 151, 158 — обратного прохода 164, 165 — Парнаса 137 — поиска с возвратом 168—172 — последовательных приближений 158—160 — «разделяй и властвуй» 155—158 Мнемонические имена 223 Множество 79, 81—82 Моделирование дискретных событий 175— 179 Моделирование переходов состояний 175— 179 Модуль 318 — длина 240—244 — завершение выполнения 282 — иерархическое проектирование 119—125 — независимый 113—117 — нумерация 109, 136 — объем 242 — ошибки объединения 285 — предсказуемый 115, 319 — сила связности 111 — сложность 243 — спецификация 118—119 — уровень записи 242, 243 Модуляризация 101 Мультипрограммная система 318 Набор данных 85 Надежность 12, 28, 253, 318 Неоднозначность имен 238 Нестандартная функция 238, 318 Нисходящий метод 318 — кодирования 217—218 — проектирования 103 Нормальная форма 66 Область управления 127—128 — влияния 127—128 Обработка входных данных 226, 227 Общие подвыражения 307 Объектный код 318 Объявления 221—223, 245 — типы 59 Оверлеи 101, 318 Оператор СОТО 235, 236 Оператор ON 192, 193 Оптимизация текста программы 305—311 — метод развертывания цикла 309 Оптимизирующие компиляторы 315 Отладка 318 Ошибки, намеренное внесение 283, 284 — обнаружение 253—255 — оценка 262—268, 283—286 — распространение 266 — типы 255, 256, 262-267, 268 — флаг 239 Пакет 81 Параллельная обработка 147, 152 Пароли 286, 287 Паскаль 92, 185, 187, 301, 318 Передача блоками 89 Перекрестное суммирование 226 Переменные 223, 224 Переход обработки 117, 120, 132, 133 Переходы 52 Перт-диаграмма 28, 51, 318 ПЛ/1 79, 91, 186, 246, 300, 318 Повторно используемый код 319 Погрешность 319 Подпрограмма 319 Полезность 3 Полиномы Чебышева 267 Постановка задачи 16—19 Поток данных 40, 126—129 ---совместно обрабатываемый 40, 48—55 ---схема 43, 71 ---таблица 71, 73 Потокоориентированная передача данных 89 Пошаговое уточнение 103—111 Права 287, 319 Правила оформления листингов 246 Правило 209 Правильность И, 319 Предложение EXIT 189, 190 Предложение RETURN 191 Предсказуемость 319 Представление чисел 257—262 Примеры: биномиальные коэффициенты 149, 150 — вычисление площади треугольника 155 ---арифметического выражения 59, 170, 171, 180, 206 ---синуса 158, 159, 180 --- квадратного корня 151 ---числа перестановок 142—149, 252 — генератор отчетов 120, 124, 131, 138 Пробуксовка 297 Проверяемость 12 Программа выборки 49, 50 Программа удаления операций из циклам 309 Проектное решение 118, 119 Процессы 40 — управляемые данными 48 Псевдокод 106, 319 Пустая запись 96, 97 Путь доступа 64 Развертывание циклов 309, 310 Разделение на страницы 77, 78, 123—125» 319 Расширение ядра 117—125 Редактирование 227 Реентерабельный код 319 Рекурсия 147, 151, 232—235, 319 — многократная 234 — однократная 232 Сборник алгоритмов АСМ 154 Связанный список 234 Связность модулей 11—113, 139, 319 — временная (по классу) 112 — коммутативная Ш—112 — логическая 113 — последовательная 111 — по совпадению 113 — процедурная 112 — функциональная 111 Секретарь проекта 30 Сеть Петри 52, 319 Символ ограничения/прерывания 197 Синхронизация обработки 48—53 Система 319 — испытания 19, 24 — качество 27 — определение 18, 35—39 — организация проектирования 28—30; — основные компоненты 18 — проект 32 — проектирование 19, 25—27 — развернутый план проекта 26—28
.326 Предметный указатель — реализация 19, 24, 35 — требования 31 — функциональная схема 45 — функциональное описание 22 — функциональные требования 15 — эксплуатация 19 Системные константы 221 Скаляры 57, 79—80 Сквозной анализ проекта 30 Слияние циклов 311 Сложность алгоритмов 143—146 — исходных текстов 240 — • модулей 243 — операторов 307 — программ 240—243 Смешанное программирование 218 Совместимость 12, 319 Сопрограммы 122, 153—154 Сортировка-обмен 144, 145 'Сортировка-слияние 155 Сортировка Шелла 156 'Составной ключ 67 Специальные функции 290 Список клиентов 17 Спулинг 56, 88 Среда заказчиков 15, 16 — пользователей 13 — ЭВМ 14 Средства оптимизации 314 Стандарт шифрования данных 289 Стандарты 27 Стиль программирования 208 Страничное прерывание 297 Страничный обмен 319 Структурное проектирование 22 -Структурные схемы программ 195—202, 319 Структурный анализ 20 Схема Джексона 120—122, 320 Схема «исток — преобразование ** сток» 107-110, 117, 320 Схема HIPO 53-55, 134 Схемы Насси — Шнейдермана 202, 320 Сцепление модулей 113—116, 320 — — по внешним ссылкам 115 ------данным 114 — кодам 115—116 —-----образцу 114 ------общей области 114 ------ управлению 115 -Счетчик записей 225 Точки ветвления 277 — слияния 277 Точность 11, 320 Указатель 80 Универсальная программа решения задач СР 174 Универсальность 12, 320 Уничтожение записи 96 Управляющая группа 104—105 — конструкция 183—185 Управляющее поле 77, 216, 320 Управляющее прерывание 77, 102, 117, 120, 131—133, 320 Управляющий граф 249—251 Условие несущественное 210 — неформальное 191 — «почти нормальное» 191—192 — составное 231 Утверждения 277—282 Файл вспомогательный 87 — выбор организации 97, 98 — доступ 90, 93, 95, 319 — копирование 87 — обработка 95—97 — организация индексированная 93—95, 318 ---последовательная 90—92, 97—98, 319 ---с относительной нумерацией записей 92, 97-98, 320 — регистрационный 48—50, 88 — сопровождение 43, 47, 130, 135 — типы 87, 89 Физическая организация внешних данных 87 Флаг отладки 239 Формальное определение 82, 83 Фортран 71, 91, 186, 222, 228, 246, 300, 320 Функциональная декомпозиция 103 Функциональная схема системы 45 ---учитывающая синхронизацию процесс сов 48 — условные обозначения 46 Хэширование 86 Таблица перекрестных ссылок 250 Таблица решений 130, 320 ----вход действий 209 ---- избыточная 211 ---- полная 209 ---- противоречивая 211 ----совмещение столбцов 211 ----с ограниченным входом 209 ----с расширенным входом 209 ----со смещенным входом 210 ----столбец действий 209 Таблица связей ПО ' Тестирование ветвлений 272—273 — нестандартных ситуаций 274 — путей 269—271 — путем фиктивного выполнения програм- мы 275, 276 — специальных значений 273—275 — структур управления 271, 272 *— текста программы 296—301 ЦикломатиЧеское число 240 Цикл со счетчиком индекса 200 Черный ящик 320 Численное интегрирование 159 Шифрование 288—290 Шифрование с общим ключом 290 Эвристики 142, 320 Экономия времени 301—309, 311 Экономия памяти 299—301 Эффективность 12 — модуля 215 — работы программиста 293 Язык управления заданиями 40, 56
ОГЛАВЛЕНИЕ Предисловие редактора перевода.................................. 5 Предисловие автора . ............................................. 7 Глава 1. ВВЕДЕНИЕ..............................................10 1.1. Качество программных систем.....................11 1.2. Постановка задачи..................................16 1.3. Проектирование системы.............................19 1.4. Вспомогательные средства проектирования............25 1.5. Системная документация.............................30 1.6. Упражнения.........................................33 Глава 2. ПРОЕКТИРОВАНИЕ СИСТЕМ.................................35 2.1. Определение основных компонентов системы .... 35 2.2. Методы разработки данных...........................43 2.3. Методы разработки средств управления...............43 2.4. Проектная документация.............................55 2.5. Упражнения.........................................53 Глава 3. МЕТОДЫ ОРГАНИЗАЦИИ ДАННЫХ..............................57 3.1. Типы данных.........................................57 3.2. Уровни организации данных...........................61 3.3. Уровень логической организации данных...............65 3.4. Представление данных................................70 3.5. Физическая организация данных.......................83 3.6. Документирование данных.............................93 3.7. Упражнения.........................................100 Глава 4. ПРОЕКТИРОВАНИЕ ПРОГРАММ.................................101 4.1. Метод нисходящего проектирования......................ЮЗ 4.2. Метод расширения ядра................................117 4.3. Метод восходящего проектирования.....................125 4.4. Анализ внутреннего потока данных.....................126 4.5. Вспомогательные средства проектирования программ . * 129 4.6. Программная документация.............................135 4.7. Упражнения ......................................А 137 Глава 5. АЛГОРИТМЫ...........................................130 5.1. Типы алгоритмов..................................133 5.2. Способы реализации алгоритмов....................146 5.3. Методы построения алгоритмов....................154 5.4. Документация алгоритмов..........................179 5.5. Упражнения.......................................179 Глава 6. ПРОЕКТИРОВАНИЕ МОДУЛЕЙ................181 6.1. Структурированные алгоритмы...........................183 6.2. Схемы передач управления............................, 193
328 Оглавление 6.3. Управляющие таблицы...................................203 6.4. Документация модулей..................................214 6.5. Упражнения............................................216 Глава 7. РЕАЛИЗАЦИЯ ПРОГРАММНОГО МОДУЛЯ .... 217 7.1. Подходы к реализации.................................217 7.2. Реализация данных.....................................220 7.3. Реализация ввода-вывода...............................224 7.4. Реализация управления.................................228 7.5. Сложность программы...................................240 7.6. Оформление программы..................................244 7.7. Вспомогательные средства, используемые при реализации 248 7.8. Упражнения............................................251 Глава 8. ПРОВЕРКА ПРАВИЛЬНОСТИ ПРОГРАММ.......................253 8.1. Обнаружение ошибок . ........................253 8.2. Тестирование модулей . . '.......................268 8.3. Формальные методы доказательства правильности про- грамм ................................................276 8.4. Оценки ошибок.....................................283 8.5. Средства защиты программных систем................286 8.6. Качество документации.............................291 8.7. Упражнения........................................291 Глава 9. ОПТИМИЗАЦИЯ ПРОГРАММ................................293 9.1. Экономия памяти...................-..............294 9.2. Экономия времени.................................301 9.3. Повышение эффективности программ.................303 9.4. Средства оптимизации.............................314 9.5. Документирование показателей эффективности программы 315 9.6. Упражнения.......................................316 Список терминов.......................................317 Литература............................................321 Предметный указатель..................................324 УВАЖАЕМЫЙ ЧИТАТЕЛЬ! Ваши замечания о содержании книги, ее оформлении, качест- ве перевода и другие просим присылать по адресу: 129820, Москва, И-110, ГСП, 1-й Рижский пер., д. 2, изд-во «Мир».