dt=Text B. Парадигмы программирования. 0 Цель главы: 1. Познакомить читателей с основными парадигмами программирования, применяемых в современном программировании; 2. Научить читателей программировать, читать алгоритмы, создавать программы, на языках программирования, входящих в основные парадигмы; 3. Научить правильному выбору парадигмы программирования и языка для использования в своих проектах. Содержание B.1. Введение. Общие сведения о парадигмах B.2. Процедурная парадигма B.3. Логическая парадигма B.4. Объектно-ориентированная парадигма B.5. Резюме 5P9i0s8y19Z dt=Text ! Л И Т Е Р А Т У Р А 1 1 Матросов А.В., Чаунин М.П. Perl. Программирование на языке высокого уровня. Учебник для вузов. – СПб.: Питер, 2003. – 608 с.:ил. 2 Бочков С.О., Субботин Д.М. Язык программирования Си для персонального компьютера. Под общей редакцией П.И. Садчикова – М.: Радио и связь, 1990. – 384 с. 3 Козелл Е.И., Романовская Л.М., Русс Т.В., Свитковский С.Г., Шапетько Т.Н От Си к С++ – М.:Финансы и статистика, 1993. – 272 с.:ил. 4 Савитч, У. Язык Java. Курс программирования. 2-е изд., Пер. с англ., – М., Издательский дом «Вильямс», 2002. – 928 с.: ил. 5 Фролов А.В., Фролов Г.В. Графический интерфейс GDI в MS Windows (Библиотека системного программиста; Т. 14) – М.: «ДИАЛОГ-МИФИ», 1994. – 288 с. 6 Фролов А.В., Фролов Г.В. Модемы и факс-модемы. Программирование для MS-DOS и Windows. (Библиотека системного программиста; Т. 16) – М.: «ДИАЛОГ-МИФИ», 1995. – 284 с. 7 Фролов А.В., Фролов Г.В. Операционная система Microsoft Windows 3.1 для программиста. В 3-х ч. Ч. 3. (Библиотека системного программиста, Т. 13) – М.: «ДИАЛОГ-МИФИ», 1994. – 288 с. 8 Фролов А.В., Фролов Г.В. Операционная система Microsoft Windows 3.1 для программиста. (Библиотека системного программиста; Т. 11) – М.: «ДИАЛОГ-МИФИ», 1994. – 269 с. 9 Фролов А.В., Фролов Г.В. Операционная система Microsoft Windows 3.1 для программиста. В 3-х ч. Ч. 2. (Библиотека системного программиста, Т. 12) – М.: «ДИАЛОГ-МИФИ», 1994. – 269 с. 10 Фролов А.В., Фролов Г.В. Операционная система Microsoft Windows 3.1 для программиста. (Дополнительные главы). (Библиотека системного программиста; Т. 17) – М.: «ДИАЛОГ-МИФИ», 1995. – 288 с. 11 Хикс, Клинт C (серия «Без проблем»!) Пер. с англ. Тимофеев В.В., Лубков А.В. – М.: Восточная Книжная Компания, 1997. – 448 с.:ил. 12 Страуструп Б. Язык программирования Си++ Пер. с английского. Пиголкина М.Г., Яницкого В.А. – М.: Радио и связь, 1991. – 352 с.: ил. 13 Кушниренко А.Г., Лебедев Г.В., Сворень Р.А. Основы информатики и вычислительной техники Проб. Учебник для средних учебных заведений. – 2-е изд. – М.: Просвещение, 1991. – 224 с.: ил. 14 Очков В.Ф., Пухначёв Ю.В. 128 советов начинающему программисту. – М.: Энергоатомиздат, 1991. – 256 с.: ил. 5P9i0s8y19Z dt=Text B.1. Введение 1 Общие сведения о парадигмах программирования За те пятьдесят с небольшим лет, которые существует дисциплина: «программирование», – сменилось несколько поколений правил, концепций, моды, взглядов на то, как надо «писать программы». Все эти правила, концепции, взгляды, мода, наконец, позже назвали в информатике «парадигмами программирования». Вот неполный список парадигм, вместе с датами их появления: * 1957г., процедурная парадигма, язык ФОРТРАН, создатель Бэкус; * 1967г., функциональная парадигма, язык ЛИСП, создатель Маккарти; * 1970г., структурированная парадигма, язык ПАСКАЛЬ, создатель Вирт; * 1974г., логическая парадигма, язык ПРОЛОГ; * 1983г., объектно-ориентированная парадигма, пример – язык C++, создатель Страуструп. В конце списка представлены языки программирования и их создатели, внёсшие значительный вклад в популяризацию этих парадигм. В процессе своего развития эти парадигмы «мутировали», «отпочковывались», «объединялись» (так, язык Си объединил в себе процедурную и структурированную парадигму). В результате этих «мутаций» по состоянию на 01.06.2008 года оформились следующие парадигмы программирования: 1. Процедурная (или модульная) парадигма; 2. Логическая парадигма; 3. Объектно-ориентированная парадигма. Эти три совершенно разные по подходу парадигмы составляют основу современного программирования. Хотя в настоящее время наиболее «модной» является объектно-ориентированная парадигма, остальные парадигмы также используются на практике и изучаются в ВУЗах. Их мы и рассмотрим ниже. Кроме деления языков программирования по парадигмам, существует также деление языков на «императивные» и «декларативные». Императивными называются такие языки программирования, в которых описываются в основном «инструкции» по пошаговому выполнению алгоритмов. Всё внимание в них отводится лишь реализации этой последовательности действий, а данным и их структуре отводится второстепенная роль. Примером такого рода языков может служить языки создания драйверов устройств: Assembler и C- -. При использовании этих языков мы не знаем, какие данные будет отправлять/получать устройство, но зато мы знаем, какие действия нужно осуществить над данными. Декларативными называются языки программирования, в которых алгоритм работы с данными «зашит» в языке программирования, а сама программа представляет собой данные, упорядоченные и структурированные таким образом, что их легко обрабатывают «стандартные» алгоритмы вывода. В декларативных языках как бы «нет разницы» между данными и алгоритмом, их обрабатывающим. От «значения» данных, подаваемых на вход программы, зависит дельнейшее направление расчётов. Примерами декларативных языков могут служить языки: LISP и Prolog. И программа, и данные на языке ЛИСП представляют собой «списки» вместе с функциями, выполняемыми над ними. На языке ПРОЛОГ есть условное разделение программы на данные («факты») и код («правила», «продукции»). Но это разделение достаточно условно: и факты, и правила имеют одну и ту же форму записи. Декларативные языки лучше всего использовать в случаях, когда «данные управляют программой»: при написании экспертных систем, при конструировании трансляторов с языков программирования, для большинства задач искусственного интеллекта. Именно там их использование приведёт к наибольшей эффективности. 5P9i0s8y19Z dt=Text B.2. Процедурная парадигма 1 Содержание B.2.1. История возникновения парадигмы B.2.2. Языки, поддерживающие парадигму B.2.3. Особенности этой парадигмы B.2.3.1. Конструкции языка B.2.3.2. Представление программ и реализация вычислений B.2.3.3. Особенности метаязыка === *** === *** === Эта парадигма является самой «древней» и, одновременно, – самой простой в реализации и обучении программирования. На основе этой парадигмы изучаются языки программирования в школе. И поэтому, вполне естественно, автор поставил её на первое место. 5P9i0s8y19Z dt=Text B.2.1. История возникновения парадигмы 2 «Истоки» процедурной парадигмы программирования лежат в далёких 50-х годах прошлого века. После появления ЭВМ с архитектурой «фон Неймана» появилась возможность компьютеру «самому», без участия человека, проводить сложные вычисления. Эти годы и стали переломными в становлении профессии «программист». Чтобы произвести сложные вычисления, нужно сначала написать сложный алгоритм. Поскольку сложный алгоритм трудно реализовать в ЭВМ так же, как на калькуляторе, появилась необходимость в «записи» алгоритмов на более «понятном» человеку языке, чем язык машинных кодов ЭВМ. Так, вначале появился язык ассемблера, представляющий машинные коды в «удобочитаемом» виде. В 1957 году Бэкус разработал первый язык высокого уровня: ФОРТРАН. Эта аббревиатура переводится как: «FORmula TRANslator», – переводчик формул. Как следует из его названия, его назначение – это числовые вычисления. С помощью этого языка можно было «кодировать» численные алгоритмы, не вдаваясь в архитектуру ЭВМ, количество регистров в её процессоре, особенности её функционирования, и другие, «не важные» для прикладного программиста вопросы. Вскоре, в начале 60-х годов прошлого века, появился язык: «COBOL», – язык, предназначенный для коммерческих вычислений. Аббревиатура «COBOL» расшифровывается как: «COmmon Business Oriented Language» – «язык, ориентированный на общий бизнес». В реализации этих языков была предусмотрена (как и в ассемблере) возможность «разбивки общего алгоритма» на несколько независимых модулей: «процедур». «Нанизывая» процедуры в основном модуле программы, как гирлянды на нитку, можно получить алгоритмы любой сложности. В этом и состоит преимущество процедурного программирования. Таким образом, возникла «процедурная парадигма», гласящая: «Реализацию алгоритмов вычислений необходимо создавать с помощью мелких, не зависимых друг от друга процедур, которые вызывают друг друга в соответствии с логикой программы». Эта парадигма проста не только в «написании» алгоритма, но и его отладке: нужно убедиться в «работоспособности» каждого из модулей, что намного проще, чем отладка всего алгоритма целиком. 5P9i0s8y19Z dt=Text B.2.2. Языки, поддерживающие парадигму 2 Среди языков, поддерживающих процедурную парадигму программирования, используются следующие языки программирования: * Ассемблер; * Фортран; * Кобол; * Алгол; * PL 1; * Бейсик; * Си; * Паскаль; * Perl; * VB Script; * Jscript; * Multi Edit Macro Script; * MS-DOS Command Shell; * NDOS Shell; * Bash shell; * tc shell; * REXX; * язык SQL-запросов; * и др. Все они поддерживают создание «модулей» – функций и подпрограмм, которые разрабатываются и тестируются независимо, и осуществление «вызовов» между ними. 5P9i0s8y19Z dt=Text B.2.3. Особенности этой парадигмы 2 Содержание B.2.3.1. Конструкции языка B.2.3.2. Представление программ и реализация вычислений B.2.3.3. Особенности метаязыка 5P9i0s8y19Z dt=Text B.2.3.1. Конструкции языка 3 Содержание. B.2.3.1.01. Оператор присваивания. B.2.3.1.02. Оператор условия. B.2.3.1.03. Оператор «ветвления» B.2.3.1.04. Оператор цикла: «Пока» B.2.3.1.05. Оператор цикла: «до … пока» B.2.3.1.06. Оператор цикла с индексом B.2.3.1.07. Оператор множественного выбора B.2.3.1.08. Оператор выхода из цикла: «break» B.2.3.1.09. Оператор новой итерации: «continue»; B.2.3.1.10. Оператор безусловного перехода: «goto» B.2.3.1.11. Оператор вызова процедуры/функции B.2.3.1.12. Операторы ввода-вывода B.2.3.1.13. Составной оператор B.2.3.1.14. Оператор выхода из процедуры B.2.3.1.15. Резюме === *** === *** === В качестве основных конструкций языков, построенных на «процедурной парадигме», используются: 1. Оператор присвоения значений; 2. Операторы условия «if … then»; 3. Операторы ветвления «if … then … else …»; 4. Операторы цикла: «пока» (while); 5. Оператор цикла: «до … пока» (do … while); 6. Оператор цикла с индексом; 7. Оператор множественного выбора; 8. Оператор выхода из цикла: «break»; 9. Оператор новой итерации: «continue»; 10. Оператор безусловного перехода: «goto»; 11. Оператор вызова процедуры/функции; 12. Операторы ввода-вывода; 13. Составной оператор; 14. Оператор выхода из процедуры. Рассмотрим эти операции поподробнее. 5P9i0s8y19Z dt=Text B.2.3.1.01. Оператор присваивания. 4 Основной оператор «присвоения значений» переменным обозначается как «=» или »:=». Его синтаксис следующий: <переменная> = <выражение> [B.001], где <переменная> – идентификатор, задающий имя переменной определённого типа, а <выражение> – новое значение переменной, представляющее собой константу, переменную, арифметическое, логическое, символьное выражение либо вызов функции. Замечание. При присвоении переменной нового значения старое значение теряется безвозвратно. Это надо помнить. Также значение теряется при выходе из блока или функции, в котором была объявлена переменная. Оператор присвоения значений обозначается в блок-схеме как прямоугольник. Примечание: при реализации оператора присвоения значений тип данных, под которым определена переменная, может не совпадать с типом данных, возвращаемых выражением. Поэтому может потребоваться операция принудительного приведения типов. По поводу операций приведения типов смотри [[c0000.htm]часть C] данного учебника. 5P9i0s8y19Z dt=Text B.2.3.1.02. Оператор условия. 4 Этот оператор служит для реализации реакции алгоритма на некоторое условие: значения переменной, или событие в ЭВМ и её окружении. Синтаксис оператора следующий: IF <условие> THEN <оператор> [B.002] где <условие> – переменная булевского (логического) типа или операция отношения между целыми, вещественными, символьными константами, переменными и выражениями, а <оператор> – любой допустимый оператор языка, группа этих операторов в скобках (в блоке) либо (в старых языках программирования) – оператор безусловного перехода на метку. Оператор вычисляется в случае истинности условия в этом операторе. Примечание: меткой называется номер строки, число либо идентификатор, стоящий перед оператором и указывающий, куда будет перенаправлено выполнение программы после оператора перехода. Замечание: на операции отношения в арифметике плавающей точки накладываются ограничения, а именно – исключение из рассмотрения операций отношения «равно» и «не равно». Поскольку вещественные числа представляются в виде «фрагмента» бесконечной дроби, чувствительного к ошибкам округления, то, например, выражение: 2.0 * 2.0 – может не равняться константе 4.0. Следовательно, отношение «равно» переменных с плавающей точкой будет всегда «ложь», а отношение «не равно» – всегда истина. То же самое относится и к операторам отношения: «больше или равно» и «меньше или равно». Оператор: «условие» на блок-схеме представляется так, как показано на [[#b001f]рисунке B.001]: Рис. B.001. Оператор «условие» на блок-схеме. Оператор условия является самым «древним» оператором изменения последовательности действий операторов алгоритма. Он применялся ещё в релейной ЭВМ К. Цюзе. Примеры написания условия: [Пример 01] /* 1. Переменная D больше нуля? (Да/Нет) */ D > 0 /* 2. Переменная x лежит в пределе значений от -1 до +1 (на языке Си) */ x >= -1 && x <= 1 REM 2. То же самое на языке Бейсик X >= -1 AND X <= 1 /* 3. Файл, связанный с дескриптором: fstream, не прочитан до конца (переменная eof не равна 0, язык Си) */ !eof( fstream ) 5P9i0s8y19Z dt=Text B.2.3.1.03. Оператор «ветвления» 4 Этот оператор очень похож на оператор условия. Его синтаксис следующий: if <условие> then <оператор-истина> else <оператор-ложь> [B.003] где <условие> – то же, что и в [[#b02.3.1.02]разделе B.2.3.1.02], <оператор-истина> – оператор или группа операторов, выполняемых в случае истинности условия, а <оператор-ложь> выполняется в случае невыполнения условия оператора ветвления. После выполнения оператора ветвления продолжается выполнение последовательности операторов, нарушенной при вызове оператора ветвления. На блок-схеме оператор «ветвления» представлен так, как показано на [[#b002f]рисунке B.002]. Рис. B.002. Оператор ветвления на блок-схеме. Оператор ветвления появился сравнительно недавно, в 1970 году в языке: «Паскаль». 5P9i0s8y19Z dt=Text B.2.3.1.04. Оператор цикла: «Пока» 4 Операторы цикла предназначены для многократного повторения одних и тех же операций (операторов) в программе. Цикл завершается после выполнения условий окончания цикла, после чего следуют остальные операторы алгоритма. Если выхода из цикла нет, то говорят, что программа: «зацикливается». Самым «строгим» циклом (в смысле его соответствия правилам структурированной парадигмы программирования) является цикл: «Пока». Его синтаксис следующий: while <условие> do <оператор> [B.004] где <условие> – условие входа в цикл и его окончания (в случае несовпадения значения выражений этому условию продолжения цикла). Условием может быть переменная логического типа, или операция отношения между целыми, вещественными, символьными константами и переменными, и выражений с ними, <оператор> – любой допустимый оператор или блок операторов языка программирования. Примеры операторов цикла на языке Си представлены в примере 02. [Пример 02] /* 1. Чтение и печать всех строк из текстового файла (до тех пор, пока не кончился файл) */ /* Описание констант */ const short MAXSTR = 255; /* Описание переменных */ FILE *stream; char str[MAXSTR]; … while( !eof( stream ) ) // Пока не достигнут конца { // потока stream цикл fgets( str, MAXSTRING, stream ); // Читается файл в строку puts( str ); // печать строки на экране дисплея } // Конец цикла … /* 2. Реализация функции «эникейщиков» (цикла пока не нажата клавиша). */ char c; // Символ ввода с = '\0'; // условие продолжения цикла while( !c ) c=getc(); // Бесконечный цикл, пока не введён с клавиатуры символ c. На блок-схеме оператор цикла: «пока» – имеет вид, приведённый на [[#b003f]рисунке B.003]: Рис. B.003. Оператор цикла: «пока», – на блок-схеме. Как видно из указанных примеров, для реализации цикла «пока» должны быть выполнены следующие условия: * условие входа в цикл; * условие выхода из цикла; * последнее условие должно меняться в теле цикла. Вообще цикл «пока» соответствует модификатору «звёздочка» в регулярном выражении (то есть повторению ноль или более раз). О синтаксисе регулярных выражений смотри [[g0000.htm#g043]раздел G.4.3]. 5P9i0s8y19Z dt=Text B.2.3.1.05. Оператор цикла: «до … пока» 4 Этот оператор аналогичен оператору цикла «пока», за исключением того, что операторы в теле цикла будут исполняться минимум один раз. Синтаксис этого оператора следующий: do <оператор> while <условие> [B.005] где <условие> – условие окончания цикла (см. [[#b023104] раздел B.2.3.1.04]), а <оператор> – любой допустимый оператор или блок операторов языка программирования, который выполняется определённое число раз («тело цикла»). В теле цикле должно находиться условие, прекращающее цикл. В противном случае цикл будет повторяться бесконечное число раз (будет «зацикливаться»). На блок-схеме цикл: «до … пока» имеет следующий вид (см. [[#b004f]рисунок B.004]): Рис. B.004. Оператор цикла: «до … пока» на блок-схеме. Цикл: «до … пока» соответствует модификатору «плюс» («+») в регулярном выражении (повторению один или более раз). Примечание: правилом «хорошего тона» в программировании является не использование цикла: «до … пока», – а замена его циклом: «пока» (с соответствующим изменением алгоритма). Хотя автор выступает против этого правила: созданные с помощью цикла: «до … пока» алгоритмические конструкции более просты в понимании. 5P9i0s8y19Z dt=Text B.2.3.1.06. Оператор цикла с индексом 4 Не смотря на то, что цикл с параметром – одна из первых синтаксических конструкций, не существует единого стандарта на его запись. В «наиболее общей форме» этот цикл записывается так: для <пер> от <начало> до <конец> шаг <шаг> делать <оператор> [B.006] где <пер> является переменной целого типа, принимающая значение переменной – индекса. Вне цикла её значение не определено (попросту, её нельзя использовать вне цикла). <начало> означает начальное значение индекса в цикле, <конец> – конечное значение индекса в цикле, а <шаг> – приращение цикла на каждой итерации (по-умолчанию используется приращение переменной цикла, равное «+1»). <начало>, <конец> и <шаг> могут принимать значения констант и переменных, однако переопределение этих параметров в цикле запрещено. Параметр <оператор> имеет то же значение, что и в описании цикла: «пока» (см. [[#b023104] раздел B.2.3.1.4]). Конструкция, приведённая в [[#b006e]B.006], задаёт переменной <пер> последовательность значений: <начало>, <начало>+<шаг>, <начало>+2·<шаг>, …, <конец> на каждой итерации. При этом «тело цикла» повторяется int((<конец>-<начало>)/<шаг>)+1 число раз (см. [[#b007e]формулу B.007]), Это число должно быть больше нуля. n := int( (<конец>-<начало>)/<шаг>)+1 [B.007] Примечание: если по формуле [[#b007e]B.007] получается отрицательное число, компилятор выдаст сообщение об ошибке. Если оно находится в пределах [0;1[, то число повторений не определено (всё зависит от реализации языка). Примечание: этот цикл соответствует модификатору: «{n}» в регулярных выражениях (n-точное число повторений). Оператор «цикл с параметром» может быть реализован циклом: «пока» – следующим образом (см. Пример 03): [Пример 03] /* Объявление переменных цикла: */ int i, imax=10; /* Присвоение начального значения переменной: */ i = 0; /* Объявление цикла и условия остановки цикла: i >= 10 */ while( i < imax ) { // Тело цикла; i++; // Инкремент переменной i (увеличение её значения на единицу) } /* Конец цикла */ В Примере 04 приведён код подпрограммы, задающий обратный порядок следования символов в строке языка Си без использования специальных функций: [Пример 04] /* Определение констант */ const short MAXSTRLENGTH = 255; /* Определение переменных */ char c; //Временная переменная str[MAXSTRLENGTH]; // искомая строка int i, nmax, imax; // Индекс массива и максимальное число итераций /* Определение числа повторений */ imax = strlen(str); nmax = imax/2; // Для неполных строк /* Цикл: */ for( i = 0; i < nmax; i++) { c = str[i]; str[i] = str[imax-i]; str[imax-i] = c; /* В цикле меняются первые и последние элементы */ } /* Конец цикла */ Примечание: определение типов, операций, функций и т.п. смотри в [[c0000.htm#c01]разделе C.1]. На блок-схеме цикл с параметром имеет следующий вид (см. [[#b005f]рисунок B.005]). Рис. B.005. Оператор цикла с параметром на блок-схеме На языке Quick Basic алгоритм из примера 04 будет записан следующим образом: [Пример 05] STRINGS$ = «Demo» REM Определяется длина строки переменной STRINGS$ IMAX% = LEN(STRINGS$) + 1 REM Определяется число циклов NMAX% = IMAX% / 2 REM Цикл: FOR I% = 1 TO NMAX% C$ = MID$(STRINGS$, I%, 1) MID$(STRINGS$, I%, 1) = MID$(STRINGS$, IMAX% - I%, 1) MID$(STRINGS$, IMAX% - I%, 1) = C$ NEXT I% REM Конец цикла PRINT STRINGS$ Цикл с параметром используются в следующих случаях: 1. При задании точного числа повторений тела цикла; 2. Для последовательного изменения «индекса массива» (при операциях с ними в теле цикла). Об индексах массива см. [[c0000.htm#c04]раздел C.4]. 5P9i0s8y19Z dt=Text B.2.3.1.07. Оператор множественного выбора; 4 Этот оператор используется для выбора одной из нескольких альтернатив выполнения алгоритма, задающихся значением целой переменной. Его синтаксис примерно следующий: Для <переменная> выбор <откр_скобка> при <значение1>: <тело 1>; break; при <значение2>: <тело 2>; break; … иначе: <последнее тело>; <закр_скобка> [B.008] где <откр_скобка><закр_скобка> – «составной оператор» (см. [[#b023113] раздел B.2.3.1.13]); <переменная> – переменная целого типа или типа, приводимого к целому (например, перечисления, одиночный символ и т.п.); <значение n> – одно из возможных значений этой переменной, < тело n> – последовательность действий для этого значения переменной; и <последнее тело> – последовательность операций в случае, если переменная не принимает ни одного из значений в блоке: при. Замечание: оператор множественного выбора может «моделироваться» повторением оператора ветвления, например: [Пример 06] если <значение 1> то <тело 1> иначе если <значение 2> то <тело 2> иначе если … иначе <последнее тело> где <тело> – оператор или последовательность операторов, заключённых в скобки. Замечание: оператор множественного выбора не часто встречается в символьных вычислениях. Вместо него чаще используется конструкция из примера 06. Рис. B.006. Множественный выбор на блок-схеме. 5P9i0s8y19Z dt=Text B.2.3.1.08. Оператор выхода из цикла: «break»; 4 Иногда в реализации алгоритма возникают условия, когда нужно выйти из цикла до завершения всех его итераций. Именно для этих целей служит оператор выхода из цикла. Его синтаксис следующий: break; [B.009] Этот оператор прекращает действие текущего цикла, и переводит выполнение программы в точку за прерванным циклом (в тело программы или в тело «родительского» цикла). Замечание: если по условию нужно выйти изо всех циклов («кессонный выход»), лучше использовать оператор безусловного перехода goto (см. [[#b023110]раздел B.2.3.1.10]). Примечание: в языке Perl изменён порядок выхода из цикла и синтаксис операций. Подробнее смотри [[Чаунин, Матросов]1]. 5P9i0s8y19Z dt=Text B.2.3.1.09. Оператор новой итерации: «continue»; 4 Часто в реализации алгоритма требуется прервать выполнение тела цикла и перейти к следующей итерации. Эта операция реализована в разных языках по-разному. На языке Си его синтаксис следующий: continue; [B.010] Конкретную реализацию этой конструкции смотри [[Чаунин, Матросов; Бочков;От Си к С++; Савитч]1-4]. Замечание: в языках, не имеющих конструкции: «continue», (например – в Quick Basic), данную операцию можно «моделировать» с помощью безусловного перехода «goto» на метку после последнего оператора тела цикла. Приведём пример, как можно использовать операции «break» и «continue» в бесконечном цикле на языке Си: [Пример 07] char c; // Определение переменной while(1) // бесконечный цикл { c=getc() // Чтение символа с клавиатуры // Выход, если введён символ («эникейщиком») if( c ) then break; else continue; } 5P9i0s8y19Z dt=Text B.2.3.1.10. Оператор безусловного перехода: «goto»; 4 Этот оператор является самым первым оператором для изменения последовательности выполняемых действий. В настоящее время его применение считается «дурным тоном», поэтому его использования необходимо избегать. Однако он не заменим в следующих случаях: 1. В языке отсутствуют другие конструкции алгоритма, более «структурированные» (например, в языке «Ассемблере»); 2. Необходимо выполнить «кессонный» выход из цикла при возникновении «исключительной ситуации». Примечание. «Кессонный выход» из вложенного цикла подразумевает прекращение итераций и выход не только из «текущего цикла» (в котором возникла исключительная ситуация), но также из «родительского цикла» и даже вообще из всех циклов, заставив ЭВМ продолжать выполнение программы вслед за вложенными циклами. «Кессонным» этот выход называется так потому, что он как бы соответствует «немедленному подъёму водолаза на поверхность», без предварительной адаптации его к пониженным давлениям. В результате этого подъёма у водолаза может наблюдаться «кессонная болезнь», которая заключается в закупоривании сосудов пузырьками сжатого воздуха. Поскольку этот подъём небезопасен, его используют редко. То же самое относится к «кессонному выходу из цикла»: при возможности его следует избегать. Синтаксис оператора безусловного перехода следующий: goto <метка> [B.011] где <метка> – смотри первое примечание [[#b023102] в разделе B.2.3.1.02]. На блок-схеме безусловный переход указывается линией со стрелкой. 5P9i0s8y19Z dt=Text B.2.3.1.11. Оператор вызова процедуры/функции; 4 Этот оператор является «основным» оператором процедурного программирования. Его синтаксис: <имя подпрограммы>(<фактические параметры>) [B.012] – для вызова подпрограмм и <переменная> = <имя функции>(<фактические параметры>) [B.013] – для вызова функции. Здесь <имя подпрограммы> (<имя функции>) – имя, под которым процедура (подпрограмма или функция) встречается в программе, <переменная> – возвращаемое функцией значение переменной, а <фактические параметры> – список передаваемых программе параметров-констант и переменных (их значений). При вызове процедур тип и число переменных – фактических параметров должно совпадать с числом и типом формальных параметров, заданных при объявлении процедуры. Объявление процедуры следующее: [<тип>]<имя процедуры>(<формальные параметры>) <тело процедуры> [B.014] где <тип> – тип возвращаемого функцией результата (отсутствует при вызове подпрограммы), а <формальные параметры> – передаваемые через стек значения переменных. Синтаксис формальных параметров: <формальные параметры> ::= <тип> <имя>{,<тип> <имя>}* [B.015] где <тип> – тип переменной – формального параметра (должен совпадать с типом фактического параметра), <имя> – имя переменной – формального параметра (может отличаться от имени/значения фактических параметров), остальные знаки – специальные символы РБНФ (не пишутся в образце). Замечание: в языке Си есть разница между объявлением и описанием процедуры. При объявлении даётся структура, указанная в формуле [[#b014e]B.014], но без тела функции. Описание функции представляет собой структуру [[#b014e]B.014], но содержащие операторы алгоритма в теле функции. Объявление функции должно предшествовать не только его описанию, но и использованию. В остальных языках, как правило, объявления и описания совпадают. Замечание. Фактические параметры могут передаваться по значению и по ссылке. При передаче по значению значение переменной – фактического параметра заносится в стек, и формальный параметр принимает значение уже из стека. При выходе из процедуры значение переменной в стеке уничтожается. Передача данных по ссылке – это, фактически, передача «разыменованного указателя на переменную». При выходе из процедуры значение переменной – формального параметра сохраняется и передаётся фактическому параметру. Подробнее о передаче параметров смотри в [[c0000.hjt#c04] разделе C.4]. Хотя передача параметров по ссылке выглядит предпочтительнее, её использование ведёт к усложнению машинного кода и снижению безопасности системы. Примечание: «Джентльменское соглашение» о последовательности записи формальных параметров следующие: 1. Сначала идут передаваемые по значению переменные, потом – передаваемые по значению указатели, и в конце – передаваемые по ссылке переменные и указатели; 2. Вначале идут переменные с исходными значениями, затем – переменные, изменяющие значения исходных переменных, и лишь затем – счётчики, дескрипторы (файлов, областей памяти) и т.п. Вызов процедуры на блок-схеме изображается так, как показано на [[]рисунке B.007] Рис. B.007. Обозначение вызова процедуры на блок-схеме. Замечание: Тип возвращаемого функцией значения может переопределяться (см. [[c0000.hjt#c04]раздел C.4]). 5P9i0s8y19Z dt=Text B.2.3.1.12. Операторы ввода-вывода. 4 В большинстве языков ввод-вывод осуществляется с помощью функций или операторов, очень похожих на функцию. Здесь автор отмечает, что ввод-вывод данных может быть прямым (сразу на консоль или в файл), буферизированным, или «потоковым» (через определённый буфер, связанный с файлом либо устройством и расположенный в оперативной памяти компьютера) и, наконец, «графическим», через заранее определённый GUI API («программный интерфейс приложения для графического пользовательского интерфейса»). Для каждого из этих типов вывода существуют свои API (функции и библиотеки). Подробнее о функциях ввода-вывода смотри [[Чаунин, Матросов; Бочков; от С к С++; Савитч; братья Фроловы 11-14, 16, 17]1-10]. Вид операторов ввода и вывода данных на блок-схеме представлен на [[#b008e]рисунке B.008]. Рис. B.008. Обозначение операторов ввода-вывода на блок-схеме. 5P9i0s8y19Z dt=Text B.2.3.1.13. Составной оператор 4 Блок операторов представляет собой допустимую последовательность операторов, заключённых в скобки (например, в Си/C++ это фигурные скобки: «{…}») или между лексемами: «Begin … End» (как в Паскале и Алголе). На блок-схеме такие операторы никак не выделяются, а представляют собой просто последовательности операторов. Составной оператор применяется в следующих случаях: 1. В операциях условия, цикла, ветвления, множественного выбора, когда вместо одного оператора необходимо выполнить несколько операций (блок); 2. Необходимо определить «локальные переменные», определяемые лишь внутри блока; 3. Нужно написать тело функции или процедуры. Новые переменные определяются только в начале блока, и их действие заканчивается после выхода из блока. 5P9i0s8y19Z dt=Text B.2.3.1.14. Оператор выхода из процедуры 4 Этот оператор имеет следующий синтаксис: return [<значение>] [B.016] где <значение> – выдаваемое после окончания функции её значение (только для функций). Оператор «return» может быть расположен в любом месте программы. На блок-схеме он обозначен как: «Конец [<значение>]» (см. [[]рисунок B.009]). Рис. B.009. Изображение оператора: «выход из функции» на блок-схеме. 5P9i0s8y19Z dt=Text B.2.3.1.15. Резюме 4 Итак, мы познакомились с основными алгоритмическими конструкциями, присутствующими в языках программирования процедурной парадигмы. Более подробно о синтаксисе этих конструкций в конкретных языках программирования смотри [[…, учебник Си, Страуструп]1-4, 11, 12]. Там же смотрите примеры программ. А мы движемся дальше, к общему представлению программ на процедурных языках и описанию «метаязыков». 5P9i0s8y19Z dt=Text B.2.3.2. Представление программ и реализация вычислений 3 Прежде, чем передать код программы на обработку компилятору, программист должен соответствующим образом оформить код программы. В этом разделе даются общие правила оформления кода программы на разных языках программирования. Содержание B.2.3.2.1. Quick Basic B.2.3.2.2. C/C++ B.2.3.2.3. Perl 5P9i0s8y19Z dt=Text B.2.3.2.1. Quick Basic 4 В языке Quick Basic есть следующие правила оформления программ: 1. Программа на языке Quick Basic находится в файле с расширением «*.bas»; 2. Основная программа начинается с первого её оператора; 3. Желательно «нумеровать» все строки файла метками – натуральными числами. Нумерацию строк желательно начинать с числа 10 и задать шаг нумерации тоже 10; 4. Заканчиваться вычисления в программе должны оператором: «STOP»; 5. Завершаться описание всех алгоритмических конструкций в программе необходимо оператором: «END»; 6. На каждый оператор выделяется одна строка; 7. Комментарии к строкам алгоритма должны начинаться отдельной строкой, после ключевого слова: «REM», которые указываются сразу после метки (или вместо метки); 8. Подпрограммы на языке: «Quick Basic», оформляются отдельно, внутри файла; 9. Оформление подпрограмм начинается с ключевого слова: «SUB», за которым идёт имя подпрограммы; 10. Выполнение программы на языке «Quick Basic», открытой в его оболочке («qbasic.exe»), начинается после выбора меню: «Run» -> «Continue», или нажатием клавиши: «F5». 5P9i0s8y19Z dt=Text B.2.3.2.2. C/C++ 4 На языке Си и, частично, С++, существуют следующие правила оформления программ: 1. Программа на языке Си находится в файле с расширением: «*.c», а на языке C++ – с расширением «*.cpp»; 2. Файлы с «заголовочной частью» к основной программе находятся в файлах с расширением «*.h» и «*.hpp»; 3. Основная программа (которая затем будет запускаться по имени файла, например, «myprog.exe»), должна иметь имя и расширение «myprog.c». То есть, в общем случае, имя основного файла проекта – «<имя проекта>.c» (для программ на Си) или «<имя проекта>.cpp» (для программ на С++), где <имя проекта> будет соответствовать имени Вашей программы после компиляции; 4. Файл с программой начинается с включения (в заголовочной части) файлов с расширением «*.h» и «*.hpp», с помощью прагма оператора: «include». При этом имена заголовочных файлов, находящихся в одном каталоге с файлами-описаниями алгоритма, заключаются в кавычки, а имена файлов, пути к которым прописаны в переменной операционной системы: «INCLUDE», заключаются между знаками: «<…>», например: * #include * #include «myprog.h» 5. Далее в файле с основной программой задаётся основная функция алгоритма: «main», – управляющая вызовом всех остальных функций и подпрограмм. Примечание: при программировании на языке Си с использованием библиотеки Windows API основной функцией алгоритма становится функция: WinMain. Подробнее об этой функции смотри [[Братья Фроловы, 11]8]. 6. Функция «main» из пункта 5 может возвращать целое значение (тип «int»). Это значение впоследствии используется при запуске функции внутри командного файла оболочки операционной системы, для его реакции на неудачный результат работы этой функции. Принято, что в случае успешного результата своей работы функция «main» возвращает нулевой результат. 7. Функция «main» может принимать в качестве аргументов следующие формальные параметры: main( int argc, char *argv[], char *env[]), где * argc – число аргументов у функции. Эта переменная принимает значение 1 в случае отсутствия опций и параметров, 2 в случае присутствия одного параметра и т.п.; * *argv[] – массив символьных переменных – указателей на опции и аргументы программы, запускаемой из командной строки. При этом элементу «0» соответствует полное путевое имя файла с запускаемой программой. Элементам с номерами: «1» – «argc-1» соответствуют аргументы программы, набранные в командной строке, с соответствующими номерами (по-порядку); * *env[] – массив символьных переменных – указателей на системное окружение операционной системы, в которой запускается скомпилированная программа. 8. Комментарии на языках: «Си» и «С++» обозначаются следующим образом: * Игнорирование конца строки вслед за комментарием: «//»; * Игнорирование текста внутри «скобок» – спецсимволов: «/* … */». 9. Тексты-описания функций и процедур на языке: «Си» располагаются либо после описания функции «main», либо в отдельных файлах (с расширениями: «*.C» и «*.CPP» соответственно для функций «Си» и «С++»). 10. В «заголовочных файлах» (с расширением: «*.h» и «*.hpp») задаются, во-первых, директивы компилятору на обработку файлов, во-вторых, макрофункции и макроподстановки, в-третьих, определения констант и глобальных переменных в программе, и, в-четвёртых, объявления функций. Формат описания заголовочной части смотри [[bapp001.hjt]в приложении I] и в книге [[Бочков]2]. 11. Перед запуском программы, написанной на языках Си и С++, необходимо сначала «скомпилировать» модули и «построить», скомпоновать («Build») проект в IDE (с именем: «<имя проекта>»), а затем запустить её, либо набрав в командной строке: <имя проекта>.exe {<опции>}*, либо щёлкнув на иконке этой программы (в Windows). Подробнее о других аспектах программирования на C/C++ смотри: [[Бочков; Козелл; Хикс; Страуструп]2,3,11,12] 5P9i0s8y19Z dt=Text B.2.3.2.3. Perl 4 В языке Perl существуют следующие правила оформления программных модулей: 1. Программа на языке Perl находится в файле с расширением: «*.pl». 2. Начинаться программа должна с директивы компилятору, имеющей следующий синтаксис: #!<путь к интерпретатору Perl> {<опции>} [B.017] Например: #!/usr/bin/perl [B.018] – для интерпретатора perl в UNIX, и #!perl -w [B.019] – для интерпретатора perl в Windows. Опция -w означает: «включить отладку сценариев». 3. Основная программа начинается с первого её оператора, а заканчивается – концом файла или оператором описания функций «sub». 4. Комментарии на языке Perl обозначаются как: «#». Все символы, набранные после этого знака, игнорируются интерпретатором. 5. Описание подпрограмм и функций начинается со слова «sub», после которого идёт блок с операторами. Подпрограмма завершается при выходе из этого блока: sub <имя функции> {<тело функции>} [B.020] 6. Вызов подпрограммы осуществляется следующим образом: [<переменная>=] &<имя функции> [(<аргументы>)] [B.021] где <переменная> – присвоенное значение-результат работы функции; <имя функции> – имя, зарезервированное в [[#b020e]B.020]. Аргументами могут быть строковый литерал либо ссылка: <аргументы> ::= {<литерал>|\<имя фактического параметра>}+ 7. Объявление процедур осуществляется либо сразу же после основной функции, либо в отдельном файле-библиотеке (расширение – «*.pl») или модуле (расширение – «*.pm»). Подробнее о реализации библиотек и модулей см. [[Чаунин, Матросов]1]. Стандартные же шаблоны оформления модулей и библиотек см. в [[bapp002.hjt]приложении II]. 8. Доступ к аргументам (как основной программы, так и функций) осуществляется через переменную: «@_». 9. Программа («макрос», «скрипт») на языке Perl может выдавать целое значение при выходе из неё (с помощью функции: «exit( <целое число> )»). По «джентльменскому соглашению» программа возвращает «0» в случае своего нормального завершения. 10. Для запуска макросов в командной строке необходимо набрать: perl <полное имя макроса> {<аргументы>} [B.022], где <полное имя макроса> – имя программы (с расширением: «*.pl») вместе с его путевым именем; <аргументы> – передаваемые программе аргументы-значения. Примечание. Если в качестве аргументов используются имена файлов, необходимо указывать их полные путевые имена. Примечание. В [[bapp003.hjt]приложении III] представлены тексты командных файлов Windows, с помощью которых можно «облегчить» выполнение сценариев на Perl, указывая не абсолютные путевые, а «относительные» имена файлов. Инструкция к ним прилагается. 5P9i0s8y19Z dt=Text B.2.3.3. Особенности метаязыка 3 для процедурной парадигмы Содержание: B.2.3.3.1. Блок-схемы B.2.3.3.2. «Школьный» язык программирования. B.2.3.3.3. Требования для «структурирования» алгоритма B.2.3.3.4. Резюме метаязыков === *** === *** === Для визуализации алгоритмов, созданных с помощью процедурной парадигмы, используют: * блок-схемы; * «школьный» язык программирования, созданный Кушниренко. Рассмотрим эти способы поподробнее: 5P9i0s8y19Z dt=Text B.2.3.3.1. Блок-схемы 4 Это «старый» способ визуализации алгоритмов. Его преимущества очевидны при «неструктурированном» программировании (например, на языке «Ассемблер»). Вот его составные части: 1. Начало программы отмечается символом, указанным на рисунке [[#b009f]B.009 a], а окончание отмечается изображением, указанным на рисунке [[#b009f]B.009 b]; 2. Переход к следующему по-порядку оператору осуществляется при помощи линий со стрелками; 3. Разрыв линий указывается с помощью фигуры на рисунке [[#b010f]B.010]; Рис. B.010. Фигуры, указывающие на «разрывы» линий, отмечающих последовательность операторов. 4. Основные конструкции алгоритмов на блок-схемах представлены фигурами, изображённые на рисунках: B.001 – B.007; 5. Операторы ввода-вывода данных представлены фигурами, изображёнными на рисунке [[#b008f]B.008]. 6. Все остальные операторы представлены прямоугольником, указанным на [[#b011f]рисунке B.011]. Рис. B.011. Обозначение операторов на блок-схеме. Ниже приведена блок-схема алгоритма нахождения наибольшего общего делителя по алгоритму Евклида. [Пример 08] Рис. B.012. Блок-схема алгоритма Евклида. Примечание. Блок-схему алгоритма всегда следует прикладывать как приложение к тексту программы на «неструктурированных» языках программирования, таких как «Ассемблер» и «BasicA» (ранняя реализация Бейсика от Microsoft). 5P9i0s8y19Z dt=Text B.2.3.3.2. «Школьный» язык программирования. 4 Этот «язык записи алгоритмов» был предложен в 1986 году Кушниренко [[Кушниренко]13] специально для обучения программированию в средних школах. Этот язык оказался настолько «мощным» для описания алгоритмов процедурной парадигмы, что он используется (с небольшими изменениями) до сих пор. В [[bapp004.hjt]приложении IV] представлены основные конструкции этого языка. В примере 09 представлена запись алгоритма из примера 08 на «школьном» языке. [Пример 09] В [[bapp005.hjt]приложении V] находятся требования к «описательной части» школьного языка программирования, не вошедшие в [[bapp004.hjt]приложение IV] 5P9i0s8y19Z dt=Text B.2.3.3.3. Требования для «структурирования» алгоритма 4 Вот ещё одно «неписанное правило» структурированного программирования: «Каждый оператор внутри блока операторов должен выделяться дополнительным фиксированным отступом от левой границы текста на одну позицию табуляции (символ «\t»). При закрытии блока операторов позиция отступа «уменьшается назад» на одну позицию табуляции.» Для иллюстрации этого правила смотри Пример 09 на «школьном» языке программирования. 5P9i0s8y19Z dt=Text B.2.3.3.4. Резюме метаязыков 4 В предыдущих разделах мы ознакомились с основными «метаязыками» программирования, используемых при записи алгоритмов, вместе с их возможными областями применения. Вы получили представления о блок-схемах, «школьном языке программирования», познакомились с основными правилами «структурированной парадигмы». Более подробно о процедурной парадигме программирования смотри, например, [[128 Советов]14]. В следующем разделе будет рассказано о конструкциях логической парадигмы программирования. В [[bapp006.hjt]приложении VI] даётся описание языка MS-DOS command shell и bash shell Linux, а также других языков оболочек операционных систем. 5P9i0s8y19Z dt=Text B.3. Логическая парадигма 1 5P9i0s8y19Z dt=Text B.4. Объектно-ориентированная парадигма 1 5P9i0s8y19Z dt=Text B.5. Резюме 1 5P9i0s8y19Z