Тема 2. Основы структурного проектирования алгоритмов. Файл 308820029 С. 1 из 7 Тема 2. Основы структурного проектирования алгоритмов 1. Сущность структурного программирования. Базовые структуры алгоритмов 2. Язык псевдокод 3. Преобразование и некоторые типовые структуры алгоритмов Материал к теме: базовые структуры алгоритмов; файлы bas_str1.doc и bas_str2.doc. В данной теме речь идет о двух принципах структурной методологии: структурном программировании и надъязыковом подходе. 1. Сущность структурного программирования. Базовые структуры алгоритмов Принцип структурного программирования состоит в следующем: на любом этапе проектирования алгоритма порядок выполнения действий задается одной из трех конструкций (структур): следованием; развилкой (ветвлением, альтернативой); повторением (циклом). Суть этих структур хорошо отображается в графической форме средствами языка блок-схем, а в текстовой – средствами языка псевдокод, детально рассматриваемого в вопросе 2. При описании базовых структур используется понятие функционального блока. Функциональный блок – это любая допустимая совокупность действий, рассматриваемая как единое целое и имеющая один вход и один выход. В предельном простейшем случае функциональный блок может быть представлен одной элементарной операцией изменения данных – вводом, выводом, присваиванием или вызовом процедуры. Ниже функциональные блоки, входящие в состав базовых конструкций, обозначены S, S1, S2, … Sn. Следование представляет собой последовательность действий. S1 S2 Запись на псевдокоде: … Sn S1; S2; … Sn; Развилка обеспечивает выбор одной из двух альтернатив. истина ложь В S1 S2 Запись на псевдокоде: если В то S1 иначе S2 кесли; Если условие B истинно, то выполняется блок S1, иначе – блок S2. Каждый из путей (альтернатива то, альтернатива иначе) ведет к общей точке слияния, так, что по завершении выполнения конструкции обработка продолжается независимо от того, какой путь был выбран. Цикл обеспечивает многократное повторение действий. ложь В истина S Запись на псевдокоде: пока В цикл S кц; Действие (блок) S выполняется, пока условие В истинно. Как только условие становится ложным, цикл завершается. Если до начала выполнения цикла условие ложно, то S не выполнится ни разу. Приведенный цикл называется цикл-пока. Тема 2. Основы структурного проектирования алгоритмов. Файл 308820029 С. 2 из 7 Альтернативная форма базовой структуры цикла – цикл-до: Запись на псевдокоде: S цикл S до В; ложь В истина Действие S выполняется до момента, когда условие В станет истинным. В отличие от предыдущей конструкции, здесь S обязательно выполняется хотя бы один раз. Эти конструкции преобразуемы одна в другую, и каждая из них может быть выбрана в качестве базовой. В курсе предпочтение отдается циклу-пока. Алгоритм, построенный на основе базовых конструкций, называется структурированным. Заметим, что все базовые конструкции имеют один вход и один выход, т.е. являются функциональными блоками. Использование ограниченного числа конструкций позволяет по возможности формализовать процесс построения и анализа алгоритма и получить ясный, легкий для понимания алгоритм. Кроме того, принцип структурного программирования лежит в основе доказательства правильности (верификации) алгоритмов1. При структурном подходе анализ данных и управление вычислительным процессом, т.е.операции, определяющие сложность алгоритма, определяются приведенными конструкциями. Является ли перечисленный набор конструкций достаточным для построения любой программы? Ответ на этот вопрос содержит принцип, называемый «структурной теоремой» и гласящий: всякая реальная программа может быть построена с использованием трех перечисленных конструкциий: следования, развилки, повторения. Это и опредеяет их название: базовые управляющие структуры. Доказательство теоремы состоит в преобразовании алгоритма с произвольной структурой (т.е. с произвольными переходами между операторами, создающими «блюдо спагетти») в структурированный алгоритм путем формального применения ряда правил. Такой процесс называется структурированием. Поскольку преобразования применимы к любому алгоритму, теорема полагается доказанной. Следствие из структурной теоремы: любой неструктурированный алгоритм может быть структурирован. Замечание. Процесс написания алгоритма с произвольной структурой (по принципу «так быстрее и проще»), а затем его структурирование дает очень малонаглядный результат, так как распутывание «блюда спагетти» делается за счет введения дополнительных переменных и развилок. Реально гораздо проще и эффективнее сразу проектировать алгоритм как структурированный, т.е. описывать его средствами базовых структур. Это связано с тем, что по существу базовые конструкции отображают наш способ формального мышления при описании любой деятельности. На практике удобно использовать избыточную систему базовых структур, так как существуют целые классы задач, где можно использовать усеченный или модифицированный вариант конструкции, и это упрощает запись. Как правило, в языках проектирования и высокоуровневых языках программирования наличествует три вида развилок и три вида циклов. Полностью базовые структуры, их описание и кодирование на языках Си и Паскаль приведены в файлах bas_str1.doc и bas_str2.doc. Ветвление если-то используется, если действие выполняется только по одной ветви. В таком случае условие в ветвлении надо записывать так, чтобы действие выполнялось в ветви то. Например, кн. Лингер Р.,Миллс Х., Уитт Б. Теория и практика структурного программирования. – М.: Мир, 1982 содержит сильный математический аппарат конструирования и анализа алгоритмов, базирующийся на исчислении предикатов. 1 Тема 2. Основы структурного проектирования алгоритмов. Файл 308820029 С. 3 из 7 Множественный выбор используется, когда некоторое множество действий выполняется в зависимости от значения некоторого ключа, имеющего более двух значений; выбор заменяет последовательность или вложение развилок. О циклах пока и до сказано выше. На практике могут быть использованы оба, но предпочтительно не прибегать к двум конструкциям там, где достаточно одной. Цикл-до лучше использовать только там, где есть уверенность, что он обязательно должен быть выполнен хотя бы один раз и более наглядно описывает нужные действия. Цикл-для, или цикл с параметром, используется, когда число повторений цикла известно в алгоритме. В языках программирования он реализован по схеме цикла-пока. Реализация развилок в языках Паскаль и Си схожа. Что касается циклов, то в Паскале цикл-для является частным случаем цикла-пока (параметр инициализируется и изменяется с шагом 1 или -1, а условие состоит в сравнении текущего значения параметра с конечным), а в языке Си этот цикл, напротив, реализован как самый общий и позволяет в разделах инициализации и коррекции записывать любые операции, причем более одной в каждом разделе (что может привести к потере читабельности алгоритма и чего делать не следует). Следует помнить о специфике синтаксиса развилок и циклов в языках программирования: в ветвях развилок и в циклах выполняется один оператор, простой или составной. Поэтому при необходимости выполнения в этих конструкциях действий произвольной структуры следует их оформить как составной оператор (заключить в операторные скобки). 2. Язык псевдокод Традиционный способ представления алгоритмов – графический язык блок-схем. Его достоинством является наглядность. Это важное достоинство, но проявляется оно только при описании алгоритма на достаточно общем уровне, для выделения общей его структуры и фиксации основных подзадач. Недостатки языка блок-схем: принципиальное отличие от записи программы – блок-схема двумерна, запись программы линейна; отсутствие средств описания данных; отсутствие средств описания процедур; громоздкость. Общепринятым языком описания алгоритмов в настоящее время является псевдокод. Псевдокод – неполностью формализованный язык, принципиально допускающий словесное описание алгоритма. С одной стороны, запись алгоритма на псевдокоде близка к естественноязыковой; с другой стороны, конструкции псевдокода достаточно формализованы и представляют собой надъязыковой (обобщенный) вариант описания структур данных и базовых конструкций конкретных языков программирования. Возвращаясь к трехуровневой схеме решения задач с помощью компьютера (тема 1, рис. 1.1), можно сказать, что псевдокод – это средство описания задачи на логическом уровне. Использование псевдокода на этапе проектирования – также один из принципов структурной методологии программирования (см. тему 1). Цель такого использования – создание единого нормативного начала в программировании при наличии огромного количества конкретных систем программирования. До какой степени должны быть формализованы средства псевдокода? – Это зависит от класса решаемых задач и используемых средств программирования (т.е. от соотношения абстрактного и физического уровней постановки и решения задачи). В любом случае псевдокод должен быть таким, чтобы можно было описать проект решения: строго и кратко; не теряя смысловой связи с задачей; не используя несущественных для описания логики решения деталей конкретного языка программирования. Тема 2. Основы структурного проектирования алгоритмов. Файл 308820029 С. 4 из 7 Примером псевдокода, построенного на формальной основе и имеющего строгий синтаксис, является язык PDL (Process Design Language) приведенный в кн. Лингера и др. (см. ссылку на с. 2 темы). Язык не нашел широкого применения из-за его универсальности, приведшей к обратному: конструкции языка порой настолько близки к конструкциям того же Паскаля, что теряется смысл их использования как надъязыковых. Реально эффективнее использовать более свободный синтаксис языка проектирования и формализовать его в зависимости от класса задач. Например, если речь идет о достаточно сложных алгоритмах обработки достаточно простых данных, то псевдокод должен включать средства описания алгоритма, «очищенные» от особенностей языка программирования; описание данных может быть приближено к языку программирования. То же касается структур данных. При необходимости средства псевдокода можно расширять, вводя новые структуры данных и/или операции. Так, при работе с записями (record в Паскале) можно ввести в псевдокод соответствующие структуры данных; при работе с динамической памятью можно ввести ряд операций с адресами (например, при проектировании алгоритма на Паскале можно определить операцию увеличения адреса так, как это сделано в Си,; реализацией такой операции будет последовательность операторов Паскаля). Таким образом, «псевдокод» – это скорее некоторое условное собирательное название для средств описания проекта решения задачи, а не жестко определенный единожды язык. В «классической» части нашего курса акцент делается на проектировании алгоритма, что и определяет используемые средства псевдокода. По мнению Макконнелла1, значение псевдокода трудно переоценить, поскольку он: упрощает пересмотр конструкции алгоритма; поддерживает идею итеративного усовершенствования; упрощает внесение изменений; упрощает комментирование и др. Помимо прочего, это один из наиболее удобных видов документации, упрощающий сопровождение программного продукта. Операторам псевдокода являются: элементарные операторы – ввод, вывод, присваивание, вызов процедуры; управляющие операторы, т.е. базовые структуры алгоритмов, описанные выше. Таким образом, псевдокод поддерживает структурный подход. Два принципа методологии – надъязыковой подход и структурное программирование –тесно взаимосвязаны. Для записи алгоритмов на псевдокоде будем использовать следующий синтаксис. алг <имя алгоритма> (<список имен входных и выходных данных>); арг Курсивом выделены ключевые слова – фиксиро<описания входных данных>; ванные последовательности символов с предрез определенным смыслом. <описания выходных данных>; В любое место алгоритма можно вставить комнач ментарий: <описания промежуточных данных>; {<пояснительный текст>} <операции > кон; Операторы псевдокода см. в примере кон <имя алгоритма>; Примеры записи алгоритмов наличествуют в лабораторных работах. В целях наглядности (при более глубоком рассмотрении – во избежание многих ошибок) запись алгоритма делается рельефной. Рельеф для общей структуры алгоритма показан выше. Для отступа оптимальны 2-3 позиции. 1 Макконнелл С. Совершенный код. – СПб, Питер, 2005. – 896 с – с. 212 – 213. Тема 2. Основы структурного проектирования алгоритмов. Файл 308820029 С. 5 из 7 В теле алгоритма рельеф делается по схеме: последовательность операторов записывается на одном уровне с отступом в 2 позиции от ключевых слов нач и кон; в ветвлениях операции в каждой из ветвей смещаются вправо на 2 позиции; в циклах операции, выполняемые в цикле, смещаются вправо на 2 позиции. Пример записи последовательности операторов np:=0; s:=0. для i от 1 до n цикл si:=sqrt(x[i]*x[i]+y[i]*y[i]); s:=s+si; если si>r & x[i]>0 & y[i]>0 то np:=np+1; кесли; кц; p=(np/n)*100; sa=s/n; При такой записи значительно снижается вероятность неправильного вложения или завершения конструкций. Код программы также следует записывать с рельефом. Помимо описанного выше, рельеф делается и при записи составного оператора: его содержимое должно быть смещено вправо относительно операторных скобок begin и end. Пример записи приведен ниже. program points(input,output);{ввод с клавиатуры, вывод на экран} uses crt; {подключение модуля для работы с экраном} const nmax=100; {верхняя граница массива} var n,i:integer; r,p,sa:real; x,y:array[1..nmax] of real; begin {A0.1 - ввод-вывод входных данных} ............ {A0.2. Обработка} {А0.2 - обработка} np:=0; s:=0; for i := 1 to n do begin si:=sqrt(x[i]*x[i]+y[i]*y[i]); s:=s+si; if (si>r) and (x[i]>0) and (y[i]>0) then np:=np+1; end; p=(real(np)/n)*100; sa=s/n; {A0.3 - вывод результатов} writeln(' Искомый процент точек = ',p:4:1); {p по обр9} writeln(' Среднее удаление = ',sa:4:1); {sa по обр9} end. Для описания и вызова процедур на псевдокоде будем использовать синтаксис, аналогичный синтаксису описания алгоритма Тема 2. Основы структурного проектирования алгоритмов. Файл 308820029 С. 6 из 7 •• Процедура общего вида (подпрограмма) Описание {назначение процедуры} проц <имя процедуры>(<список формальных параметров>); арг <список описаний входных параметров>; рез <список описаний выходных параметров>; нач <список описаний локальных переменных>; <операторы> кон; кон <имя процедуры>; Вызов (обращение) записывается как отдельный оператор в вызывающем алгоритме: <имя процедуры>(<список фактических параметров>); •• Функция Описание <тип>функ<имя функции>(<список входных форм. параметров>); арг <список описаний входных параметров>; нач <список описаний локальных переменных>; <операторы вычисления результирующего значения> <имя функции>:=<значение>; кон; кон <имя функции>; Единственное выходное значение присваивается имени функции. Во избежание неконтролируемых ошибок целесообразно делать это присваивание только один раз и соответствующий оператор записывать последним из операторов обработки. Вызов (обращение) осуществляется через указатель функции – конструкцию следующего вида: <имя функции>(<список фактических параметров>) Поскольку алгоритм на псевдокоде – это текст проекта, не обрабатываемый компилятором, процедуры можно описывать и размещать независимо от вызывающего алгоритма и отдельно от него (на отдельных листах и/или в отдельных файлах). Код процедуры должен быть размещен в соответствии с правилами языка программирования. При работе в среде Паскаля (turbo или borland) процедуры размещаются в разделе процедур программы program(внутренние процедуры) или в отдельном модуле unit. При работе в среде Delphi процедуры размещаются в разделе процедур модуля формы (внутренние процедуры) или в отдельном модуле unit. 3. Преобразование и некоторые типовые структуры алгоритмов Итак, в схемах базовых структур S , S1, S2, … Sn – функциональные блоки; сами базовые структуры также являют собой функциональные блоки. Таким образом, внутри любой из структур в качестве S , S1, S2, … Sn может выступать любая структура этого же набора. В таком случае справедливы преобразования «структура – функциональный блок»: пока В цикл S кц; если В то S1 иначе S2 кесли; S1 S и т.д. Тема 2. Основы структурного проектирования алгоритмов. Файл 308820029 С. 7 из 7 Преобразование – укрупнение алгоритма. Используется для понимания алгоритма, когда нас не интересуют детали, и доказательства его правильности. Преобразование – детализация. Используется в процессе проектирования алгоритма по нисходящей схеме, т.е. исходя из единого функционального блока, соответствующего задаче в целом, который постепенно раскрывается в сложную структуру. При этом алгоритм остается структурированным на любом уровне детализации. Некоторые типовые структуры алгоритмов В конструкции цикла S – ветвление: цикл с разветвлением пока B цикл если B1 то S1 S иначе S2 кесли кц; В конструкции цикла S – цикл: вложенные (кратные) циклы пока B1 цикл пока B2 цикл S S1 кц; кц; Таким образом, раскрывая функциональный блок в какой-либо базовой конструкции также как базовую конструкцию, можно получить сколь угодно сложные алгоритмы, сохраняющие структурированность.