Министерство образования РФ

Шадринский государственный педагогический институт

 

 

 

 

 

 

Слинкин Д.А.

 

 

 

Программирование.

 

ЧАСТЬ 1

 

язык программирования турбо-паскаль

 

 

 

 

Учебное пособие

для студентов Вузов

 

 

 

 

 

 

 

Шадринск 2000


 

УДК   681.3.066.3

C47

 

 

Слинкин Д.А.

Программирование. Часть 1. Язык программирования Турбо-Паскаль: Учебное пособие для студентов вузов (издание 2-е, исправленное). Шадринск: Шадринский пединститут, 2000. - 140 с.

 

 

 

 

Рекомендовано к печати кафедрой теории и методики информатики Шадринского пединститута.

Первая часть учебного пособия подготовлена на основе четырехлетнего опыта преподавания дисциплины "Языки и методы программирования" на младших курсах физико-математического факультета ШГПИ, и посвящена одному из наиболее распространенных языков высокого уровня. В пособии приводится полное описание синтаксиса и семантики Турбо-Паскаля 7.0 для программирования в реальном режиме микропроцессора, более 50 подробно разобранных задач и более 100 задач для самостоятельно решения.

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

С вопросами и предложениями обращаться к автору по E-mail: sda@shadrinsk.zaural.ru

 

 

 

 

 

 

 

 

ISBN 5-87818-160-6

 

 

 

© Шадринский пединститут, 2000

© Слинкин Д.А., 2000


 

Оглавление

Оглавление.......................................................................................................... 3

Введение............................................................................................................... 6

Глава 1....................... Язык программирования Турбо-Паскаль. 7

1.1            Ключевые понятия языков  программирования....................................................................... 7

Алгоритм............................................................................................................................................................ 7

Свойства алгоритма..................................................................................................................................... 7

Программа......................................................................................................................................................... 9

Данные............................................................................................................................................................ 9

Оператор........................................................................................................................................................ 9

Подпрограмма............................................................................................................................................ 10

Модуль.......................................................................................................................................................... 10

Язык программирования.............................................................................................................................. 11

Текстовый редактор....................................................................................................................................... 11

Транслятор....................................................................................................................................................... 11

Отладчик............................................................................................................................................................ 12

Интегрированная среда................................................................................................................................. 12

1.2            Синтаксис и семантика языков  программирования............................................................. 13

1.3            Основные понятия Турбо-Паскаля, требуемые для создания программ.......................... 15

1.3.1              Подготовка и запуск программ............................................................................................. 15

1.3.1.1           Интегрированная среда Турбо-Паскаля 7.0................................................................... 15

1.3.1.2           Свойства интегрированной среды................................................................................... 16

1.3.1.3           Имена и расширения файлов для  работы в интегрированной среде..................... 17

1.3.1.4           Некоторые комбинации клавиш,  используемых при работе с ИС.......................... 17

1.3.2              Лексемы Турбо-Паскаля........................................................................................................ 18

1.3.2.1           Специальные символы........................................................................................................ 19

1.3.2.2           Зарезервированные слова.................................................................................................. 19

1.3.2.3           Директивы.............................................................................................................................. 19

1.3.2.4           Идентификаторы.................................................................................................................. 20

1.3.2.5           Метки...................................................................................................................................... 20

1.3.2.6           Числа....................................................................................................................................... 20

1.3.2.7           Строковые константы.......................................................................................................... 20

Задачи для раздела "Лексемы Турбо-Паскаля".................................................................................. 21

1.3.3              Структура программы............................................................................................................ 22

1.3.3.1           Низкоуровневая структура программы......................................................................... 22

1.3.3.2           Важнейшие операторы Турбо-Паскаля.......................................................................... 23

Оператор присваивания....................................................................................................................... 23

Составной оператор.............................................................................................................................. 23

Оператор вызова процедуры.............................................................................................................. 24

1.3.3.3           Высокоуровневая структура программы...................................................................... 25

Заголовок программы.......................................................................................................................... 25

Тело программы.................................................................................................................................... 26

Раздел объявлений............................................................................................................................. 26

Раздел объявления переменных................................................................................................ 26

Раздел объявления констант....................................................................................................... 27

Раздел объявления процедур и функций................................................................................ 28

1.3.3.4           Примеры небольших программ....................................................................................... 29

Задачи для раздела "Структура программы"...................................................................................... 31

1.4            Выражения........................................................................................................................................ 33

1.4.1              Структура выражений............................................................................................................. 33

1.4.2              Приоритет операций................................................................................................................ 33

1.4.3              Типы операций......................................................................................................................... 34

1.4.3.1           Арифметические операции............................................................................................... 34

1.4.3.2           Логические операции......................................................................................................... 34

1.4.3.3           Строковые операции........................................................................................................... 35

1.4.3.4           Операции над множеством................................................................................................ 35

1.4.3.5           Операции отношения.......................................................................................................... 35

1.4.3.6           Операция @........................................................................................................................... 36

Задачи для раздела "Выражения"............................................................................................................... 36

1.5            Стандартные функции и процедуры........................................................................................... 38

1.5.1              Процедуры и функции модуля System............................................................................... 38

1.5.1.1           Арифметические функции................................................................................................ 38

1.5.1.2           Процедуры выхода............................................................................................................... 39

1.5.1.3           Различные процедуры и функции................................................................................... 39

1.5.1.4           Обзор дополнительных  процедур и функций.............................................................. 41

1.5.2              Процедуры и функции модуля Crt....................................................................................... 41

Задачи для раздела "Процедуры и функции модуля Crt"................................................................. 43

1.6            Операторы.......................................................................................................................................... 45

1.6.1              Простые операторы................................................................................................................. 45

1.6.2              Структурные операторы......................................................................................................... 46

1.6.2.1           Условные операторы.......................................................................................................... 46

Оператор IF............................................................................................................................................. 46

Оператор CASE...................................................................................................................................... 48

Задачи для раздела "Условные операторы"........................................................................................ 49

1.6.2.2           Операторы цикла.................................................................................................................. 49

Цикл For.................................................................................................................................................... 49

Цикл While............................................................................................................................................... 53

Цикл Repeat............................................................................................................................................. 54

Вложенные циклы.................................................................................................................................. 55

Выбор вида цикла при решении задач.............................................................................................. 58

Процедуры break и continue................................................................................................................ 58

Задачи для раздела "Операторы цикла"................................................................................................ 60

1.7            Типы.................................................................................................................................................... 61

1.7.1              Простой тип............................................................................................................................... 61

1.7.1.1           Порядковый тип.................................................................................................................... 61

Встроенный порядковый тип.............................................................................................................. 62

Целочисленный тип.......................................................................................................................... 62

Логический тип.................................................................................................................................. 63

Символьный тип................................................................................................................................ 64

Перечислимый тип............................................................................................................................ 65

Тип поддиапазона.............................................................................................................................. 65

Свойства порядковых типов............................................................................................................ 66

1.7.1.2           Вещественный тип............................................................................................................... 67

Задачи для раздела "Простой тип"......................................................................................................... 71

1.7.2              Структурные типы.................................................................................................................... 71

1.7.2.1           Массивы................................................................................................................................. 71

Заполнение и обработка массивов.................................................................................................... 72

Определение содержимого массива в разделе типированных констант.................................. 72

Задачи для раздела "Массивы"............................................................................................................... 72

1.7.2.2           Строковый тип...................................................................................................................... 72

Стандартный строковый тип............................................................................................................... 72

ASCIIZ строковый тип.......................................................................................................................... 72

Задачи для раздела "Строковый тип".................................................................................................... 72

1.7.2.3           Записи..................................................................................................................................... 72

Определение содержимого записи в разделе типированных констант..................................... 72

Задачи для раздела "Записи"................................................................................................................... 72

1.7.2.4           Множества............................................................................................................................. 72

Задачи для раздела "Множества"........................................................................................................... 72

1.7.2.5           Файловые типы..................................................................................................................... 72

Принципы работы с файлами............................................................................................................. 72

Файловая позиция.................................................................................................................................. 72

Дополнительные процедуры и функции для работы с файлами............................................... 72

Задачи для раздела "Файловые типы"................................................................................................... 72

1.7.2.6           Объектные типы (обзор)..................................................................................................... 72

1.7.3              Указатели.................................................................................................................................... 72

Задачи для раздела "Указатели".............................................................................................................. 72

1.7.4              Процедурные типы (обзор).................................................................................................... 72

1.7.5              Приведение типов..................................................................................................................... 72

1.7.6              Совместимость типов.............................................................................................................. 72

1.8            Процедуры и функции..................................................................................................................... 72

1.8.1              Объявление процедур и функций........................................................................................ 72

1.8.2              Формальные и фактические  параметры............................................................................ 72

1.8.3              Открытые массивы и строки.................................................................................................. 72

1.8.4              Процедурные типы.................................................................................................................. 72

1.8.5              Процедурные директивы........................................................................................................ 72

1.8.5.1           Директивы Near и Far.......................................................................................................... 72

1.8.5.2           Опережающие объявления................................................................................................ 72

1.8.5.3           Interrupt объявления............................................................................................................ 72

1.8.5.4           Внешние объявления (external)......................................................................................... 72

1.8.5.5           Блок asm.................................................................................................................................. 72

1.8.5.6           Объявление inline................................................................................................................. 72

Задачи для раздела "Процедуры и функции".......................................................................................... 72

1.9            Модули................................................................................................................................................ 72

1.9.1              Интерфейсная секция.............................................................................................................. 72

1.9.2              Секция реализации................................................................................................................... 72

1.9.3              Секция инициализации............................................................................................................ 72

1.9.4              Косвенные ссылки на модули............................................................................................... 72

1.9.5              Циклические ссылки модулей............................................................................................... 72

1.9.6              Пример разработки модуля................................................................................................... 72

1.9.7              Стандартные модули (обзор)................................................................................................. 72

Задачи для раздела "Модули"...................................................................................................................... 72

Список литературы..................................................................................... 72

 


 

Введение

Язык программирования Паскаль был задуман и разработан в 1968-1971гг. Никлаусом Виртом в Швейцарии, в Цюрихском Институте информатики как учебный язык для студентов с ясным синтаксисом и простой семантикой. В дальнейшем, с развитием новейших концепций программирования, появились его новые и более мощные реализации. Прежде всего, это относится к семейству Турбо-Паскаль (в дальнейшем - ТП) фирмы Borland International inc. (на сегодняшний день - Inprise). В настоящее время для разработки самого широкого спектра программных средств, начиная от СУБД и кончая антивирусными пакетами, используются версии 6.0 и 7.0 Турбо-Паскаля, Borland-Pascal для защищенного режима и среды Windows, а также Delphi 1.0, 2.0, 3.0 и 4.0 как система разработки приложений под Windows 3.1-3.11, Windows 95 и Windows NT.

Мы не случайно выбрали Турбо-Паскаль из всего спектра средств разработки, основанных на Паскале. Цель данного пособия – научить основам программирования, а не, скажем, визуальной разработке программ. К сожалению, начала работы в такой среде, как Delphi, имеют множество отвлекающих концепций и понятий, реализация которых сознательно предельно упрощена для повышения скорости создания программ. Эффективное их использование возможно только для профессионала, четко и ясно понимающего и разграничивающего возможности языка и среды разработки.

Знание программирования подразумевает понимание как высоко-, так и низкоуровневых механизмов обработки данных. Среда быстрой разработки приложений (RAD - Rapid Application Development), какой является Delphi, включающая в себя идеи визуального программирования, скрывает все низкоуровневые механизмы, выводя на первый план скорость разработки стандартных приложений. В случае усложнения исходной задачи или возникновения нестандартной ситуации программист, начавший изучение программирования с Delphi, не найдет решения проблемы без высококвалифицированной помощи извне или глубокого изучения принципов, положенных в основу языка и операционной системы. Первый подход применим далеко не всегда, а второй подразумевает значительное удлинение сроков создания программного продукта.

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

 

 

Глава 1.    Язык программирования Турбо-Паскаль.

1.1  Ключевые понятия языков
программирования

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

Алгоритм.

Алгоритмом называется последовательность дискретных шагов (действий), предназначенных для исполнения и приводящих к некоторому конечному результату. Алгоритм обладает определенным набором свойств

Свойства алгоритма

Любой алгоритм должен обладать следующими основными свойствами:

1) Конечность. Выполнение программы должно быть завершено за некоторый конечный промежуток времени.

Существует целый набор операторов, неправильное использование которых может вызвать бесконечный цикл, "зависание" программы. Это касается операторов цикла, условного и безусловного перехода и даже оператора присваивания (например - присваивание произвольной области памяти некоторого значения. Если эта область находится не в области данных программы, то результаты такого действия совершенно непредсказуемы).

2) Определенность. Все операторы, а также любые другие структуры, из которых состоит программа, должны быть известны исполнителю.

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

3) Возможность ввода данных и вывода результатов.

В ТП ввод-вывод организуется с помощью процедур ввода-вывода. Рассмотрим четыре из них.

Процедура Write позволяет выводить на экран значения выражений, следующие за ней в скобках через запятую. Процедура Writeln делает то же самое, но после вывода информации переводит строку:

begin

 writeln(1231+4*(231-21),'  ',444+23);

end.

В результате работы этой программы на экране появятся 2 числа:

2071  467

Все, что заключено в апострофы, выводится на экран без изменений. В данном случае в апострофы заключен пробел, используемый для разделения друг от друга первого и второго результатов.

Дополнительно, процедуры Write и Writeln позволяют ставить после каждого числового выражения одно или два натуральных числа через двоеточие. Первое означает общее количество символов в числе (если цифр не хватает, то при выводе значение выражения дополняется слева требуемым количеством пробелов), а второе - количество выводимых знаков дробной части.

Пример:

begin

 writeln (11.32/3:8:3, 4+5+6:5);

 writeln (11.32/0.3:8:3, 44+55+66:5);

end.

В результате работы программы на экране появится:

    3.773    15

  37.733  165

Таким методом легко производить форматирование и выравнивание числовых выражений.

Процедура Read позволяет вводить с клавиатуры значения и присваивать их переменным, имена которых идут после Read в скобках через запятую. Процедура Readln делает то же самое, но после ввода информации происходит переход на другую строку:

var A,B: integer;

begin

 Writeln('Введите два целых числа');

 Readln (A,B);

 Writeln('Произведение чисел ', A, ' и ', B, ' равно ', A*B);

end.

После запуска данная программа остановится в ожидании ввода двух чисел (разделенных пробелом). После ввода и нажатия на клавишу "Enter" произойдет вывод на экран произведения этих чисел с соответствующими комментариями:

Введите два целых числа

6 2

Произведение чисел 6 и 2 равно 12

Процедура Readln без параметров ждет нажатия клавиши "Enter" (что удобно использовать как задержку в конце работы программы), а процедура Writeln - переводит курсор на другую строку. Процедуры Read и Write без параметров ничего не делают.

Программа.

В языках программирования синонимом алгоритма служит программа.

Создание готовой к исполнению программы проходит через несколько этапов:

1)     Подготовка текста программы с помощью текстового редактора

2)     Трансляция (компиляция и компоновка).

3)     При обнаружении ошибок запускается процесс отладки.

Данные.

Данные программы - это информация, хранимая в памяти ЭВМ и обрабатываемая программой. В ТП данные сохраняются в переменных, а их обработка ведется с помощью операторов (см. ниже). Переменной называется именованная область памяти, содержимое которой программист может произвольно изменять в процессе работы программы. Каждая переменная имеет свой собственный тип, от которого зависит набор операций над переменной и занимаемый ею объем памяти компьютера. В ТП переменные могут быть целочисленными, вещественными, логическими, строковыми и т.д.

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

Любые переменные в ТП должны быть определены до своего использования. Это делается с помощью зарезервированного слова VAR:

Var X,Y:integer; Z,B:real;

В данном примере зарезервированное слово Integer определяет, что переменные X и Y будут иметь целочисленный тип, а слово Real - что переменные Z и B будут вещественными.

Оператор.

Оператор – это команда или набор команд исполнителю (в данном случае исполнителем является компьютер), некоторое действие над данными программы. Операторы выполняются в программе один за другим до тех пор, пока выполнение не дойдет до самого последнего из них или до оператора, который принудительно заставит программу завершить работу.

Чаще всего употребляемый оператор в ТП - это оператор присваивания, который используется для того, чтобы помещать в переменные различные значения:

var A:real;

begin

 a:=7;

 a:=a+2;

end.

В этом примере между зарезервированными словами begin и end (последовательность которых также является оператором и означает (в данном случае) начало и конец программы) находятся 2 оператора присваивания, первый из которых присваивает переменной A значение 7, а второй - увеличивает A на 2.

Подпрограмма.

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

Функция - это процедура, возвращающая результат, что позволяет использовать ее в выражениях.

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

ТП имеет множество встроенных процедур и функций. Типичным примером могут служить арифметические и алгебраические функции: sin, cos, arctan, abs, sqrt и т.д. Каждая из них имеет только один числовой параметр, передаваемый ей в скобках и возвращает некоторое значение. Например, для получения тангенса некоторой переменной следует составить такую программу:

var pi,z:real;

begin

 pi:=3.1415926;

 z:=sin(pi)/cos(pi);

end.

Если процедура или функция имеет несколько параметров, то они передаются ей в скобках через запятую.

Модуль.

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

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

Язык программирования.

Язык программирования - это четкий, не допускающий различных толкований, искусственно созданный язык для точного описания алгоритмов, используемых в вычислительных машинах. Существуют языки программирования низкого и высокого уровней. Под языком низкого уровня понимают язык, синтаксис и семантика которого зависят от типа вычислительной машины, на которой он используется. Таким, например, является язык ассемблер, который имеет различные мнемонику и команды для различных типов машин. Синтаксис и семантика языка высокого уровня едины для любого типа ЭВМ. Результат выполнения программ на Паскале или Си будет одинаков и на IBM PC, и на Ямахе MSX-2. Однако в этом случае для каждого типа ЭВМ должна существовать программа-транслятор (см. ниже).

Текстовый редактор.

На первом этапе создания программы используется текстовый редактор - программа подготовки разнообразных текстов.

Транслятор.

Транслятор - это переводчик с языка высокого уровня на язык ассемблера. Трансляторы бывают двух видов - интерпретаторы и компиляторы. Программа-интерпретатор выполняет команды языка строка за строкой. Программа-компилятор переводит всю программу на язык ассемблера и только затем позволяет ее выполнить. Компоновщик - это одно средств разработки программ, входящее в состав транслятора. Под компоновкой понимается процесс объединения различных автономных программных блоков (модулей) в единое целое - готовую к исполнению программу.

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

Отладчик.

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

Интегрированная среда.

Интегрированная среда (IDE или ИС)- это система разработки программ, объединяющая текстовый редактор, компилятор (интерпретатор), компоновщик и отладчик в единое целое.

IDE Турбо-Паскаля - это мощная и удобная система, позволяющая быстро создавать программы небольших и средних размеров. При больших объемах программного текста удобнее использовать автономные средства разработки программ, также входящие в состав ТП.


 

1.2  Синтаксис и семантика языков
программирования

Описание любого языка (и не только языка программирования) всегда начинается с определения его синтаксиса и семантики.

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

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

Семантика - смысл, вкладываемый в каждую конструкцию или последовательность конструкций языка.

И синтаксис и семантику любого языка можно описать неформально, приводя варианты всех видов конструкций и примеры их использования в различных ситуациях. Однако для описания синтаксиса существуют формальные методы, применение которых значительно сокращает объем описаний. Формальные методы для описания семантики также существует, однако они достаточно сложны, громоздки и используются, в основном, для доказательства свойств программ.

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

Правила продукций БНФ:

1) Имя определяемой синтаксической конструкции размещается слева, а ее определение - справа. Имя конструкции и ее определение разделены символом '::='. Друг от друга синтаксические конструкции и лексемы отделяются пробелами.

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

3) Для обозначения возможного выбора из нескольких вариантов используется знак ¦.

4) Для обозначения повторов используются знаки a{}b.

Если a отсутствует, то считается, что a=0

Если b отсутствует, то считается, что b=∞

5) Для обозначения факультативных возможностей  используются знаки [].

6) Для группирования используются знаки ().

7) Точка показывает конец определения.

Пример 1:

В качестве примера рассмотрим определение рационального десятичного числа с использованием БНФ.

Ниже показаны несколько рациональных десятичных чисел с плавающей точкой:

1   2.04331   -12   +32.00   -2

Объединим все варианты в одной форме:

РДЧ ::= Целое_число ["." Натуральное_число].

Целое _число::=["+"|"-"] Натуральное_число.

Натуральное_число::= 1{Цифра}.

Цифра::="0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9".

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


 

1.3  Основные понятия Турбо-Паскаля, требуемые для создания программ

1.3.1        Подготовка и запуск программ

1.3.1.1   Интегрированная среда Турбо-Паскаля 7.0.

В комплекте поставки Турбо-Паскаля имеется как интегрированная среда разработки (TURBO.EXE - для реального режима, BP.EXE - для реального, защищенного режимов и Windows, BPW.EXE - для Windows), так и автономный компилятор-компоновщик (TPC.EXE - для реального режима, BPC.EXE - для реального, защищенного режимов и Windows). Отдельный от IDE текстовый редактор не предусматривается, так как для написания текста программы не требуется никаких специальных возможностей. В принципе, для подготовки текста программы может быть использован любой текстовый редактор любого производителя, с возможностью сохранения текста в формате ASCII.

В дальнейшем мы будем рассматривать программирование в реальном режиме, которое на языках высокого уровня отличается от создания программ для защищенного режима меньшим объемом памяти, выделяемой для программы и некоторым понижением скорости выполнения результирующего кода. Программирование для Windows или для любой другой оконно-ориентированной многозадачной операционной системы удобнее изучать с помощью Delphi, чисто языковые отличия которой от ТП очень невелики. Любая программа, разработанная для реального режима DOS, может быть преобразована под Windows, после изменения методов ввода-вывода с терминальных (вывод в окно DOS и ввод напрямую с клавиатуры, мыши или других устройств ввода) на оконно-ориентированные (вывод информации в окна Windows, и обработка ввода из очередей событий). В то же время программирование для защищенного режима или Windows значительно усложняется проблемами отладки. Поиск и анализ ошибок затруднен в первом случае, из-за отсутствия встроенного отладчика, а во втором, дополнительно, из-за событийной модели организации программы, которая принята в Windows, где часто невозможно определить, каким образом выполнение программы оказалось в некоторой точке. По этим причинам для первоначального изучения языка программирования проще воспользоваться IDE реального режима. На компьютерах с процессором 80386 и выше более удобна интегрированная среда BP.EXE, которая занимает около 10К обычной памяти компьютера, оставляя всю остальную (до 600 К) для выполнения программы.  К сожалению на процессорах ниже 80386 вся интегрированная среда размещается в обычной памяти, поэтому в таких случаях мы предлагаем использовать IDE TURBO.EXE.

1.3.1.2   Свойства интегрированной среды.

В этом параграфе мы коротко рассмотрим интегрированную среду Борланд-Паскаля (BP.EXE), как наиболее общую и включающую в себя все компоненты Турбо-Паскаля 7.0 для реального режима (TURBO.EXE).

ИС Турбо-Паскаля является многооконной системой. Это означает, что одновременно на экране может быть расположено несколько окон – с текстами программ, информацией для отладки, списком выполняемых в данный момент процедур и функций, значениями регистров микропроцессора и т.д., причем одно из них всегда является активным - т.е. в него производится ввод информации. При открытии или создании нового файла программы происходит открытие нового окна. ИС позволяет произвольно перемещаться по открытым окнам с помощью мыши или специальных комбинаций клавиш.

Сверху находится разветвленная система меню, которая позволяет легко управлять разработкой программы.

Каждый элемент меню может быть выбран с помощью перемещения по меню, с помощью "мыши" или "горячей клавиши" - нажатия комбинации ALT и подсвеченной буквы элемента меню (для главного меню) или просто нажатием подсвеченной буквы элемента подменю.

Например - выход в меню File может быть произведен нажатием Alt-F, а открытие затем файла (подменю File/Open ) - нажатием на клавишу О. Многие элементы меню также могут быть выбраны с помощью специальных комбинаций клавиш. Например, компиляция всей программы выполняется с помощью клавиши F9, а компиляция только текущего модуля программы, который находится в активном окне - с помощью комбинации Alt-F9.

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

1.3.1.3   Имена и расширения файлов для
работы в интегрированной среде.

Файл текста программы на языке Паскаль имеет расширение ".pas" и компилируется средой в исполняемый файл с расширением ".exe". Дополнительно, Турбо-Паскаль поддерживает модули, которые как отдельные, независимые блоки, которые могут включаться в любую программу. Подробное описание многомодульного программирования будет дано в параграфе 1.9, сейчас же нам требуется знать, что расширение файла исходного текста модуля также, как и для программы, ".pas", но после компиляции - ".tpu" (хотелось бы отметить, что в зависимости от того, для какого режима вы компилируете программу - реального, защищенного или Windows, расширения скомпилированного модуля будет соответственно ".tpu", ".tpp" и ".tpw").

Интегрированная среда сохраняет все свои настройки в файле с расширением ".tp" или ".bp", в зависимости от того, какую среду вы запускаете. Расположение окон хранится в файле с расширением ".dsk"

Большое значение для эффективности разработки программ имеет эргономика виртуального рабочего места. Прежде всего, требуется понимать, что нерационально хранить свои программы в том же каталоге, в котором находится Турбо-Паскаль. Мы предлагаем заводить для каждого нового проекта подкаталог, где будут находиться:

1. Файлы проекта (".pas", ".tpu", ".exe")

2. Файлы настройки среды (".tp" или ".bp", ".dsk")

3. Командные файлы для запуска интегрированной среды (например: bp.bat или turbo.bat, причем содержимое того и другого может состоять всего из одной строки - c:\bp\bin\bp.exe или c:\bp\bin\turbo.exe)

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

1.3.1.4   Некоторые комбинации клавиш,
используемых при работе с ИС.

Работа с окнами:

Перейти к некоторому окну по его номеру –ALT-номер окна

Перейти к произвольному окну – Alt-0. При этом открывается диалоговое окно со списком открытых в данный момент окон, в котором можно затем производится выбор)

Закрыть активное окно –Alt-F3

Компиляция и запуск программы.

Компиляция программы – Alt-F9

Компиляция всех измененных модулей программы –F9

Компиляция и запуск программы – Ctrl-F9

Работа с файлами.

Открыть файл – F3

Сохранить файл – F2

Работа с буфером обмена и блоками.

Отметить блок – Shift и клавиши перемещения курсора.

Скопировать блок в буфер обмена – Ctrl-Ins

Вставить блок из буфера обмена в программу по положению курсора – Shift-Ins

Полное описание меню, окон, комбинаций клавиш Турбо-Паскаля Вы можете найти в приложении.

1.3.2        Лексемы Турбо-Паскаля

Лексемы (минимальные смысловые единицы языка) в ТП подразделяются на специальные символы, зарезервированные слова, директивы, идентификаторы, метки, числа и строковые константы.

Турбо-Паскаль не различает в лексемах (кроме содержимого строковых констант) прописные и строчные буквы.

Например, зарезервированное слово for будет пониматься правильно и в следующих вариантах написания: FoR, fOR, FOR и т.д.

 


 

1.3.2.1   Специальные символы

Таблица специальных символов

+ - * /

Знаки арифметических операций

=  >  <  <>  >=  <=

Знаки операций сравнений

[ ] . , ( ) : ; ^ @ { } $ # (* *)

 

1.3.2.2   Зарезервированные слова

Зарезервированные слова в ТП используются для определения структуры программы и построения всех операторов языка:

and, asm, array, begin, case, const, constructor, destructor, div, do, downto, else, end, exports, file, for, function, goto, if, implementation, in, inherited, inline, interface, label, library, mod, nil, not, object, of, or, packed, procedure, program, record, repeat, set, shl, shr, string, then, to, type, unit, until, uses, var, while, with, xor.

Зарезервированные слова Турбо-Паскаля уникальны и не могут быть переопределены. Например, вы не можете объявить переменную с именем VAR или процедуру с именем uses.

1.3.2.3   Директивы

Директивы в ТП используются для изменения смысла действия некоторых операторов. Большинство из них являются процедурными директивами, то есть изменяют синтаксис и семантику процедур и функций:

absolute, assembler, export, external, far, forward, index, interrupt, near, private, public, resident, virtual

В отличие от зарезервированных слов, директивы могут быть переопределены в программе, однако разработчики Турбо-Паскаля не советуют этого делать.

Действительно, определив, например, переменную с именем absolute, мы не сможем в дальнейшем воспользоваться этой директивой в ее первоначальном значении.

В ТП имеются также директивы компилятора, которые не являются лексемами, так как имеют сложную структуру, практически идентичную комментариям (см. параграф 1.3.3.1). В то же время, как и обычные директивы, директивы компилятора вносят изменения, только на этот раз, в процесс компиляции программы. Более подробно они будут рассмотрены в приложении.

Примеры директив компилятора:

 {$I+}

 {$I include.pas}

 {$L objfile.obj}

 {$O-}

 {$define debug}

1.3.2.4   Идентификаторы

Идентификаторы позволяют ссылаться на используемые объекты. Выступают в качестве имен констант, типов, переменных, процедур, функций, модулей и полей в записях.

ИД::=(лат_буква| "_"){ лат_буква | "_" | цифра}.

Это означает, что любой идентификатор начинается с буквы или знака подчеркивания и продолжается буквами, знаками подчеркивания или цифрами, расположенными в произвольном порядке

Примеры идентификаторов:

R23_34

TTT

ttt

R4fDdG

При этом второй и третий идентификатор идентичны.

1.3.2.5   Метки

Метки являются либо числами от 0 до 9999 либо идентификаторами и используются совместно с оператором перехода goto.

Примеры меток: 0, 4532, Ff34

1.3.2.6   Числа

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

Шестнадцатеричное число ::=

["+"|"–"] "$" послед.шестн.цифр.

Последовательность шестнадцатеричных цифр представляет собой набор из цифр и первых 6 латинских букв в произвольном порядке (A означает 10, B - 11, C - 12, D - 13, E - 14, F - 15)

  Примеры вещественных десятичных чисел:

123.56

1.0034E+0032 (что означает 1.0034•1032)

  Примеры шестнадцатеричных чисел:

+$1F0

–$117

$ADFA

Принципы работы с различными системами счисления Вы можете узнать из приложения.

1.3.2.7   Строковые константы

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

Строковая_константа::=

Простая_строка |

Упр_символ |

{Простая_строка | упр_символ}.

Пробелы между простыми строками и управляющими символами не ставятся.

Простая_строка ::= " ' " {символ_строки} " ' ".

Под символом строки понимается любой видимый на экране символ, кроме знака апострофа.

Пример: 'Запасайтесь, дьяволы, гробами, стрелять буду!'

{Зощенко}

А следующий пример синтаксически неверен!

'Коль подарите нас своим вниманьем,

Изъяны все загладим мы стараньем.'

{Вильям Шекспир "Ромео и Джульетта"}

В случае, если строка должна включать в себя знак апострофа, требуется ставить два таких знака подряд.

Пример: строковая константа, состоящая из простой строки

'Вы даже, если хотите знать, нес''едобны'

{Стругацкие, "Сказка о тройке"}

при выводе на экран будет выглядеть следующим образом:

Вы даже, если хотите знать, нес'едобны

Упр_символ ::= "#" натуральное_однобайтовое_число

Управляющие символы позволяют использовать знаки, которые имеют специальное предназначение. Так как в стандартной таблице символов ASCII всего 256 знаков, то для определения любого символа достаточно однобайтового числа.

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

'Бьется в тесной печурке огонь'#10#13'На поленьях смола как слеза'

на экране будет выглядеть так

Бьется в тесной печурке огонь

На поленьях смола как слеза

Задачи для раздела "Лексемы Турбо-Паскаля"

Задание 1. Какие из нижеперечисленных наборов символов являются идентификаторами?

1)   ПАСКАЛЬ     d10       DIV       !g1w             $1             32

2)         monday    mod         xor       insert    absolute      132D2

3)         XxXxX   #YY         -.-.-     1_1_1     _1_1_1          RTR

Задание 2. Какие из нижеперечисленных наборов символов являются числами?

1)   345.43    23,56       $G12F        -$34A             12.4E-01             12345

2)   $FFFF    +$12         $12.4        78.789              4321234                FFF

3)    43,56      $HF2           1$3        111111             1A2B3C       $1A2B3C

Задание 3. Какие из нижеперечисленных наборов символов являются строковыми константами?

1)      "Утро вечера мудренее"

2)      'Без труда не вытащишь и рыбку из пруда'

3)      'Пришлось с'ехать с дороги'

4)      'Строковая'   #10   #13  'константа'

5)      #10#13'Попробуем другой вариант'#10#13

6)      ' '#0' '#1' '#2

Задание 4. Какие из нижеперечисленных наборов символов являются метками, числами, идентификаторами? Может ли набор символов относится одновременно к первому, второму и третьему типам лексем?

1)   dsewя     1023      45302      $g23     a1111     43.5D+12         DFF        $12.5

2)   34,34    -$45f             12       A!!!!       RTD       1097999    F12043   32D.314

3)     рык     FdFe         $345      +319        913          combat       32sd3       33e.31

1.3.3        Структура программы

1.3.3.1   Низкоуровневая структура программы

На низком уровне программа состоит из лексем и разделителей, размещаемых в строках, где разделители представляют собой пробелы или комментарии. Строки программы не могут быть длиннее 126 символов.

Под комментарием в ТП понимается любой набор знаков, заключенный между специальными символами { } или (* *). Комментарии и директивы компилятора очень схожи друг с другом, однако имеется очевидное отличие в синтаксисе (если после открывающей скобки сразу идет знак "$", то данный набор скобок считается директивой компилятора). Комментарии игнорируются компилятором. Они не могут быть вложенными при использовании только первого или только второго набора скобок.

Пример:

{Это правильный комментарий}

(* Это тоже

правильный комментарий *)

{А {здесь} ошибка!!! }

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

Пример:

{Демонстрируем (*первый*) уровень (*вложенности*)}

(*Снова {демонстрируем первый} {уровень} вложенности*)

{Демонстрируем (*типичную {ошибку}, которую допускают*) начинающие программисты}

1.3.3.2   Важнейшие операторы Турбо-Паскаля

Операторы в ТП подразделяются на простые и структурные.

В этом параграфе среди простых операторов мы рассмотрим оператор присваивания и вызова процедур (функций), среди структурных - только составной оператор.

Оператор присваивания

Оператор присваивания используется в основном для присвоения значений переменным.

Оператор_присваивания::=

(ИД_переменной | ИД_функции) ":=" выражение.

ИД_переменной - идентификатор (имя) некоторой переменной, которой присваивается значение выражения. Естественно, что выражение должно возвращать значение такого же типа, какой определен для переменной (или совместимого по присваиванию - см. параграф 1.7.6). Это означает, например, что если переменная описана в разделе объявлений как целочисленная, то ей можно присваивать выражения только целочисленного или натурального типа, а если переменная строковая, то выражение должно быть строковым или символьным, но никак, например, не вещественным.

Использование ИД_функции мы рассмотрим в позднее, в параграфе 1.8.

Пример:

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

A:=7+23-A;

B:='Утро вечера мудренее';

A:=B;          {ОШИБКА!!!}

B:='3415';

B:=12;         {ОШИБКА!!!}

Составной оператор

Составной_оператор::=

"begin" оператор {";" оператор} [";"]"end"

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

Пример

begin

 A:=7; c:=a+6;

 begin

  a:=8;

  c:=c+a;

 end;

end;

Оператор вызова процедуры

Оператор вызова процедуры производит вызов ранее определенной или встроенной процедуры, передавая ей список фактических параметров. Процедура Обязательно должна быть определена либо в разделе объявления процедур и функций программы, либо в одном из подключаемых модулей (в том числе и объектных, присоединяемых к программе с помощью директивы компилятора $L).

Вызов функций в ТП производится аналогично оператору вызова процедуры. Отличие состоит в том, что функции используются только в выражениях (исключение составляет применение директивы компилятора {$X+}, после чего любая функция может быть вызвана как процедура).

Оператор_процедуры ::=

ИД_процедуры список_фактических_параметров.

Список_фактических_параметров ::=

["(" выражение {"," выражение}")"]

Например:

begin

 

  RunError(111);

 

  Exit;

 

  Writeln('Вы истратили ', N, ' рублей из ', S1+S2);

 

  Dec(A,5+b);

 

  MyProc(10, A+X+Z, 34);

end.

Выражения в списке фактических параметров должны быть совместимыми по типу с формальными параметрами процедуры, заданными при ее определении. Количество формальных и фактических параметров также должно совпадать.

Например:

Процедура Bar, определенная в модуле Graph, рисует на экране закрашенный прямоугольник и имеет следующий заголовок:

procedure Bar(x1, y1, x2, y2: Integer);

x1, y1, x2, y2 - формальные параметры процедуры Bar.

Ниже приводятся правильные и неправильные примеры вызовов этой процедуры.

var   a,b:integer;

  c:real; x:boolean;

begin

{вызывая процедуры, подставляем вместо формальных параметров фактические}

bar(a,b,15,23+b);

bar(x,12,34,a+b);    {неверно!}

bar(c+a,1,a,a+b);    {неверно!}

bar(a+10,b,x,30+b);    {неверно!}

bar(a/b,b,a,10);     {неверно!}

bar(a div b,b,a,10);

bar(a+b,a+b,a*b,a*b);

end.

Более подробно о процедурах и функциях будет рассказано в параграфе 1.8, а также ниже, при анализе раздела объявлений процедур и функций.

1.3.3.3   Высокоуровневая структура программы

На высоком уровне программа состоит из необязательного заголовка программы и блока, который собственно является телом программы.

Программа::=Заголовок_программы Блок "."

Заголовок_программы::=

["program" идентификатор ";"] [подключение_модулей]

Блок ::= Раздел_объявлений Составной_оператор

Заголовок программы

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

Подключение_модулей::= "uses" список_модулей ";".

Под списком модулей мы понимаем имена модулей, перечисленных через запятую.

Пример:

Program Grafik;

 Uses Graph, Dos, Printer;

 ...

В ТП существует специальный, автоматически подключаемый модуль System.tpu, содержащий в себе множество стандартных процедур и функций и не требующий указания в списке подключаемых модулей. Если в дальнейшем, объясняя работу той или иной функции, процедуры, смысл переменой, константы и т.д., мы не будем приводить имя модуля, в котором она находится, значит данный объект определен в модуле System.

Тело программы.

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

В разделе объявления происходит описание всех идентификаторов и меток, которые будут использоваться в составном операторе блока.

Раздел объявления действителен только для этого блока и всех внутри лежащих.

Это означает, что если, например, переменная объявлена в некотором внутреннем блоке, и, следовательно, является локальной для него (например - в теле процедуры или функции), то во внешнем блоке она не может быть использована. Если же переменная объявлена во внешнем блоке то в любом из внутренних она может быть использована.

Из этого правила есть одно исключение. Если идентификатор или метка с одним и тем же именем объявлены как в основном, так и во вложенном блоках, то последний будет иметь доступ только к своему идентификатору или метке. Более подробно этот нюанс будет рассмотрен при изучении процедур и функций в параграфе 1.8.

Раздел объявлений.

Далее мы проанализируем раздел объявлений.

Раздел_объявления::={раздел_объявления_переменных

| раздел_объявления_констант

| раздел_объявления_меток

| раздел_объявления_типов

| раздел_объявления_проц_и_функц}

В разделе объявления меток определяются используемые в блоке метки, а в остальных разделах - различные идентификаторы.

В этом параграфе мы рассмотрим разделы объявления переменных, констант и, частично, процедур и функций.

Раздел объявления переменных

Раздел объявления переменных служит для определения всех переменных, используемых в текущем и всех вложенных блоках.

Раздел_объявления_переменных::=

"VAR" 1{ объявление_переменных }.

Объявление_переменных::=

ИД_перем { "," ИД_перем } ":" тип [местоположение]";".

Местоположение::= "absolute" (адрес | ИД_переменной)

Тип переменной указывает набор значений, которые она может принимать и действия, которые могут быть над ней выполнены. В ТП определено множество различных типов, из которых в этом параграфе мы рассмотрим только один из целочисленных, один из вещественных и логический: INTEGER является типом с диапазоном значений от -32768 до 32768, занимающим 2 байта памяти. REAL является вещественным типом с диапазоном значений от  до  в положительной области и с таким же диапазоном в отрицательной. При этом количество значащих десятичных цифр 11-12 при 6 байтах занимаемой памяти. BOOLEAN представляет собой логический тип и имеет только 2 значения - true(истина) и false(ложь). Логические типы используется по большей части в условных операторах, которые позволяют выполнить то или иное действие в зависимости от значения выражения логического типа, а также в операторах цикла, которые повторяют один или группу операторов до тех пор пока выражение логического типа не примет значение true или false (в зависимости от типа цикла).

Местоположение позволяет определять абсолютные переменные. Если после слова absolute стоит адрес памяти в виде СЕГМЕНТ:СМЕЩЕНИЕ (например $40:$17), это означает адрес расположения абсолютной переменной в памяти компьютера. Если после absolute находится имя ранее определенной переменной, то и эта переменная и абсолютная будут ссылаться на одну и ту же область памяти.

Пример раздела объявления переменных:

Var x, y : integer;

  k1:integer absolute $40:$17;

  xx:integer absolute x;

  Z:real; bool:boolean;

     B123FG_34, XXX, yyy : integer;

     MyVar, Temporary_Variable : real;

  В данном примере, обращаясь к переменной X, мы будем автоматически обращаться к значению переменной XX и наоборот. А обращаясь к переменной K1, мы будем обращаться к ячейке памяти по адресу $40:$17, в которой находится статус нажатия таких клавиш, как Alt, Shift, Ctrl, а также статус режимов Scroll Lock, Num Lock и Caps Lock.

Раздел объявления констант.

Константа - это именованное, рассчитываемое при компиляции и неизменяемое в процессе работы программы значение.

Типированная константа - это переменная, инициализируемая первоначальным значением в разделе объявления констант.

Раздел_объявления_констант:=

"CONST" 1{ объявление_констант }.

Объявление_ констант::=

ИД_константы [":" тип] "=" значение_константы ";".

ИД_константы - это идентификатор (имя) константы.

Если при объявлении константы используется тип, то константа является типированной.

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

Пример:

Var a,b:integer;

Const X=7+2;

  Y=6-x*2;

Z=A+x+y; { Неверно! A является переменной с неизвестным в момент компиляции значением, поэтому не может быть использована для расчета константы}

  CS:integer=5+x; {типированная константа}

D=CS+X; { Неверно! Несмотря на то что значение типированной константы CS известно во время компиляции, оно может изменяться в процессе работы программы. Однако значение константы D должно оставаться постоянным, что невозможно при изменяемости CS }

Раздел объявления процедур и функций.

В разделе объявлений также можно определить пользовательские процедуры и функции. Более подробно данный пункт мы рассмотрим в параграфе 1.8, а здесь коротко разберем их заголовки.

Заголовок любой процедуры выглядит следующим образом:

Заголовок_процедуры ::= "procedure" ИД

["(" объявление_параметра

 {";" объявление_параметра }")"] ";".

объявление_параметра::=["var" | "const"] список_ИД

[":" тип_параметра].

Если в объявлении формального параметра присутствует зарезервированное слово VAR, то процедуре в качестве этого параметра может передаваться только переменная, так как во время выполнения процедура будет сама помещать в нее и возвращать основной программе значение. Если VAR или CONST отсутствует, то передаваться может любое выражение соответствующего типа. Если отсутствует тип параметра и в пояснении к процедуре он специально не оговорен, то передаваться может переменная любого типа. Формальные параметры, предваряемые зарезервированным словом CONST мы рассмотрим в параграфе 1.8.

Пример:

В модуле Graph определена следующая процедура для инициализации графического режима:

procedure InitGraph( var GraphDriver:Integer;

var GraphMode: Integer;

PathToDriver: string);

При вызове процедуры ей передаются три параметра - два целочисленных и один строковый. Первые два параметра обязаны быть переменными, третий - произвольным строковым выражением:

var a,b:integer;

begin

 a:=0;

 InitGraph(a,b,'c:\bgi');

 

end.

Процедура InitGraph при таком вызове проанализирует графическую подсистему, установит максимально возможный графический режим, считав файл графического драйвера из подкаталога 'c:\bgi' и возвратит в переменной a номер видеоадаптера, а в переменной b - номер установленного графического режима.

Заголовок функции несколько отличается от заголовка процедуры:

Заголовок_функции ::= "function" ИД

["(" объявление_параметра

 {";" объявление_параметра }")"] ":" ИД_типа ";".

Идентификатор типа, стоящий после двоеточия, определяет тип возвращаемого функцией значения.

Пример:

Заголовки функций Sin и Cos выглядят следующим образом:

function Sin(X: Real): Real;

function Cos(X: Real): Real;

Функции принимают в качестве аргумента угол в радианах (вещественный) и возвращает в качестве результата синус и косинус этого угла (также вещественный).

var x:real;

begin

 x:=0.5;

 writeln(sin(x)*sin(x)+ cos(x)*cos(x));

 x:=0.7;

 writeln(sin(x)*sin(x)+ cos(x)*cos(x));

 x:=1.97;

 writeln(sin(x)*sin(x)+ cos(x)*cos(x));

 readln; {для задержки}

end.

Предскажите значения чисел, которые появятся на эеране после выполнения программы.

1.3.3.4   Примеры небольших программ

На данный момент нам известно уже достаточно, чтобы составить несколько небольших программ.

Задача 1[1, № 31]. Дано действительное число A. Не пользуясь никакими операциями, кроме умножения, получить A6 за 3 операции.

Решение:

Для решения данной задачи нам потребуется промежуточная переменная, так как простым умножением мы сможем получить A6 только за 5 операций, а не за 3, как требуется по условию задачи.

Var A,B:real;

begin

 readln(a);

 b:=a*a; {первая операция}

 b:=b*b*b; {еще две операции}

 writeln(b);

readln;

end.

Задача 2. Даны действительные числа X и Y. Найти:

Решение:

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

PROGRAM Calc;

      VAR rXY,rX,rY,rSinXY,rCosXY:Real;

   BEGIN

      readLn(rX);

      readLn(rY);

      rXY:=rX+rY;

      rSinXY:=sin(rXY);

      rCosXY:=cos(rXY);

      WriteLn((rSinXY*rCosXY)/(rCosXY+rSinXY+(rSinXY/rCosXY)));

      readln;

   END.

Задача 3. Треугольник задан своими сторонами. Найти площадь окружности, описанной около треугольника.

Решение:

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

; ;

;

Теперь можно составить программу (опишем ее подробнее, чем предыдущие):

var a,b,c,p,s,r:real; {определяем требуемые переменные}

 const Pi=3.1415926; {определяем константу Pi}

begin

 readln(a,b,c); { вводим стороны треугольника}

 p:=(a+b+c)/2;  { находим полупериметр}

 s:=sqrt(p*(p-a)*(p-b)*(p-c)); {находим площадь треугольника

SQRT - функция для

вычисления квадратного корня}

 writeln('Площадь треугольника = ',s);

 r:=(a*b*c)/(4*s); { находим радиус описанной окружности }

 s:=pi*r*r; { присваиваем переменной S площадь описанной окружности}

 WRITELN('Площадь описанной вокруг тр-ка окр-ти = ',s);

 readln;

end.

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

1.     Всегда делайте некоторый отступ вправо внутри составного или любого другого структурного оператора.

2.     Называйте свои переменные, константы, процедуры и т.д. удобочитаемыми именами.

3.     Не создавайте программные блоки размером более 50 (а лучше - 25) строк, разбивайте программу на небольшие, логически завершенные части.

Задачи для раздела "Структура программы"

Задание 5. Какие из нижеперечисленных наборов символов являются комментариями?

1)      {предполагаю, что (* это } комментарий *)

2)      (* {} это {} уж {} точно {} комментарий {}*)

3)      { комментарий :-) Комментарий ;-) КОММЕНТАРИЙ  *)  }

4)      { remark :-} Remark ;-} REMARK :-{}

Задание 6. Какие из нижеперечисленных наборов символов являются синтаксически верными вызовами процедур?

1)      Proc1(123; 7; 32)

2)      x:=3*sin(3)

3)      Ps()

4)      X1(3,5,v)

Задание 7. Напишите программу, в которой будет 3 раздела объявления переменных 1 раздел объявления констант.

Задание 8[1, № 1]. Даны два числа. Найти их сумму, разность и произведение.

Задание 9[1, № 3]. Дано ребро куба. Найти объем куба и площадь его боковой поверхности.

Задание 10. В электрической цепи находятся четыре резистора с сопротивлениями R1, R2, R3 и R4. Определить общее сопротивление цепи в случае последовательного соединения и всех возможных вариантов параллельного.

Задание 11[1, № 31]. Дано число a. Вычислить a64 за 6 операций. Определить сложности, возникающие при проверке программы на компьютере.

Задание 12. Даны числа r1 и R2 (r1<R2). Определить площадь кольца, внутренний радиус которого равен r1, а внешний - R2.


 

1.4  Выражения

Выражением называется вычисляемая структура, возвращающая значение некоторого типа.

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

Пример:

Выражение , записанное на ТП, может выглядеть следующим образом: x*sqrt(x+y)/sin(25*x)+sqrt(cos(x)/sqr(sin(x)))

1.4.1        Структура выражений

В ТП выражения строятся из операций и операндов. Операнды могут быть переменными, константами, результатами вызова функций, и также выражениями. Большинство операций в ТР являются бинарными, то есть действуют на 2 операнда, стоящих слева и справа от операции. Некоторые операции являются унарными, то есть действуют на операнд, стоящей за операцией. Выражение может содержать и только один операнд, то есть константа или переменная также являются выражением в его максимально вырожденном виде.

Прямоугольная выноска: Операнд-выражениеПрямоугольная выноска: ОперандПрямоугольная выноска: ОперацияПрямоугольная выноска: ОперандПрямоугольная выноска: ОперандПрямоугольная выноска: Операция
Подпись: x				*							 (	x				+ 				y	)

Рассмотрим следующее выражение:


Пример:

1.4.2        Приоритет операций

Для правильного формирования выражения очень важна расстановка скобок. В их отсутствие порядок выполнения зависит от приоритета операций.

Операции

Приоритет

Вид операции

@, not

первый (высший)

унарные операции

*,/,div,mod, and,shl,shr

второй

операции умножения

+,-,or,xor

третий

операции сложения

=, <>, <, >,<=, >=, in

Четвертый
(низший)

операции отношения

1.4.3        Типы операций

В ТП существует 6 типов операций:

Операции ::=      арифметические_операции |

логические_операции |

строковые_операции |

операции_над_множеством |

операции_отношения |

операция_@

1.4.3.1   Арифметические операции

Арифметические_операции::= "+" | "-" | "*" | "/" | "div" | "mod".

Арифметические операции действуют только на целочисленные и вещественные выражения. div и mod - только на целочисленные.

Операция div используется для целочисленного деления, при этом дробная часть результата отбрасывается.

Пример: результатом выражения 5 div 3 будет число 1.

Операция mod используется для нахождения остатка от целочисленного деления.

Пример: 5 mod 3 = 2, 1 mod 10 = 1, 11 mod 10 = 1,.32 mod 11 = 10.

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

Пример:

Выражение 7/2 даст результат 3.5. Выражение 12/4 даст результат 3.0 (вещественный), а не 3 (целочисленный).

Операции "+" и "–" могут быть также унарными.

Пример:

Если B - целочисленная или вещественная переменная, то выражения -b и +b совершенно правомерны.

1.4.3.2   Логические операции

Логические_операции::= "not" | "and" | "or" | "xor" | "shl" | "shr".

Операция not является унарной.

Логические операции могут производить действия над целочисленным (побитовые операции) и логическим типом. shl и shr - только над целочисленным типом (побитовый сдвиг влево и вправо на целое количество бит). Наиболее часто первые четыре операции используются для формирования сложных логических выражений в условных и циклических операторах (см. параграф 1.6.2). Операции shl и shr используются чаще всего для быстрого деления и умножения целых чисел на степени двойки (2, 4, 8, 16, 32 и т.д.).

Дополнительная информация о логических операциях и двоичной системе счисления (для операций shl и shr) находятся в приложении.

1.4.3.3   Строковые операции

Строковые_операции::= "+";

Данная строковая операция производит контактенцию строк. В качестве операндов могут служить строковые, символьные и упакованные строковые значения.

Пример: Для создания длинных строк можно использовать операцию "+"

...

Writeln('Вначале Он сотворил Аинур, Первых Святых,'#13#10 +

'Порождение Его мысли,'#13#10 +

'И они были при Нем уже тогда,'#13#10 +

'Когда еще ничего другого не было.');

...

{Джон Рональд Руэл Толкиен. "Сильмариллион"}

1.4.3.4   Операции над множеством

Операции_над_множеством::= "+" | "-" | "*".

Бинарные операторы, которые выполняют соответственно объединение, разность и пересечение множеств.

Операции над множеством мы подробно рассмотрим в параграфе 1.7.2.4.

1.4.3.5   Операции отношения

Операции_отношения::= "="| ">"| "<"| "<>"| ">="| "<="| "in".

Операндами операций "=", ">", "<", "<>", ">=", "<=" могут быть простые и строковые типы, для операций "=" и "<>" также типы указателя и множества, для операций "<=", ">=", "in" - множественный тип. Типы операндов должны быть совместимыми (см. параграф 1.7.6). Единственное исключение из этого правила - разрешается сравнение целочисленных и вещественных операндов.

Результат действия операций отношения - всегда логический.

Операции отношений возвращают TRUE (истину) при выполнении следующих условий:

Операция

Название

Выполняемое условие

=

Равно

левый операнд равен правому

>

Больше

левый операнд больше правого

<

Меньше

левый операнд меньше правого

<>

не равно

левый операнд не равен правому

>=

1) больше либо равно 2) надмножество

левый операнд больше или равен правому.

Если операнды являются совместимыми типами множеств, то левое множество представляет собой надмножество правого

<=

1) меньше либо равно

2) подмножество

левый операнд меньше или равен правому

Если операнды являются совместимыми типами множеств, то левое множество представляет собой подмножество правого

in

Вхождение во множество

Левый операнд входит во множество, представленное правым операндом

Пример:

var b:boolean;

 begin

b:=5>2;

  writeln(b);

 end.

В результате выполнения программы на экране появится слово TRUE.

1.4.3.6   Операция @

С помощью операции @ можно создать указатель на переменную, процедуру или функцию, то есть получить адрес ее местоположения в памяти. Операция является унарной.

Более подробно эта операция будет рассмотрена в параграфе 1.7.3.

Задачи для раздела " Выражения"

Задание 13. Сгруппируйте каждую операцию отношения (кроме in), с обратной для нее.

Задание 14. Сократите логическое выражение: not(a>b) and not (a<c)

Задание 15. Предскажите результат вычисления следующего выражения:
-2+3*6-12
/6. Каким образом надо расставить скобки. чтобы получить
число -1
?.

Задание 16. С помощью только одного вызова процедуры Writeln, 20 раз написать на экране фразу "Ничего на свете лучше нету, чем писать программы "на лету" ".

Задание 17. Дано трехзначное натуральное десятичное число. Используя операции div и mod, выделить цифры числа и напечатать его в столбик.

Задание 18. Дано натуральное число А. Не используя арифметические операции, увеличить его в 16 раз.

Задание 19. Не используя арифметические операции преобразуйте число 7 в 56, 56 - в 14, а 14 - в 224.

Задание 20. С помощью арифметических операций поменяйте местами содержимое двух вещественных переменных, не используя третью, промежуточную переменную.


 

1.5  Стандартные функции и процедуры

Турбо-Паскаль содержит множество стандартных функций и процедур, сосредоточенных в модуле system.tpu (который по умолчанию находится в библиотеке turbo.tpl, но может быть выделен оттуда и использован отдельно).

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

1.5.1        Процедуры и функции модуля System

1.5.1.1   Арифметические функции

Функция или процедура

Действие

Тригонометрические функции

f. ArcTan(X: Real): Real;

Возвращает арктангенс аргумента

f. Cos(X: Real): Real;

Возвращает косинус аргумента

f. Sin(X: Real): Real;

Возвращает синус аргумента

f. Pi: Real;

Возвращает число Пи:

3.1415926535897932385

Функции для выделения целой и дробной части числа

f. Frac(X: Real): Real;

Возвращает десятичную часть аргумента

f. Int(X: Real): Real;

Возвращает целую часть аргумента.

Логарифмическая и экспоненциальная функции

f. Ln(X: Real): Real;

Возвращает натуральный логарифм аргумента.

f. Exp(X: Real): Real;

Возвращает экспоненту аргумента

Степенные функции

f. Sqr(X: тип):тип;

Возвращает квадрат аргумента. Тип результата соответствует типу аргумента.

f. Sqrt(X: Real): Real;

Возвращает квадратный корень аргумента

Абсолютное значение

f. Abs(X:тип):тип;

Возвращает модуль аргумента. Тип результата соответствует типу аргумента.

В отличии, например, от языка программирования Бейсик, ТП содержит очень небольшой объем арифметических функций. С помощью несложных операций он может быть расширен.

ab = exp(b*ln(a)); {а в степени b}

logab=ln(b)/ln(a); {логарифм по произвольному основанию}

Tan(x) = sin(x)/cos(x); {тангенс x}

сTan(x) = 1/tan(x); {арктангенс x}

Arcsin(x)=arctg(x/sqrt(1-sqr(x))); {арксинус x}

Arccos(x)=Pi/2-arcsin(x); {арккосинус x}

Arcctan(x)= Pi/2-arctan(x); {арккотангенс x}

Ch(x)=(exp(x)+exp(-x))/2; {гиперболический косинус x}

Sh(x)=(exp(x)-exp(-x))/2; {гиперболический синус x}

1.5.1.2   Процедуры выхода

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

procedure Exit

Процедура Exit немедленно завершает работу текущего блока. Если текущий блок - программа, то происходит выход из нее.

procedure Halt [ ( Exitcode: Word ) ];

Процедура Halt производит выход из программы с кодом завершения ExitCode.

procedure RunError [(Errorcode: Byte)];

Процедура RunError производит выход из программы, эмулируя ошибку времени выполнения с номером Errorcode.

Процедура Exit чаще всего используется для выхода из процедур и функций.

Процедура Halt - для остановки программы с целью передачи операционной системе кода завершения, который затем может быть проанализирован, например, с помощью оператора BAT-файла :
IF ErrorLevel код завершения ...

Процедура RunError применяется обычно для отладки программы. Ее использование позволяет наиболее простым методом определить место ошибки, а также проверить реакцию программы на критические ситуации.

1.5.1.3   Различные процедуры и функции

Обработка больших объемов данных (до 64К)

procedure FillChar(var X; Count: Word; value)

Процедура FillChar заполняет переменную или произвольную область памяти X значением Value любого типа (размером в 1 байт), в количестве Count байт

procedure Move(var Source, Dest; Count: Word);

Процедура Move копирует область памяти Source в Dest размером в Count байт.

Работа с байтами одного слова

function Hi(X): Byte;

Функция Hi возвращает старший байт аргумента. X должна быть переменной типа integer или word (см. параграф 1.7.1.1).

function Lo(X): Byte;

Функция Lo возвращает младший байт аргумента. X должна быть переменной типа integer или word (см. параграф 1.7.1.1).

function Swap(X:тип): тип;

Функция Swap меняет в аргументе младший и старший байт местами и возвращает полученный результат. Переменная X должна быть любого порядкового двухбайтного типа (см. параграф 1.7.1.1)

Генератор случайных чисел

function Random [( Range: Word)]: тип

Функция Random возвращает:

1)     Случайное вещественное число из промежутка (0,1) (В случае, если Range отсутствует. При этом тип результата – Real)

2)      Случайное натуральное число (или 0) из промежутка [0, Range) (В случае, если Range передается в качестве аргумента. При этом тип результата – Word)

procedure Randomize;

Процедура Randomize инициализирует генератор случайных чисел. После вызова этой процедуры Random будет возвращать другую последовательность случайных чисел.

Границы и размеры типов данных

function High(X):тип

Функция High возвращает максимальное значение аргумента. Если переданный параметр является строкой или массивом, то возвращаемый результат - максимальная длина строки или массива. Если же переданное значение имеет порядковый тип, то возвращаемое является верхней границей диапазона данного типа.

function Low(X):тип

Функция Low возвращает минимальное значение аргумента. Если переданный параметр является массивом, то возвращаемый результат - минимальный индекс массива. В случае передачи строки возвращаемое значение = 0. Если же переданное значение имеет порядковый тип, то возвращаемое является нижней границей диапазона данного типа.

function SizeOf(Аргумент):integer;

Функция SizeOf возвращает размер аргумента в байтах. Передаваемый в функцию параметр может быть как переменной, так и типом данных

Параметры командной строки

function ParamCount: Word;

Функция ParamCount возвращает количество аргументов, переданных программе в командной строке DOS при ее запуске.

function ParamStr(Index:byte): String;

Функция ParamStr возвращает параметр командной строки с порядковым номером Index. Параметр с номером 0 - это полное имя самой программы.

1.5.1.4   Обзор дополнительных
процедур и функций

Строковые процедуры и функции будут рассмотрены в параграфе 1.7.2.2.

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

Процедуры и функции для работы с указателями и динамической памятью будут рассмотрены в параграфе 1.7.3.

При изучении типов также будет рассмотрено несколько процедур и функций, ассоциированных с конкретными типами данных.

1.5.2        Процедуры и функции модуля Crt

Модуль Crt содержит множество процедур и функций, поддерживающих работу с экраном и клавиатурой в обход стандартных потоков ввода-вывода.

Обработка клавиатуры

function KeyPressed: Boolean;

Функция KeyPressed возвращает True, если произошло нажатие клавиши.

function ReadKey: Char;

Функция ReadKey возвращает символ с клавиатуры, ожидая нажатия.

Окна

procedure Window(X1, Y1, X2, Y2: Byte);

Процедура Window устанавливает окно на экране. Весь дальнейший вывод и ввод информации происходит в его пределах. В качестве параметров передаются координаты левого верхнего и правого нижнего углов

procedure ClrScr;

Процедура ClrScr очищает текущее окно.

Цвета

ТП определяет в модуле Crt 15 констант для цветов текстовых режимов, плюс атрибут мигания, который для требуемого эффекта суммируется с цветом тона.

Константы тона и фона:

Black=0, Blue=1, Green=2, Cyan=3, Red=4, Magenta=5, Brown=6, LightGray=7;

Константы тона:

DarkGray=8, LightBlue=9, LightGreen=10, LightCyan=11, LightRed=12, LightMagenta=13, Yellow=14, White=15;

Атрибут мигания: Blink=128

procedure TextBackground(Color: Byte);

Процедура TextBackground устанавливает цвет фона

procedure TextColor(Color: Byte);

Процедура TextColor устанавливает цвет тона

procedure NormVideo;

Процедура NormVideo восстанавливает цвета тона и фона, определенные до запуска программы.

procedure HighVideo;

Процедура HighVideo устанавливает повышенную яркость символов.

procedure LowVideo;

Процедура LowVideo устанавливает пониженную яркость символов.

Позиционирование курсора

procedure GotoXY(X, Y: Byte);

Процедура GotoXY устанавливает курсор в позицию X,Y

function WhereX: Byte;

Функция WhereX возвращает текущую координату X курсора.

function WhereY: Byte;

Функция WhereY возвращает текущую координату Y курсора.

Экранные строки

procedure ClrEol;

Процедура ClrEol очищает все символы, начиная с позиции курсора до конца строки.

procedure DelLine;

Процедура DelLine удаляет строку, на которой находится курсор, все нижележащие поднимаются выше.

procedure InsLine;

Процедура InsLine вставляет пустую строку по позиции курсора, все нижележащие опускаются ниже.

Звуки

procedure Sound(Hz: Word);

Процедура Sound включает внутренний динамик с частотой звука Hz герц.

procedure NoSound;

Процедура NoSound выключает внутренний динамик

Различные процедуры

procedure TextMode(Mode: Integer);

Процедура TextMode устанавливает текстовый режим экрана.

В модуле Crt определен следующий набор констант режимов экрана:

  BW40=0, черно-белый режим 40x25

  CO40=1, цветной режим 40x25

  BW80=2, черно-белый режим 80x25

  CO80=3, цветной режим 80x25

  Mono=7, монохромный режим 80x25

procedure AssignCrt(var f: Text);

Процедура AssignCrt сопоставляет текстовый файл с экраном. Таким образом весь дальнейший ввод или вывод в текстовый файл будет перенаправляться на экран.

procedure Delay(MS: Word);

Процедура Delay приостанавливает работу программы на MS тиков.

Задачи для раздела "Процедуры и функции модуля Crt"

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

Задание 21. Ввести X и Y координаты курсора. Перевести курсор в позицию X и Y и вывести на экран желтым цветом (Yellow) слово "ЗДЕСЬ!".

Задание 22. Всеми цветами вывести на экран фразу "Паскаль - ЧЕМПИОН!".

Задание 23. Вывести на экран ваше любимое четверостишие. С промежутком примерно в секунду, стереть его построчно с экрана, начиная с первой строки.

Задание 24. В центре экрана создать окно размером 6 строк на 50 столбцов. Затем решить предыдущую задачу.

Задание 25. Вывести на экран фразу "Жизнь - это движение!". Передвинуть ее на 10 строк вниз с задержкой в 0.25 секунды на каждой строке. Затем аналогичным образом привести ее в первоначальное положение.

Задание 26. Попытайтесь сыграть музыкальную гамму. Время звучания каждой ноты - полсекунды.


 

1.6  Операторы

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

Оператор::=[метка ":"] [(простой_оператор | структурный_оператор )]

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

1.6.1        Простые операторы

Простой_оператор::=оператор_присваивания |

оператор процедуры |

оператор перехода.

Оператор присваивания и оператор вызова процедуры были подробно разобраны в параграфе 1.3.3.

Оператор перехода позволяет передать управление оператору, которому предшествует метка. Любая метка, используемая в блоке, должна быть в этом блоке и объявлена. Для этого требуется воспользоваться разделом объявления меток (см. параграф 1.3.3).

Раздел_объявления_меток:= "LABEL" Метка {"," Метка}";".

Оператор_перехода::= "goto" метка.

Например:

label xxx1,12;

var x:integer;

begin

xxx1:    x:=7;

goto 12;

12:

x:=x+1;

goto xxx1;

end.

Не допускаются переходы из одного блока в другой, вне зависимости от его уровня вложенности.

Оператор goto позволяет легко перемещаться по программе, однако злоупотребление им приводит к нежелательным результатам. Применение goto ухудшает читабельность программы, создает значительные трудности при попытках анализа ее структуры, затрудняет отладку и тестирование, является источником трудноуловимых ошибок. В разделах математики, посвященных программированию, доказывается, что ЛЮБАЯ программа может быть создана без использования оператора goto. Применяйте goto только в тех случаях, когда с его помощью можно значительно сократить размер программы.

1.6.2        Структурные операторы

Структурные операторы отличаются от простых тем, что содержат обычно в своем теле один или более других операторов.

Структурный_оператор::= составной_оператор |

условный_оператор |

оператор_цикла |

оператор_With.

В этом параграфе мы рассмотрим только условные операторы и операторы цикла. Составной оператор был введен ранее при анализе общей структуры программы (см. параграф 1.3.3), а оператор With, используемый для ускорения доступа к переменным типа запись, мы разберем позднее, при описании этого типа в параграфе 1.7.2.3.

1.6.2.1   Условные операторы

Условные операторы позволяют выбирать для выполнения один или не одного оператора из двух или из списка. Используются для организации ветвлений в программе, значительно реже – для организации циклов.

Условный оператор ::= "оператор_if"| "оператор_case"

Оператор IF

Оператор_if::= "if" логическое_выражение "then" оператор1 ["else" оператор2]

Если логическое выражение принимает значение True, то выполняется оператор1, иначе - оператор2.

Например:

Задача 4. Дано 2 числа. Найти наибольшее из них.

Решение:

var a,b:integer;

begin

 readln(a,b); {вводим 2 числа с клавиатуры }

{находим максимальное и выводим его на экран}

 if a>b then writeln(a) else writeln(b);

 readln;

end.

Задача 5. Найти максимальное из трех чисел.

Решение:

var a,b,c,z:integer;

begin

 readln(a,b,c);

 if a>b then z:=a else z:=b; {переменной z присваиваем максимальное

из двух первых чисел}

{сравниваем z с третьим числом и выводим максимальное на экран}

 if z<c then z:=c;

 writeln(z);

 readln;

end.

Задача 6. Определить, поместится ли открытка со сторонами a и b в конверте со сторонами x и y.

Решение:

var a,b,x,y:integer;

begin

 write('Введите через пробел стороны открытки ');

 readln(a,b);

 write('Введите через пробел стороны конверта ');

 readln(x,y);

{ Воспользуемся при формировании логического выражения в условном операторе операциями AND и OR}

 if ((a<x)and(b<y))or((b<x)and(a<y)) then

writeln('Открытка помещается в конверт')

else

writeln('Открытка в конверт не входит');

readln;

end.

Задача 7. Решить квадратное уравнение ax2+bx+c=0

Var a,b,c:real;    {коэффициенты уравнения}

d:real;   {дискриминант}

x1,x2:real;   {корни уравнения}

Begin

Readln(a,b,c); {вводим коэффициенты}

If a=0 then

writeln('Уравнение не является квадратным')

Else begin

{находим дискриминант}

D:=sqr(b)-4*a*c;

{проводим анализ дискриминанта и требуемые вычисления во вложенных операторах IF}

If d<0 then

writeln('Уравнение не имеет действительных корней')

else

if d=0 then begin

x1:=-b/(2*a);

writeln('Уравнение имеет 1 корень: X=',x1:7:3);

end

else begin

x1:=(-b+sqrt(d))/(2*a);

x2:=(-b-sqrt(d))/(2*a);

writeln('Уравнение имеет 2 корня:');

writeln(' X1=',x1:7:3);

writeln(' X2=',x2:7:3);

end;

end;

readln;

end.

Оператор CASE

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

Оператор case состоит из выражения (селектора) и списка операторов, каждому из которых предшествует одна или более констант (они называются константами выбора) или ключевое слово else.

Оператор_case::= "case" выражение_селектор "of"

выбор {";"выбор}

["else" оператор [";"]]

"end"

выбор::=константа [".." константа]

{"," константа [".." константа]} ":" оператор

Селектор должен иметь порядковый тип размера байт или слово (о порядковых типах будет рассказано в параграфе 1.7.1.1, а сейчас момент нам достаточно знать, что тип Integer подходит под данное правило). Все константы выбора должны быть уникальными и иметь порядковый тип, совместимый с типом селектора.

Оператор case приводит к выполнению оператора, которому предшествует константа выбора, равная значению селектора или диапазону выбора, в котором находится значение селектора.

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

Например: По введенному часу определить время суток.

Var hour:integer;

Begin

Readln(hour);

Case hour of

0,24:writeln('Полночь');

12: writeln('Полдень');

1..3: writeln(Ночь');

4..10: writeln('Утро');

11,13..16: writeln('День');

17..11: writeln('Вечер')

else writeln('Ошибка при вводе числа');

end;

readln;

end.

Задачи для раздела "Условные операторы"

Задание 27. Даны три целых числа. Возвести в куб те из них, значения которых кратны 3.

Задание 28. Дано целое число N. Если NÎ[-10,-5], то увеличить его в 2 раза. Получить его модуль, если NÎ[-4,-1]. Присвоить ему единицу, если NÎ[0,15] и обнулить N в любом другом случае.

Задание 29[1, № 47]. Даны три числа a, b и с. Выяснить, существует ли треугольник со сторонами a, b и с, и если существует, то определить, является ли он остроугольным.

Задание 30. Даны три числа. Вывести их на экран в порядке возрастания.

Задание 31[1, № 41]. Даны три действительных числа. Вывести на экран только те из них, которые принадлежат промежутку (1,3).

Задание 32[1, № 42]. Даны действительные числа x, y (x<>y). Меньшее из этих двух чисел заменить их полусуммой, а большее - их удвоенным произведением.

Задание 33. Даны три целых числа a, b и с. Определить, лежит ли a в промежутке [b,c] или b в промежутке [a,c] или c в промежутке [a,b].

Задание 34[1, № 53]. Даны действительные числа X1, Y1, X2, Y2, X3, Y3. Определить, принадлежит ли начало координат треугольнику со сторонами (X1, Y1), (X2, Y2), (X3, Y3). Для решения задачи можете воспользоваться следующими фактами: Две точки (a,b) и (c,d), не лежащие на прямой, уравнение которой sx+ty+u=0, принадлежат одной полуплоскости, если sa+tb+u и sc+td+u - числа одного знака. Уравнение прямой, проходящей через 2 точки с координатами (e,f) и (g,h) выглядит следующим образом: (x-e)(h-f)-(y-f)(g-e)=0.

1.6.2.2   Операторы цикла

Операторы цикла используются для повторения (итераций) одного или нескольких операторов определенное количество раз.

Оператор_цикла::= оператор_for | оператор_while | оператор_repeat

Цикл For

Цикл For используется для повторения оператора фиксированное количество раз.

оператор_for::=

"for" управляющая_переменная ":=" начальное_значение

("to" | "downto") конечное_значение "do" оператор.

Где управляющая переменная - идентификатор порядковой переменной, начальное и конечное значения - выражения порядкового типа (см. параграф 1.7.1.1).

Управляющая переменная должна быть объявлена в том же блоке, где используется оператор цикла (например, если цикл используется внутри процедуры, управляющая переменная также должна быть определена внутри этой процедуры).

При входе в цикл начальное и конечное значения фиксируются и остаются постоянными во время выполнения цикла. Управляющая переменная инициализируется начальным значением. После окончания выполнения оператора в теле цикла производится сравнение управляющей переменной с конечным значением. Если они оказываются равны, то происходит выход из цикла, иначе – либо увеличение("to"), либо уменьшение ("downto") управляющей переменной на 1 и переход на новую итерацию цикла.

Если при входе в цикл начальное значение больше ("to"), либо меньше ("downto") конечного значения, то происходит немедленный выход из цикла без выполнения его тела.

Примеры использования цикла FOR:

Задача 8. Вывести на экран первые N натуральных чисел.

Решение:

var n,i:integer;

begin

readln(n);

for i:=1 to n do writeln(i);

readln;

end.

Задача 9. Вывести на экран первые N положительных четных чисел в обратном порядке.

Решение:

var n,i:integer;

begin

readln(n);

for i:=n downto 1 do writeln(i*2);

readln;

end.

Задача 10. Подсчитать сумму и произведение последовательности натуральных чисел из промежутка [N,M].

Рассмотрим подробно решение задачи :

var   n,m,i:integer;

  s,p:real;

begin

readln(n,m);

s:=0; p:=1; {присваиваем первоначальное значение переменным,

 отвечающим за сумму и произведение }

for i:=n to m do begin { открываем цикл для перебора всех

натуральных значений от N до М }

 s:=s+i; p:=p*i; {увеличиваем текущее значение суммы на I,

а текущее значение произведения в I раз}

end; { Завершаем тело цикла. При этом значение переменной I

увеличивается на единицу и сравнивается с М. Если I>M,

то происходит выход из цикла, иначе выполнение

программы переходит на начало тела цикла }

writeln('Сумма = ',s);

writeln('Произведение = ',p);

readln;

end.

Задача 11. Вычислить сумму из N слагаемых:

Решение:

Для решения задачи целесообразно вывести общую формулу:

var   i,n:integer;

  s:real;

begin

readln(n);

s:=0;

for i:=1 to n do s:=s+1/i;

writeln(s);

readln;

end.

Задача 12. Вывести на экран таблицу умножения.

Решение:

Для решения данной задачи нам потребуется воспользоваться понятием "вложенного цикла", которое подразумевает использование одного цикла внутри другого.

var   i,k:integer;

begin

for i:=1 to 10 do begin {открываем внешний цикл, с помощью которого осуществляем перебор всех десяти строк}

 for k:=1 to 10 do write(i*k:4); {во внутреннем цикле для каждой строки осуществляем перебор всех столбцов и выводим на экран произведение номера столбца и строки. Для выравнивания используем специальный синтаксис оператора write (см. описание вывода числовых выражений в параграфе 1.1)}

 writeln; {после завершения внутреннего цикла производим переход на следующую строку }

end;

readln;

end.

При использовании цикла FOR следует придерживаться следующих правил:

1) Не присваивать какое-либо значение управляющей переменной внутри цикла. В результате такого действия могут возникнуть следующие ситуации:

а) Незапланированный выход из цикла (что гораздо проще организовать с помощью процедуры break)

б) "Зацикливание" - бесконечное выполнение тела цикла, и как результат - "зависание" программы.

в) Неверное количество итераций цикла.

Ниже приводится пример, иллюстрирующий типичную ошибку, связанную с непониманием функционирования цикла FOR.

Задача 13. Вывести на экран первые N нечетных чисел.

Решение:

Var i,n:intеger;

Begin

 Readln(n);

 For i:=1 to n do begin

  Writeln(i);

  i:=i+2; {НЕВЕРНО! Нельзя рассчитывать получить из единицы тройку, из тройки - пятерку и т.д. таким методом внутри цикла FOR. Управляющая переменная I сама увеличивается на единицу при каждой итерации, что учетом ее изменения в данном операторе даст приращение на 3}

 End;

 Readln;

End.

Результат работы этой программы зависит от введенного числа N. Если конечное значение не кратно 3, то произойдет "зависание", так как увеличивающееся значение управляющей переменной никогда не станет равно конечному значению (естественно, рассматривая идеальный случай, когда множество натуральных чисел бесконечно, что для типа integer конечно же не так. На самом деле, выполнение цикла закончится, сделав 2-3 круга по всему типу integer, что займет значительный объем времени и бессмысленных вычислений). Если введенное значение окажется кратным 3, то выход из цикла произойдет вовремя, оставив на экране, к сожалению, отнюдь не последовательность нечетных чисел.

С помощью элементарных арифметических действий легко получить более короткое и, что самое главное, верное решение задачи:

var n,i:integer;

begin

readln(n);

for i:=1 to n do writeln(i*2-1);

Readln;

end.

2) Не изменять начальных и конечных значений управляющей переменной, рассчитывая при этом на изменение количества итераций цикла (данное ограничение имеет смысл только при использовании переменных в качестве конечных или начальных значений).

Задача 14. Дано натуральное число N. Вывести на экран все натуральные числа из промежутка [1,N+X], где X - количество таких чисел из промежутка [1,N], которые кратны 3 и не кратны 5.

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

Var i,n:intеger;

Begin

 Readln(n);

 For i:=1 to n do begin

  Writeln(i);

  If (i mod 3=0)and(i mod 5<>0) then n:=n+1; {НЕВЕРНО!}

 End;

 Readln;

End.

Увеличение значения переменной N внутри цикла не каким образом не повлияет на количество итераций.

Для решения данной задачи только с помощью цикла FOR нам потребуется несколько увеличить объем программы:

Var i,n:intеger;

Begin

 Readln(n);

 For i:=1 to n do begin

  If (i mod 3=0)and(i mod 5<>0) then n:=n+1;

 End; {воспользовавшись спецификой цикла FOR, нашли требуемую верхнюю границу промежутка, не вводя при этом дополнительных переменных}

for i:=1 to n do writeln(i); {вывели на экран все натуральные числа из определенного в условии промежутка}

 Readln;

End.

Цикл While

Цикл While или, как его еще называют, цикл с предусловием, используется для условного повторения оператора, который называют телом цикла.

оператор_While::=

"while" логическое_выражение "do" оператор.

При входе в цикл проверяется логическое выражение. Пока его значение равно True, оператора тела цикла повторяются.

Задача 15. Вычислить бесконечную сумму с точностью до одной сотой:

Решение:

Для решения задачи нет возможности воспользоваться циклом for, так как заранее неизвестно количество членов последовательности, сумма которых вычисляется. Поэтому используем в программе цикл while:

var   i,n:integer;

  s,p:real;

Const Tochnost=0.01;

begin

s:=0; i:=1;

p:=1/(i*i*i); {Переменной p требуется присвоить первоначальное значение перед входом в цикл. Иначе при первом входе логическое выражение может быть рассчитано неверно }

while p>= Tochnost do begin

 s:=s+p;

 i:=i+1;

 p:= 1/(i*i*i);

end;

writeln(s);

readln;

end.

Задача 16. Попробуем увеличить скорость получения последовательности натуральных чисел при решении задачи №14. Напоминаем ее условие:

Дано натуральное число N. Вывести на экран все натуральные числа из промежутка [1,N+X], где X - количество таких чисел из промежутка [1,N], которые кратны 3 и не кратны 5.

Как было показано выше, используя цикл FOR, мы не можем обойтись только одним циклом, так как во время выполнения его тела ТП не допускает изменения конечного количества итераций. Цикл WHILE более либерален в этом отношении.

Var i,n:integer;

Begin

 Readln(n);

 i:=1;

 While i<=n do begin

  If (i mod 3=0)and(i mod 5<>0) then n:=n+1;

  Writeln(i);

  i:=i+1;

 End;

 Readln;

End;

Цикл Repeat

Цикл Repeat или, как его еще называют, цикл с постусловием, также, как и цикл while, используется для условного выполнения операторов, находящихся в теле цикла. Его отличительной особенностью является то, что проверка условия выхода из цикла происходит после выполнения тела цикла.

оператор_Repeat::=

"repeat" оператор {";" оператор } [";"]

"until" логическое_выражение.

Повторение оператора заканчивается (то есть происходит выход из цикла), когда логическое выражение принимает значение True.

Задача 17. Рассмотрим задачу №15, приведенную для иллюстрации возможностей цикла while. Напомним ее условие: Вычислить бесконечную сумму с точностью до одной сотой:

var   i,n:integer;

  s,p:real;

Const Tochnost=0.01;

begin

s:=0; i:=1;

{ в отличие от цикла while первоначальное

значение промежуточного произведения

не требуется рассчитывать до тела цикла,

так как проверка условия производится не

в начале, а в конце цикла.}

repeat

 p:= 1/(i*i*i);

 s:=s+p;

 i:=i+1;

until p<Tochnost;

{ Выход из цикла произошел, когда в искомой сумме уже было учтено последнее слагаемое, значение которого меньше, чем требуемая погрешность. }

writeln(s-p); { Поэтому исключаем данный член последовательности из суммы}

readln;

end.

Задача 18. Решая задачу №10 при демонстрации возможностей цикла for (Подсчитать сумму и произведение последовательности натуральных чисел из промежутка [N,M]), мы рассчитывали, что, как сказано в условии задачи, введенные значения N и M будут положительны, так и что N<M. Однако при вводе значений во время выполнения программы могут быть введены как отрицательные числа и 0, так и N>M. Поэтому в самом начале программы требуется поставить фильтр ввода данных, что очень удобно сделать с помощью цикла Repeat.

...

repeat

 write('Введите натуральные числа N и М, причем N<M');

 readln(n,m);

until (n>0)and(m>0)and(n<m);

...

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

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

Вложенные циклы

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

Например:

...

Цикл
первого
уровня
вложенности

 

Цикл
второго
уровня
вложенности

 

Цикл
третьего
уровня
вложенности

 
for i:=1 to d*2 do begin

  ...

 for k:= i to i+5 do begin

  ...

  repeat

   ...

   while x<5 do begin

    ...

   end;

   ...

  until z<>x;

   ...

 end;

  ...

end;

ВНЕШНИЙ ЦИКЛ

 
...


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

Задача 19[1, № 330]. Дано натуральное число N. Найти все совершенные числа, меньшие чем N. Натуральное число называется совершенным, если оно равно сумме всех своих делителей, за исключением самого себя (число 6 - совершенное, так как 6=1+2+3; число 8 - не совершенное, так как 8<>1+2+4).

var n,k,i,s:integer;

begin

 readln(n);

 for k:=2 to n do begin {перебираем все натуральные числа от 2 до N}

  s:=1; {первоначальное значение искомой суммы делителей = 1, так как 1 - делитель любого натурального числа}

  for i:= 2 to k div 2 do begin {для каждого анализируемого числа K открываем цикл, перебирающий первую половину меньших его натуральных чисел, чтобы найти и суммировать все его делители (вторую половину перебирать не имеет смысла, так как делители K там отсутствуют) }

if k mod i = 0 then s:=s+i; {суммируем делители}

  end;

  if s = k then writeln(k); {если число совершенное, выводим его на экран}

 end;

end.

При запуске этой программы и вводе, например 10000, мы получим 4 совершенных числа:

6

28

496

8128

При работе с вложенными циклами следует иметь в виду, что наиболее ресурсоемким для ЭВМ является цикл с максимальным уровнем вложенности. Действительно, если, допустим, внешний цикл имеет 10 итераций, цикл первого уровня - 15, а цикл второго уровня - 4, то общее количество итераций тела цикла первого уровня будет 10*15=150, а второго - 10*15*4=600. Поэтому, для повышения эффективности выполнения программы следует максимально "облегчать" содержимое внутренних циклов, выносить всевозможные расчеты за их пределы.

Изменение скорости выполнения программы в зависимости от сложности внешнего и внутреннего циклов (для простоты рассмотрим только первый уровень вложенности) можно проанализировать на следующем примере:

Задача 20. Вычислить

Решение:

var k,j:integer; s,sin1,sink,cos1:real;

 begin

  s:=0;

  for k:=1 to 300 do begin

   for j:=1 to 300 do begin

    sin1:=sin(1); cos1:=cos(1); sinK:=sin(k);

    s:=s+(sink+sin1)/(cos(j)+cos1);

   end;

  end;

  writeln(s);

  readln;

 end.

Время выполнения данной программы на компьютере IBM PC с процессором Pentium-120 составляет примерно 10 секунд.

Попытаемся несколько ускорить программу, вынося за пределы циклов подсчет синуса и косинуса единицы.

var k,j:integer; s,sin1,sink,cos1:real;

 begin

  s:=0;

  sin1:=sin(1); cos1:=cos(1);

  for k:=1 to 300 do begin

   for j:=1 to 300 do begin

    sinK:=sin(k);

    s:=s+(sink+sin1)/(cos(j)+cos1);

   end;

  end;

  writeln(s);

  readln;

 end.

Время выполнения программы составит примерно 6 секунд.

Покажем теперь, что при "облегчении" внутреннего цикла скорость выполнения программы возрастет значительнее, чем при эквивалентном (и даже в несколько раз большем) "облегчении" внешнего.

Для этого во внешний цикл мы внесем извне вычисления синуса и косинуса единицы, а из внутреннего вынесем туда же в два раза более простой расчет синуса K.

 var k,j:integer; s,sin1,sink,cos1:real;

 begin

  s:=0;

  for k:=1 to 300 do begin

   sin1:=sin(1); cos1:=cos(1);

   sinK:=sin(k);

   for j:=1 to 300 do begin

    s:=s+(sink+sin1)/(cos(j)+cos1);

   end;

  end;

  writeln(s);

  readln;

 end.

В результате такого эксперимента скорость выполнения программы возрастет до 4 секунд.

Выбор вида цикла при решении задач

Для выбора вида цикла при решения той или иной задачи следует руководствоваться следующими правилами:

1) Если количество итераций (повторений) цикла заранее известно, выбирайте цикл FOR.

2) Если требуется, чтобы цикл сработал обязательно хотя бы один раз или проверка условия должна идти после выполнения тела цикла, выбирайте цикл REPEAT.

3) В остальных случаях выбирайте цикл WHILE.

Процедуры break и continue

Для операторов цикла существует две процедуры - break и continue. Процедура break выполняет досрочный выход из цикла. Процедура continue производит переход на следующую итерацию цикла.

В ТП версии 7.0 процедуры break и continue во многих случаях заменяют оператор goto, который в раннее часто использовали для тех же целей.

Задача 21.Определить, является ли число N простым (простым называется такое натуральное число, которое делится без остатка только на себя и на единицу)

Задачу можно решить, используя определение, то есть подсчитывая количество чисел, на которые делится N без остатка:

Var i,n,s:integer;

Begin

 Readln(n);

 S:=0;

 For i:=2 to n div 2 do begin

  If n mod i=0 then s:=s+1;

 End;

 If s=0 then writeln('Число простое')

 else writeln('Число составное');

end.

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

Var i,n:integer;

  Simply:boolean;

{ значение переменной Simply по завершении алгоритма равно TRUE, если введенной число является простым и FALSE в противном случае }

Begin

 Readln(n);

 Simply:=true;

 For i:=2 to n div 2 do begin

  If n mod i=0 then begin

   Simply:=false; Break;

   {определили, что число N - составное, досрочно выходим из цикла}

  End;

 End;

 If simply then writeln('Число простое')

 else writeln('Число составное');

end.

В общем случае время работы программы значительно уменьшается по сравнению с первым методом решения, так как для определения, является ли N составным, достаточно найти хотя бы одно натуральное число, на которое N делится без остатка.

Задача 22. Найти первые N простых чисел.

Расширяем решение предыдущей задачи.

Var i,n:integer;

  Simply:boolean;

  Num:integer; { проверяемое число }

Begin

 Readln(n);

 Num:=0;

 repeat

  Num:=Num+1; {начинаем проверку с увеличения Num.

При первом входе в цикл значение

Переменной Num станет равно единице}

  Simply:=true;

  For i:=2 to Num div 2 do begin

   If Num mod i=0 then begin

    Simply:=false; Break;

   End;

  End;

  If not simply then continue; {если анализируемое число не является

простым, то переходим к следующему числу, на новую итерацию цикла (к строке
"Until n=0;")}

  Writeln(Num); {число оказалось простым, напечатали его ...}

  N:=N-1; { ... и уменьшили значение счетчика}

Until n=0;

Readln;

end.

Задачи для раздела "Операторы цикла"

Задание 35. Дано натуральное число N.

Вычислить sin(0.1)+sin(0.2)+sin(0.3)...+sin n

Задание 36. Найти произведение цифр натурального числа.


Задание 37[1, 77]. Дано натуральное N. Вычислить

Задание 38[1, 78]. Дано действительное число a, натуральное n. Вычислить: a(a+1)...(a+n-1)

Задание 39[1, 89]. Даны натуральные числа N и M. Используя алгоритм Евклида, найти наибольший общий делитель и наименьшее общее кратное N и M. алгоритм Евклида основан на следующих свойствах неотрицательных целых чисел: Пусть M>=N. Если N=0, то НОД(M,N)=M, иначе НОД(M,N)= НОД(N,R), где R - остаток от деления M на N. Например, НОД(15,6)= НОД(6,3)=НОД(3,0)=3.

Задание 40[1, № 322]. Найти натуральное число от 1 до 10000 с максимальной суммой делителей.

Задание 41[1, № 329]. Даны натуральные числа N и M. Получить все меньшие N натуральные числа, квадрат суммы цифр которых равен M.

Задание 42[1, № 332]. Дано натуральное N. Указать такие неотрицательные целые X, Y, Z и T, что N=X2+Y2+Z2+T2.


 

1.7  Типы

Каждая переменная в ТП имеет тип. От типа зависит допустимые операции над переменной и размер памяти, ею занимаемый.

Тип::= ИД_типа | простой_тип | структурный_тип | тип_указателя | процедурный_тип.

ТП позволяет программисту определять свой собственный тип, основанный на встроенных или ранее определенных типах. Для этого используется раздел объявления типов в разделе объявлений, как было показано в параграфе 1.3.3.

Раздел_объявления_типов:=

"TYPE" 1{ объявление_типа }.

Объявление_типа::= ИД_типа "=" тип ";".

После объявления идентификатора типа его можно использовать в программе как любой встроенный тип.

Типы в программе применяются при:

1.     Объявлении переменных

2.     Объявлении типированных констант

3.     Объявлении формальных параметров процедур и функций

4.     Приведении типов (т.е. преобразовании одного типа в другой).

1.7.1        Простой тип

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

Простой_тип::=порядковый_тип | вещественный_тип

1.7.1.1   Порядковый тип

Таблица порядковых типов

Встроенный порядковый тип

Встроенный порядковый тип определяет все порядковые типы, свойства которых (диапазон значений, определение и т.д.) четко зафиксированы и не могут быть изменены программистом.

Целочисленный тип

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

Таблица свойств целочисленных типов данных:

Целочисленный тип

Диапазон возможных значений

Объем занимаемой памяти

Целочисленные типы

Shortint

-128 .. 127

1 байт

Integer

-32768 .. 32768

2 байта

Longint

-2147483648 .. 2147483647

4 байта

Неотрицательные типы

Byte

0 .. 255

1 байт

Word

0 .. 65535

2 байта

С помощью функции ODD можно определить, является ли целочисленное значение четным или нет. Функция возвращает True, если значение, переданное ей, нечетно.

function Odd(X: Longint): Boolean;.

Например:

Begin

 Writeln(odd(10));

 Writeln(odd(15));

End.

В результате работы этой программы на экране появится:

FALSE

TRUE

Логический тип

Логический тип определяется зарезервированным словом boolean и имеет только 2 значения - TRUE(истина) и FALSE(ложь). Переменные логического типа - основа булевой алгебры, и все законы преобразований на основе логических операций (см. приложение и параграф 1.4.3.2) верны и в Турбо-Паскале. С их помощью можно легко, например, заменить цикл с предусловием на цикл с постусловием (если конечно предусловие не является определяющей причиной использования цикла While) и наоборот, сократить логическое выражение и т.д.

Например:

1) Используя правило де Моргана (см. приложение), цикл

while not(d>5)or not (x<d) do …;

можно заменить на цикл

repeat … until (d>5)and (x<d);

2) Значение логического выражения (x>5)or(x=5) and (not (x>=5)) равно false вне зависимости от значения x.

Переменным логического типа можно присваивать произвольные логические выражения.

Например:

var a, b: boolean;

x, y: integer;

 begin

 

  a:=(x>7)or not(y<x) and (y<>0);

  b:=a or (x=y);

  a:=b and a;

 

 end.

При переводе логического значения в целочисленное, для false мы получим число 0, для true - 1. При обратном переводе значение 0 преобразуется в false, любое другое значение - в true. Для значений логического типа всегда верно условие: TRUE>FALSE.

Например: процедура writeln(true<=false) выведет на экран слово FALSE.

Символьный тип

Любая переменная символьного типа может содержать в себе один из 256 символов набора ASCII. Объем памяти, занимаемый переменными этого типа, составляет 1 байт. Правила построения констант символьного типа соответствуют правилу построения строковых констант (см. параграф 1.3.2.7) с одним символом. Все операции, допустимые для строк, могут быть использованы и для типа Char.

Специально для символьного типа существует функция Chr, которая осуществляет перевод однобайтового значения в тип Char.

function Chr(X: Byte): Char;

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

Пример:

 Var a:byte;

begin

 repeat

  readln(A);

  writeln(chr(A));

 until a=0;

end.

С помощью этой программы можно определить символ по его коду. Однако следует учесть, что многие символы обладают специальными функциями и не имеют поэтому визуального отображения. Например, попытка вывести на экран символ с кодом 7 закончится гудком встроенного динамика компьютера, символ с кодом 13 означает переход на следующую строку, а с кодом 10 - в начало строки и т.д.

С помощью функции Ord можно создать программу, которая будет выводить на экран код введенного символа. При этом вместо процедуры Readln удобно воспользоваться функцией ReadKey (см. параграф 1.5.2).

Uses crt;  {подключение модуля, который содержит

                     функцию readkey}

 Var c:char;

begin

 repeat

  c:=readkey;

  writeln(ord(c));

 until c=#27;

end.

Выполнение программы закончится, когда пользователь нажмет на клавишу ESC (код 27). Запустив и немного поэкспериментировав с программой, можно заметить, что некоторые клавиши (например - клавиши курсора) имеют двухбайтовый код. Этот нюанс следует учитывать, разрабатывая программы, которые основаны на анализе вводимой с клавиатуры информации.

Перечислимый тип

Перечислимый тип и тип поддиапазона, в отличии от встроенных порядковых типов, не задаются каким либо зарезервированным словом. Диапазон значений этих типов и размер памяти, выделяемый для них, изменяется в зависимости от конкретного определения.

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

Перечислимый_тип::= "(" ИД {"," ИД} ")"

Например: тип week (неделя) может быть описан с помощью семи значений: monday, tuesday, wednesday, thursday, friday, saturday, sunday.

Определяем тип Week:

type Week=(monday, tuesday, wednesday, thursday, friday, saturday, sunday);

Идентификаторы, с помощью которых определяется перечислимый тип, понимаются как константы внутри блока, в котором они определены.

Согласно этому правилу monday является константой типа Week.

Размер памяти, отводимый для перечислимого типа, зависит от количества констант, определяющих данный тип, и может быть 1, 2 или (что совершенно невероятно!) 4 байта.

Создавая, например, на ТП компилятор или интерпретатор какого-либо алгоритмического языка, часто определяют перечислимый тип, в качестве имен констант которого служат аббревиатуры зарезервированных слов языка. Естественно, что таких слов может быть более 256, и тогда под переменную данного типа будет выделяться 2 байта оперативной памяти. Однако ситуация, когда количество констант начнет превышать число 65536 (то есть под переменную будет отведено не 2, а 4 байта) практически невозможна.

Тип поддиапазона

Тип поддиапазона используется, когда программисту для решения задачи требуется не весь диапазон значений какого-либо порядкового типа, а только его часть. Таким образом, тип поддиапазона определяется как диапазон значений некоторого порядкового типа (главного типа), причем определение поддиапазона включает наименьшее и наибольшее значения из этого типа.

Тип_поддиапазона::=константа .. константа.

Например:

 0 .. 100

-139 .. 34

0 .. 24

Tuesday .. Saturday

Объем памяти, выделяемый для типа поддиапазона может быть 1, 2 или 4 байта в зависимости размера диапазона.

Чаще всего тип поддиапазона применяется при объявлении массивов (см. параграф 1.7.2.1).

Свойства порядковых типов

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

Получение порядкового номера

function Ord(X): Longint;

Значение порядкового типа характеризуется порядковым номером. Первое значение любого порядкового типа, кроме целочисленных, имеет порядковый номер 0, следующее - 1, и т.д. Порядковый номер значения целочисленного типа равен самому этому значению. Функция Ord осуществляет перевод значения любого порядкового типа в его порядковый номер.

Получение предыдущего и последующего значений
порядкового типа

function Pred(X: порядковый_тип): тот_же_тип;

Функция Pred возвращает предыдущее значение аргумента. Если X - первое значение порядкового типа (нижняя граница), то выдается сообщение об ошибке.

function Succ(X: порядковый_тип): тот_же_тип;

Функция Pred возвращает последующее значение аргумента. Если X - последнее значение порядкового типа (верхняя граница), то выдается сообщение об ошибке.

Увеличение и уменьшение значений переменных
порядкового типа

procedure Inc(var X [ ; N: Longint ]);

Процедура Inc увеличивает переданное ей значение X на N. Если N отсутствует, то X увеличивается на 1 (т.о. Inc(x,n) эквивалентно x:=x+n, а Inc(x) эквивалентно x:=x+1).

procedure Dec(var X [ ; N: Longint ]);

Процедура Dec уменьшает переданное ей значение X на N. Если N отсутствует, то X уменьшается на 1 (т.о. Dec(x,n) эквивалентно x:=x-n, а Dec(x) эквивалентно x:=x-1).

 

Пример использования функции Ord:

1) begin Writeln(ord(true),' ',ord(false)); End.

В результате выполнения программы на экране появятся числа 1 и 0.

2) При рассмотрении перечислимого типа мы определили тип Week:

type Week=(monday, tuesday, wednesday, thursday, friday, saturday, sunday);

Функция ord(monday) возвратит 0, ord(tuesday) - 1 и т.д.

 

Пример использования функций Pred и Succ:

При выполнении программы:

var a:boolean;

begin

 a:=false;

 writeln(pred(7));

 writeln(Succ(a));

end.

на экране появятся число 6 и слово TRUE.

 

Примеры использования процедур Inc и Dec:

Задача 23. Определить сумму цифр натурального числа.

var N,i,s:integer;

begin

 readln(n);{вводим натуральное число}

 s:=0; {первоначальное значение суммы}

 while n<>0 do begin {пока число не обратилось в 0}

  i:=n mod 10; {находим последнюю цифру числа}

  inc(s,i);{увеличиваем сумму на эту цифру}

  n:=n div 10; {уменьшаем число в 10 раз, чтобы добраться до следующей цифры}

 end;

 writeln(s);

 readln;

end.

Задача 24. Дано натуральное число N. Вывести на экран убывающую последовательность чисел от N до X, где X - последнее натуральное число в промежутке [1,N], кратное 11. Если такого числа не существует, вывод последовательности завершить на единице.

Решение:

var N:word;

begin

 write('Введите значение N>0: '); readln(n);

 inc(n); {увеличим N для корректировки первого входа в цикл}

 repeat

  dec(n); {уменьшаем N на 1 …}

  writeln(n); { … и выводим его на экран}

 until (n mod 11 = 0)or (n=1); {выходим из цикла, если N кратно 11 или равно 1}

 readln;

end.

1.7.1.2   Вещественный тип

Множество действительных чисел в ТП представлено вещественными типами. За одним исключением, все они обладают определенной погрешностью при проведении арифметических операций.

Таблица свойств вещественных типов данных:

Вещественный тип

Диапазон возможных значений

Значащие цифры

Размер в байтах

Real

11-12

6

Single

7-8

4

Double

15-16

8

Extended

19-20

10

Comp

19-20

8

Для типов Real, Single, Double и Extended в диапазон значений включается и отрицательная область.

Если компьютер не имеет встроенного математического сопроцессора, то удобнее всего использовать тип Real, так как разработчики ТП специально для него оптимизировали по скорости все операции. Остальные 4 типа являются специфичными для математического сопроцессора и должны использоваться с директивой компилятора {$N+}, которая указывает на его присутствии в системе, где будет исполняться программа. При отсутствии сопроцессора, с помощью директивы {$N+,E+} можно произвести его эмуляцию и применять в программе все вещественные типы.

Директива {$N+} замедляет скорость вычислений с типом Real, который является специфическим для ТП и не поддерживается сопроцессором. Таким образом, арифметические операции с типом Real предваряются переводом Real в Extended и завершаются обратным переводом.

При эмуляции математического сопроцессора по аналогичной причине замедляется обработка всех вещественных типов, кроме Real.

Тип Comp на самом деле не является вещественным. Это целочисленный тип с очень большим диапазоном значений (примерно от -1018 до 1018). Его удобно использовать в бухгалтерских расчетах, когда производятся операции над гигантскими суммами и требуется вести учет до последней копейки. Математический сопроцессор обрабатывает тип Comp методами, аналогичными для других вещественных типов.

Следующий пример отвечает на вопрос о точности вычислений для различных вещественных типов:

Задача 25. Вычислить выражение  двумя способами:

1) последовательно слева направо

2) последовательно слева направо вычисляются , затем , после чего второе значение вычитается из первого.

Определить, насколько будут отличаться результаты, полученные при первом и втором методах расчета.

Решение:

var   s,s1,s2,s3:real;

i,sign:integer;

begin

  {----- ПЕРВЫЙ способ -----}

 s:=0;

 sign:=1; {значение, с помощью которого меняется знак перед слагаемым}

 for i:=1 to 100 do begin

  s:=s+sign/i; {вычисляем сумму}

  sign:=-sign; {меняем знак}

 end;

  {----- ВТОРОЙ способ -----}

 s1:=0;

 for i:=1 to 50 do s1:=s1+1/(i*2-1);{вычисляем сумму с нечетным знаменателем}

 s2:=0;

 for i:=1 to 50 do s2:=s2+1/(i*2);{вычисляем сумму с четным знаменателем}

 s3:=s1-s2;{вычисляем разность между полученными значениями }

 writeln(s);

 writeln(s3);

 writeln(s-s3);

 readln;

end.

В результате выполнения программы на экране появятся 3 числа:

 6.8817217931E-01

 6.8817217931E-01

-9.0949470177E-12

Точность вычисления в данном случае составляет примерно 10-11.

Применим директиву компилятора {$N+, E+}, чтобы проанализировать работу программы при использовании математического сопроцессора. ($N - для подключения сопроцессора, $E - для эмуляции в случае его отсутствия):

{$N+, E+}

var   s,s1,s2,s3:real;

i,sign:integer;

begin

...

Результат:

 6.88172179303365E-0001

 6.88172179314279E-0001

-1.09139364212751E-0011

Последовательно заменяя тип Real в программе на типы Single, Double и Extended получим следующие значения:

Для типа Single:

 6.88171803951263E-0001

 6.88172578811646E-0001

-7.74860382080078E-0007

Для типа Double:

 6.88172179310196E-0001

 6.88172179310195E-0001

 3.33066907387547E-0016

Для типа Extended:

 6.88172179310195E-0001

 6.88172179310195E-0001

 6.50521303491303E-0019

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

Вещественный тип можно преобразовать в порядковый (а конкретно - в целочисленный) с помощью двух функций:

function Round(X: Real): Longint;

function Trunc(X: Real): Longint;

Функция Round производит округление переданного ей вещественного значения до ближайшего целого.

Например:

Begin

 Write(round(1.3),' ', round(2.7),' ', round(-7.3),' ', round(-15.5));

End.

В результате выполнения этой программы на экране появится строка, состоящая из 4 чисел: 1 3 -7 -16

Функция Trunc производит отбрасывание дробной части у переданного ей вещественного значения.

Например:

Begin

 Write(trunc(1.3),' ', trunc(2.7),' ', trunc(-7.3),' ', trunc(-15.5));

End.

В результате выполнения этой программы на экране появится строка, состоящая из 4 чисел: 1 2 -7 -15

Отвечая на вопрос, какие вещественные типы целесообразнее использовать в различных ситуациях, мы можем посоветовать следующее:

Для простых, с небольшим объемом вещественных расчетов используйте тип Real.

Если у Вас в программе выполняются сложные математические расчеты, при этом очень важна точность, и достаточно как оперативной памяти, так и времени, используйте тип Extended.

Если объем памяти ограничен или требуется обработка больших вещественных массивов, и при этом точность вычислений не играет особой роли, используйте тип Single.

В ситуации, промежуточной между первыми двумя, используйте тип Double.

Если Вы выполняете расчеты с очень большими целыми числами, используете тип Comp.

Если у Вас отсутствует математический сопроцессор, а скорость выполнения программы играет решающую роль, используйте тип Real.

Задачи для раздела "Простой тип"

Задание 43. Дано натуральное число N. Не используя арифметические операции и цикл For, вывести на экран все натуральные числа от 1 до N.

Задание 44. Вывести на экран всю таблицу символов, начиная с пробела.

Задание 45. "Поговори с компьютером". Создать программу, которая будет отвечать на нажатие клавиши выводом произвольного количества (до 80) произвольных символов (можете воспользоваться, например, тем, что коды заглавных латинских букв находятся в промежутке [65,90]). Выход из программы - по нажатию клавиши ESC.

Задание 46. Ежемесячный доход от торговли нефтью некой арабской страны составляет 250 миллионов условных единиц. 7.564325421 процентов от этой суммы она тратит на поддержку мирового терроризма, 3.712329872 процента - на подрывную деятельность, направленную против Израиля. 49.999999999 процентов помещается в самые надежные банки, а остальное идет на повседневные нужды правителя. Какие суммы по каждой статье расходов истратит великий шейх за 25 лет своего безупречного правления и сколько УЕ окажется на его счете в банках (без учета процентов)?

1.7.2        Структурные типы

Основное отличие структурных типов от простых заключается в том, что они состоят из нескольких компонент, которые, в некоторых случаях, также могут иметь структуру.

Структурный_тип::=[packed] (тип_массив | строковый_тип | тип_запись | множественный_тип | файловый_тип | объектный_тип)

Слово packed должно указывать компилятору, что данные этого типа следует уплотнять. Однако компилятор ТП сам производит все возможные уплотнения и не нуждается в подобном указании. Упакованные типы оставлены для совместимости со стандартным Паскалем.

1.7.2.1   Массивы

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

Тип_массив::=

"array" "[" тип_индекса {"," тип_индекса"} "]" "of" тип_компонент.

тип_индекса::= порядковый тип.

тип_компонент::=тип

Массивы содержат фиксированное количество компонент одного типа. На каждый тип индекса приходится по одной размерности массива.

Например:

Подпись: Индекс типа поддиапазона

 

 


type MyArray1=array[boolean,1..10] of integer;


 



Это тип двумерного массива с компонентами целого типа.

 Type MyArray2=array[boolean] of array[1..10] of integer;

Второй тип полностью идентичен первому.

Чаще всего для типа индекса используют тип поддиапазона, однако нет никаких ограничений на применение любого другого порядкового типа. Основная причина использования типа поддиапазона состоит в том, что объем занимаемой массивом памяти рассчитывается как произведение мощности индексов друг на друга и на размер памяти одного элемента массива. Мощность (диапазон значений) любого порядкового типа, кроме типа поддиапазона и перечислимого типа, фиксирована и не может быть изменена. Т.о., если весь диапазон значений не используется, происходит бессмысленный расход оперативной памяти компьютера.

Например:

Определение типа массива:

Type FirstArray=array[shortint] of byte;

по объему занимаемой памяти и обращению к элементам массива эквивалентно:

Type SecondArray=array[-128..127] of byte;

Для получения значения элемента массива требуется указать переменную массива со следующим за ней индексом.

Индекс::= "[" выражение {"," выражение"} "]"

Например:

type MyArray1=array[boolean,1..10] of integer;

var   A:MyArray1;

B:array[-1..3] of boolean;

begin

...

writeln(A[true,3], A[false,1], B[2])

end.

ТП имеет 3 предопределенных одномерных массива для прямого доступа к памяти: Mem - для доступа к байтам, MemW - для доступа к словам, MemL - для доступа к значениям типа longint. Индексация массивов памяти производится стандартным методом построения адреса памяти с помощью значений сегмента и смещения, разделенных двоеточием (см. параграф 1.7.3).

Например:

var a:longint absolute $40:$6C; {абсолютная переменная, которая соответствует области оперативной памяти с адресом $40:$6C (дневной счетчик времени)}

b: longint;

begin

 b:=a; {присваиваем переменной b текущее время}

 

 b:=memL[$40:$6C]; {делаем то же самое с помощью прямого обращения к памяти}

end.

Для прямого доступа к портам ввода-вывода ТП имеет 2 предопределенных одномерных массива: Port - для доступа к байтам, PortW - для доступа к словам.

Заполнение и обработка массивов

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

Задача 26. Дано натуральное число N. Получить N случайных чисел и вывести их на экран сначала в прямом, а потом - в обратном порядке.

var a:array[1..100] of word; {т.к. ТП не позволяет определять массивы переменной длины, мы рассчитываем, что количество получаемых чисел будет не более 100}

    i,N:integer;

 begin

  readln(n);

  randomize; {инициализируем счетчик случайных чисел}

  for i:=1 to N do begin {заполняем и одновременно выводим массив на экран}

   a[i]:=random(23);

   write(a[i]:3);

  end;

  writeln;

  for i:=N downto 1 do begin {выводим на экран массив в обратном порядке}

   write(a[i]:3);

  end;

  writeln;

  readln;

 end.

Задача 27[1, № 377]. Дана натуральная квадратная матрица M*M. Заменить нулями все ее элементы, лежащие на главной диагонали и выше ее.

var i,j,m:integer;

     a:array[1..10,1..10]of integer; {двумерный массив для хранения матрицы}

 begin

  repeat {простейший фильтр ввода}

   readln(m);

  until (m>0)and(m<=10);

{с помощью двух вложенных циклов заполняем матрицу случайными числами и выводим ее на экран}

  randomize;

  for i:=1 to m do begin

   for j:=1 to m do begin

    a[i,j]:=random(100);

    write(a[i,j]:3);

   end;

   writeln; {производим перевод строки для разделения строк матрицы}

  end;

  writeln; {производим перевод строки для разделения исходной и результирующей матриц}

{с помощью двух вложенных циклов преобразуем матрицу в соответствии с условием задачи и выводим ее на экран}

  for i:=1 to m do begin

   for j:=1 to m do begin

    if i<=j then a[i,j]:=0; {если номер строки меньше или равен номеру столбца (условие для главной диагонали и всех вышележащих ее элементов), то обнуляем данный элемент массива}

    write(a[i,j]:3);

   end;

   writeln;

  end;

  readln

 end.

Если в результате запроса после запуска программы ввести число 5, то на экране можем получить примерно следующее:

   0   3 86 20 27

 67 31 16 37 42

   8 47   7 84   5

 29 91 36 77 32

 69 84 71 30 16

 

   0   0   0   0   0

 67   0   0   0   0

   8 47   0   0   0

 29 91 36   0   0

 69 84 71 30   0

Благодаря применению процедуры randomize следующий запуск программы построит новую последовательность случайных чисел.

Задача 28. Найти максимальный элемент одномерного массива.

var  a:array[1..10] of byte;

i,max:byte;

begin

 randomize;

 for i:= 1 to 10 do begin {заполняем и выводим на экран массив}

  a[i]:=random(100); write(a[i]:4)

 end;

 writeln;

 max:=a[1]; {предполагаем, что максимальным является первый элемент массива }

 for i:=2 to 10 do {перебираем все остальные элементы массива}

if max<a[i] then max:=a[i]; {если предположение оказалось неверным, то будем считать, что максимальным является текущий элемент массива}

 writeln(max);{выводим на экран максимальный элемент}

 readln;

end.

Определение содержимого массива в разделе типированных констант

Объявление константы с типом массив содержит значения элементов массива, разделенных запятыми и заключенными в скобки.

Константа-массив::="(" типированная_константа

{"," типированная_константа} ")".

Например:

Const  A: array[1..3] of integer=(-4,5,21);

B: array[1..3,1..2] of char=( ('A','f'),

('1','2'),

('x','y'));

Задачи для раздела "Массивы"

Задание 47. Найти индекс и значение последнего минимального элемента одномерного массива длиной N.

Задание 48. Заполнить массив A длиной N и преобразовать его следующим образом: увеличить каждый элемент массива на max(A1..AN).

Задание 49. Заменить нулями все минимальные элементы одномерного массива длиной N.

Задание 50. Найти сумму, произведение и среднее арифметическое элементов одномерного массива длиной N.

Задание 51. Найти максимальный и минимальный элементы двумерного массива размером M на N.

Задание 52. Найти максимальные элементы строк двумерного массива размером M на N.

Задание 53. Перенести содержимое двумерного массива в одномерный.

Задание 54. Отразить двумерный массив размером M на M относительно побочной диагонали (левый верхний угол становится правым нижним, а правый нижний - левым верхним).

1.7.2.2   Строковый тип

Строковые типы используются для определения произвольных наборов символов. Строку можно определить как одномерный массив с переменными границами, каждый элемент которого является символом типа Char.

Строковый_тип::= Стандартный_строковый_тип |

ASCIIZ_тип

Изначально в Паскале, а затем и в ТП существовал только один строковый тип, максимальная длина которого не могла превышать 255 байт. Одной из особенностей данного типа было то, что первый байт строки означал ее актуальную длину (отсюда и ограничение на 255 символов). При этом во многих других языках, и прежде всего в языке Си для организации строк используется совершенно другая структура, основанная на последовательности символов, заканчивающейся нулем. Длина такой последовательности может быть до 64 килобайт, что вполне достаточно для хранения небольших текстов. Поэтому для совместимости с другими языками в седьмую версию ТП был введен ASCIIZ тип (ASCII - американский стандарт набора символов, Z (zero-нуль) - заканчивающегося нулем).

Справедливости ради следует сказать, что несмотря на широкое распространение в программировании ASCIIZ строк, манипулировать стандартными паскалевскими строками значительно проще и надежнее. По видимому для этого фирма Inprise (ранее - Borland International inc.) ввела в Delphi понятие длинной строки (WideString), которая совмещает в себе все достоинства стандартных и ASCIIZ строк и простейшим преобразованием типов (а для стандартных строк даже и этого не требуется) может быть приведена к любому из них.

Стандартный строковый тип

Стандартный_строковый_тип ::=

"string" [ "[" "целое_без_знака" "]"]

Число в скобках означает максимальную длину строки. Если оно отсутствует, то максимальная длина строки =255 символам. Объем памяти, занимаемый строкой, равен ее максимальной длине, вне зависимости от актуальной длины, значение которой находится в промежутке от 0 до максимальной длины.

Аналогично элементу массива можно проиндексировать строковую переменную, причем значение индекса должно принадлежать промежутку от 0 до максимального размера строки. Таким образом можно получить доступ к каждому символу строки типа Char. Нулевой символ строки представляет собой после перевода в целочисленное выражение актуальную длину строки.

Задача 29. Ввести с клавиатуры строку и вывести ее задом наперед.

 var s:string;

i:byte;

 begin

  readln(s);

  for i:=ord(s[0]) downto 1 do write(s[i]); {открываем цикл от актуальной длины строки до 1. В теле цикла выводим все символы строки}

  writeln;

  readln;

 end.

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

Например:

Процедура writeln('AA'>'BB') выведет на экран FALSE;

Процедура writeln('AA'='aa') выведет на экран FALSE;

Процедура writeln('Привет'<'Привет, друг!') выведет на экран TRUE;

Модуль System содержит большой набор функций и процедур, предназначенных для работы со стандартными строками.

Function Concat(s1 [, s2,..., sn]: String): String;

Функция Concat объединяет переданные ей строки, присоединяя каждую последующую к концу предыдущей и возвращает полученный результат. Функция эквивалентна строковой операции контактенции "+"

Function Copy(S: String; Index: Integer; Count: Integer): String;

Функция Copy возвращает вырезку из строки S, начиная с символа, номером которого Index в количестве Count символов

Procedure Delete(var S: String; Index: Integer; Count:Integer);

Процедура Delete удаляет из строки S подстроку, начиная с символа, номер которого Index в количестве Count символов.

Procedure Insert(Source: String; var S: String; Index: Integer);

Процедура Insert вставляет строку Source в строку S, начиная с позиции Index строки S.

Function Length(S: String): Integer;

Функция Length возвращает длину переданной ей строки (эквивалентно ord(s[0]))

Function Pos(Substr: String; S: String): Byte;

Функция Pos возвращает номер позиции первого вхождения подстроки Substr в строку S. Если подстрока Substr в строке S отсутствует, то функция возвращает 0.

procedure Str(X [: Width [: Decimals ]]; var S:string);

Процедура Str переводит число X в строку S с возможностью форматирования (также как для форматированного вывода с помощью процедур Write и Writeln)

Procedure Val(S:string; var V; var Code: Integer);

Процедура Val переводит строку S в число V. Если перевод невозможен (например из-за недопустимых символов в строке, или из-за несоответствия типов - попытка перевода строкового представления вещественного числа в целочисленную переменную V), то в переменной Code возвращается код ошибки. Если перевод прошел нормально, в переменной Code возвращается 0.

Задача 30. Ввести с клавиатуры и объединить три строки.

var s1,s2,s3,s:string;

 begin

  readln(s1);

  readln(s2);

  readln(s3);

  s:=concat(s1,s2,s3); {объединяем введенные строки с использованием функции concat}

  writeln(s);

  s:=s1+s2+s3; {объединяем введенные строки с использованием операции контактенции }

  writeln(s);

  readln;

 end.

С помощью данной программы можно сравнить результаты действия функции Concat и операции контактенции.

Задача 31. Заменить в строке все знаки присваивания (':=') на знаки равенства ('=').

При решении данной задачи воспользуемся небольшой хитростью. Вместо того, чтобы заменять знаки ':=' на '=', просто удалим из каждого знака присваивания двоеточие.

var   s:string;

i:integer;

begin

 readln(s);

 i:=pos(':=',s); {находим позицию первого знака присваивания}

 while i<>0 do begin {открываем цикл для перебора всех знаков присваивания в строке}

  Delete(s,i,1); {удаляем из знака присваивания двоеточие}

  i:=pos(':=',s); {снова находим позицию знака присваивания. Так как предыдущий знак был изменен, то этим оператором мы найдем следующий знак присваивания}

 end;

 writeln(s);

 readln;

end.

Задача 32. Дана строка символов и число N. Произвести ротацию строки на N символов (например: если исходная строка равна '1x2y3z4a', а N=3, то результирующая строка будет равна ' z4a1x2y3').

var   s:string;

N:integer;

 begin

  readln(s);

  readln(N);

  n:=n mod length(s); {учитываем, что число N может быть больше, чем длина строки}

  if n<>0 then begin

   {с помощью вырезки составляем результирующую строку из двух частей исходной}

   s:=copy(s,length(s)-n+1,n)+copy(s,1,length(s)-n);

  end;

  writeln(s);

  readln;

 end.

Задача 33. Вставить пробел после каждой запятой в строке. Использование дополнительных строк не допускается.

var s:string;

     i:integer;

 begin

  readln(s);

  i:=1;

  while i<=length(s) do begin {цикл For использовать нельзя, так как в процессе итераций происходит увеличение длины строки}

   if s[i]=',' then begin {если нашли запятую, то вставляем пробел и увеличиваем счетчик на единицу, чтобы на следующей итерации не анализировать этот пробел}

    insert(' ',s,i+1);

    inc(i);

   end;

   inc(i); {переходим к следующему символу}

  end;

  writeln(s);

  readln;

 end.

Задача 34. Создать универсальный фильтр для ввода натурального числа.

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

var   s:string; {введенная строка}

N:Longint; {полученное путем преобразования число}

ErrorCode:integer; {код ошибки}

begin

 repeat {открываем цикл для организации фильтра}

  readln(s);

  val(s,N,ErrorCode); {переводим введенную строку в число}

 until ErrorCode=0; {выходим из цикла, если не произошло ошибки}

 writeln(n);

 readln;

end.

Задача 35. Дано натуральное число N. Вывести на экран значение его квадрата в прямом и обратном порядке.

var   N:Longint; i:byte; s:string;

begin

  readln(N);

  n:=sqr(n);

  writeln(n); {выводим квадрат числа}

  str(n,s); {преобразуем его в строку}

  for i:=length(s) downto 1 do write(s[i]);{выводим полученную строку в обратном порядке}

  writeln;

  readln;

end.

ASCIIZ строковый тип

ASCIIZ (тип PCHAR) представляет собой указатель (см. параграф 1.7.3) на символ типа Char, его адрес в оперативной памяти. Однако при использовании расширенного синтаксиса (что достигается с помощью директивы компилятора {$X+}) к типу PChar можно обращаться как к одномерному массиву символов, индекс которого начинается с нуля.

Например:

{$X+}

var   A:PChar;

  B:array[0..100] of char;

К переменной А можно обращаться с помощью тех же методов, что и к переменной B.

В отличии от стандартных строк, память для переменных типа PСhar не выделяется автоматически в начале программы, что накладывает определенные ограничения на использование данного типа. Например, попытка обратиться к такой переменной как к массиву до присваивания ей некоторого значения обречена на провал. Значение любой переменной PChar до присваивания ей некоторого значения всегда равно nil (см. параграф 1.7.3).

Например:

{$X+}

var s1,s2:PChar;

 begin

  s1:='Номер 1';

  writeln(s1);

  s1[6]:='2'; {здесь мы совершенно правомерно обращаемся к седьмому символу строки s1, так как ей заранее было присвоено константное значение, в результате чего s1 указывает на некоторую область памяти}

  writeln(s1);

  s2[0]:='A'; s2[1]:='B'; s2[2]:='C'; {НЕВЕРНО! Переменной s2 ничего заранее присвоено не было...}

  writeln(s2); {... и поэтому на экране ничего не появится}

  readln;

end.

Не следует забывать, что ASCIIZ строки всегда должны заканчиваться нулем. При присваивании им строковых констант или определении типированных констант это происходит автоматически. Если же потребуется принудительно сократить длину строки, достаточно в требуемое место вставить символ с кодом 0.

Например:

const s:pchar='Казнить нельзя, помиловать';

 begin

  s[7]:=#0; {всего один символ недвусмысленно решает судьбу осужденного}

  writeln(s);

  readln;

 end.

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

Например:

var s1,s2:pchar;

begin

 s1:='Cтрока номер 1';

 s2:=s1;

 s2[13]:='2';

 writeln(s1);

 writeln(s2);

 readln;

end.

В результате выполнения программы на экране появится:

Cтрока номер 2

Cтрока номер 2

Модуль Strings содержит большой набор функций, предназначенных для работы с ASCIIZ строками. Каждая из них может работать как с типом PChar, так и с массивом символов. К сожалению, функции, изменяющие или зависящие от регистра символов, не поддерживают русский алфавит.

 

Функции распределения динамической памяти

Function StrNew(Str: PChar): PChar;

Function StrDispose(Str: PChar);

Функция StrNew выделяет блок динамической памяти и копирует туда переданную ей строку Str. Адрес выделенного блока возвращается в качестве результата.

Функция StrDispose освобождает блок памяти, выделенный ранее функцией StrNew.

Функции преобразования

Function StrPCopy(Dest: PChar; Source: String): PChar;

Function StrPas(Str: PChar): String;

Функция StrPCopy копирует стандартную паскалевскую строку Source в ASCIIZ строку Dest и возвращает ее в качестве результата. Строка Dest должна иметь достаточный объем для сохранения строки Source.

Функция StrPas совершает обратную операцию.

Функция определения длины

Function StrLen(Str: PChar): Word;

Функция StrLen возвращает длину переданной ей строки

Функции поиска

Function StrPos(Str1, Str2: PChar): PChar;

Function StrScan(Str: PChar; Chr: Char): PChar;

Function StrRScan(Str: PChar; Chr: Char): PChar;

Функция StrPos возвращает указатель на первое вхождение строки Str2 в строку Str1. Если такое вхождение отсутствует, функция возвращает nil.

Функция StrScan возвращает указатель на первое, а StrRScan - на последнее вхождение символа Chr в строку Str.

Функции контактенции

Function StrCat(Dest, Source: PChar): PChar;

Function StrLCat(Dest, Source: PChar; MaxLen: Word): PChar;

Функция StrCat присоединяет к строке Dest строку Source и возвращает Dest. Строка Dest должна иметь достаточный объем для сохранения строки Source.

Функция StrLCat присоединяет к строке Dest строку Source и возвращает Dest. Полученный результат будет иметь длину не более чем MaxLen символов.

Функции сравнения

Function StrComp(Str1, Str2 : PChar): Integer;

Function StrIComp(Str1, Str2:PChar): Integer;

Function StrLComp(Str1, Str2: PChar; MaxLen: Word): Integer;

Function StrLIComp(Str1, Str2: PChar; MaxLen: Word): Integer;

Функция StrComp сравнивает две строки и возвращает значение

 <0, если Str1 < Str2

 =0, если Str1 = Str2

 >0, если Str1 > Str2

Функция StrIComp сравнивает две строки без учета регистра.

Следующие две функции сравнивают только MaxLen символов строк.

Функции копирования

Function StrCopy(Dest, Source: PChar): PChar;

Function StrECopy(Dest, Source: Pchar): PChar;

Function StrMove(Dest, Source: PChar; Count: Word): PChar;

Function StrLCopy(Dest, Source: Pchar; MaxLen: Word): PChar;

Функция StrCopy копирует строку Source в строку Dest и возвращает Dest.

Функция StrECopy копирует строку Source в строку Dest и возвращает указатель на конец объединенной строки.

Функция StrMove копирует Count символов из строки Source в строку Dest и возвращает Dest.

Для всех трех функций строка Dest должна иметь достаточный объем для сохранения Source.

Функция StrLCopy копирует максимум MaxLen символов строки Source в строку Dest и возвращает Dest.

Функции изменения регистра

Function StrLower(Str: PChar): PChar;

Function StrUpper(Str: PChar): PChar;

Функция StrLower переводит строку в строчные буквы.

Функция StrUpper переводит строку в прописные буквы.

Функция перехода

Function StrEnd(Str: PChar): Pchar;

Функция StrEnd возвращает указатель на конец строки.

Задачи для раздела "Строковый тип"

Во всех последующих задачах подразумевается, что строка вводится с клавиатуры, а измененная строка(строки) выводится на экран. Использование дополнительных строк, о которых не сказано в условии задачи, не допускается.

Задание 55. Обрезать строку за последним пробелом.

Задание 56. Удалить из строки все восклицательные знаки

Задание 57. Удвоить каждый символ строки (строка 'ABC' превращается в 'AABBCC').

Задание 58. Зашифровать строку следующим образом: увеличить код каждого символа строки на единицу.

Задание 59. "Перевернуть" строку.

Задание 60. Заменить в строке все точки троеточием.

Задание 61. Определить, имеются ли в строке два рядом стоящих одинаковых символа.

Задание 62. Разделить строку на две части по следующему правилу: все цифры остаются в первой строке, все остальные символы переносятся во вторую (строка '1sd1025ds' превратится в две - '11025' и 'sdds').

Задание 63. Все пробелы переместить в начало строки. Решите задачу с использованием и без использования дополнительных строковых переменных.

1.7.2.3   Записи

Запись в ТР является одним из самых общих и структурированных типов. Запись состоит из фиксированного числа компонент (полей), которые могут быть различных типов.

тип_запись::= "record"

[ список_полей ]

"end"

список_полей ::= ( фиксированная_часть

[";" вариантная_часть] [";"]) |

(вариантная_часть [";"])

фиксированная_часть::= список_ИД ":" тип

{ ";" список_ИД ":" тип }

 

Примеры определения типа запись с фиксированной частью:

 

type

Book=record {определяем тип записи - "Книга"}

 Title:string[40]; {название книги}

 Autor:string[50]; {автор}

 Publ:string; {издательство}

 Date:integer; {год издания}

end;

Date=record {определяем тип записи - "Дата"}

 Year: integer; {год}

 Month: 1..12; {месяц}

 Day: 1..31; {день}

end;

Заметьте, что одно из полей записи Book имеет то же имя, что и запись Date. ТП не считает это ошибкой, так как одинаковые идентификаторы находятся, тем не менее на разных уровнях.

Для обращения к определенному полю переменной типа запись требуется указать имя переменной, точку и имя поля.

Например:

var A:Book;

begin

 ...

 a.title:=’Кризис империи’;

 a.Autor:=’Дэвид Дрейк’;

 a.Publ:=’Армада’;

 a.Date:=1997;

 ...

end.

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

вариантная_часть ::= "case" [ИД_поля_признака ":"]

тип_поля_признака "of"

вариант {";" вариант}

тип_поля_признака::= порядковый_тип.

Вариант::=

константа {"," константа} ":" "(" [список_полей] ")"

Например:

type

Person=record {определяем тип записи - "Человек"}

Name:string[40]; {Имя (фамилия, отчество)}

BirthDate: Date; {дата рождения}

case Citizen:Boolean of {гражданство в нашей стране}

true: (BirthPlace: string[40]); {если гражданин, то определяем место рождения }

false: (Country:string[20]; {иначе определяем страну, из которой прибыл …}

EntryDate,ExitDate:Date); {… даты въезда и выезда}

end;

Т.о. в зависимости от гражданства определено либо место рождения, либо страна, дата въезда и дата выезда.

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

 

Для сокращения процесса обращения к полям записи служит оператор работы над записями.

Оператор_работы_над_записями::=

"with" ссылка {"," ссылка} "do" оператор

Ссылка::=ИД переменной типа запись или объект.

Внутри оператора позволяется манипулировать полями записи, ссылки на которые имеются в заголовке, без указания имени переменной типа запись.

Например:

 var I: Date;

...

with I do begin

 writeln("Год", Year);

 writeln("Месяц", Month);

 writeln("День", Day);

end;

...

Определение содержимого записи в разделе типированных констант.

Объявление константы типа запись содержит идентификатор и значение каждого поля, заключенное в скобки и разделенные точками с запятой.

Константа_запись::=

"(" ИД_поля ":" типированная_константа

{ ";" ИД_поля ":" типированная_константа } [";"] ")".

Например:

const  Man:person=

(Name: 'Иванов Иван Иванович';

BirthDate: ( Year: 1970;

Month: 2;

Day: 23);

Citizen: true;

BirthPlace: 'Шадринск');

Jerry:Person=

(Name: 'Мышонок Джерри';

BirthDate: ( Year: 1970;

Month: 2;

Day: 23);

Citizen: false;

Country: 'США';

EntryDate: ( Year: 1990;

Month: 1;

Day: 1);

ExitDate:   ( Year: 2090;

Month: 1;

Day: 1));

Задачи для раздела "Записи"

Задание 64. Создать запись с информацией об автомобиле (не менее 5 полей). Заполнить с клавиатуры массив автомобилей. Затем ввести числовое значение и вывести на экран информацию об автомобилях с объемом двигателя, равным введенному значению.

Задание 65. Создать запись с информацией о процессоре (не менее 3 полей). Заполнить с клавиатуры массив процессоров. Затем ввести строковое значение и вывести на экран информацию о процессорах, название фирмы-производителя которых соответствует введенному значению.

Задание 66. Создать запись с информацией о студенте, включающую - ФИО, номер зачетки, номер группы. Создать программу, заполняющую с клавиатуры массив студентов и переводящую их на следующий курс. Получившийся результат вывести на экран (Например: если студент был в 181 группе, то после перевода на следующий курс номер его группы изменяется на 281. Если студент был в 504 группе, то в процессе перевода на следующий курс он исключается из массива).

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

1.7.2.4   Множества

Множество в ТП является типом данных, диапазон значений которого является мощностью множества для определенного порядкового (базового) типа. Каждое возможное значение типа множества является подмножеством базового типа. Переменная типа множество может принимать как все значения множества, так и не одного. Множество не может содержит повторяющихся элементов. Размер любой переменной множественного типа составляет 32 байта.

Тип_множество::= "set" "of" порядковый тип.

Базовый тип не должен иметь более 256 значений, нижняя и верхняя границы типа не должны выходить из промежутка от 0 до 255.

Например:

type MySet = set of char;

var S:set of byte;

Создание множества получается путем записи выражений, заключенных в []. Пустое множество описывается как [].

Описатель_множества::= "[" [группа {"," группа}] "]"

группа::= выражение [".." выражение]

Значение выражения не должно выходить за пределы базового типа.

Например:

var   x:set of byte;

    c: set of char;

    k:byte;

begin

...

 x:=[1,5,100..145, K div 10];

 c:=[‘A’ .. ‘Z’, ‘a’ .. ’z’];

...

end.

Над множествами можно проводить операции объединения, разности и пересечения по алгебраическим правилам действий над множествами, а также определять, содержит ли множество некоторый элемент, или является ли одно множество подмножеством (надмножеством) другого (см. параграф 1.4.3).

Например:

Объединение:

Операция [1,2,5,12]+[0,2,5,7,34] возвратит множество [0,1,2,5,7,12,34] (состоящее из первого и второго множеств)

Разность:

Операция [1,2,5,12]-[0,2,5,7,34] возвратит множество [1,12] (состоящее из тех элементов первого множества, которые отсутствуют во втором)

Пересечение:

Операция [1,2,5,12]*[0,2,5,7,34] возвратит множество [2,5] (состоящее из тех элементов первого множества, которые имеются и во втором)

Вхождение во множество:

Операция 1 in [0,3,5] возвратит false.

Операция ‘x’ in [‘A’, ‘k’..’z’] возвратит true.

Надмножество и подмножество:

Операция [1,2,5,12]>=[0,1,2,5,7,12,34] возвратит false

Операция [1,2,5,12]<=[0,1,2,5,7,12,34] возвратит true

Задача 36. Создать фильтр ввода ответа на вопрос (типа "Да"/"Нет")

Решение:

uses crt;

var c:char;

 begin

  writeln('Вы уверены?(Д/Н)');

  repeat {начало фильтра}

   c:=readkey;

  until c in ['Д', 'д', 'Н', 'н']; {фильтр пропускает только требуемый для ответа символ}

  writeln;

  if c in ['Д','д'] then writeln(' На вопрос Вы ответили "ДА"')

  else writeln('На вопрос Вы ответили "НЕТ"');

  readln;

 end.

Для добавления и исключения элемента из множества в ТП предусмотрены две процедуры:

procedure Include(var S: set of T; I:T);

procedure Exclude(var S: set of T; I:T);

Процедура Include включает во множество S значение I.

Процедура Exclude исключает из множества S значение I.

Задача 37. Исключить из строки все повторения символов.

var   CharSet:set of char; {множество символов строки}

s:string;

i:byte;

begin

readln(s);

CharSet:=[];

i:=1;

while i<=length(s) do begin {перебираем все символы строки. Цикл For использовать нельзя, так как во время итераций происходит удаление символов и, как следствие - уменьшение длины строки}

 if s[i] in CharSet then delete(s,i,1) {если текущий символ строки уже во множестве, удаляем его (переходя таким образом к следующему символу строки)...}

 else begin {... иначе включаем его во множество и переходим к следующему символу}

  Include(CharSet,s[i]);

  inc(i);

 end

end;

writeln(s);

readln;

end.

В предыдущих версиях ТП и стандартном Паскале добавить элемент во множество можно было с помощью операции объединения, а удалить - с помощью операции разности множеств.

Например:

Если S - множество натуральных чисел, то операция [3]+S и функция Include(s,3) имеют один и тот же результат. Аналогичным образом можно оценить операцию S-[7] и функцию Exclude(s,7). Однако функции Include и Exclude выполняются значительно быстрее.

Задачи для раздела "Множества"

Задание 68. Удалить из строки все символы, входящие во множество цифр и знаков препинания.

Задание 69. Оставить в строке только буквы (Подсказка:  множество русских строчных букв разрывно. Поэтому вместо промежутка 'а'..'я' требуется использовать два промежутка - 'а'..'п' и 'р'..'я').

Задание 70. Перевести строку в верхний регистр с учетом русского алфавита.

Задание 71. Удалить из строки все символы, которые имеются в другой строке.

Задание 72. Оставить в строке только те символы, которые имеются в другой строке.

Задание 73. Все буквы строки, которые имеются во второй строке, заменить на символ 0, а оставшиеся - на символ 1.

Задание 74[1, № 557]. Воспользовавшись решетом Эратосфена, найти все простые числа в промежутке [2,N], где NÎ[2,255]. Решетом Эратосфена называют следующий способ. Выпишем подряд все целые числа от 2 до N. Подчеркнем его, а все большие числа, кратные 2, вычеркнем. Первое из оставшихся чисел - 3, поступим с ним так же, как и с двойкой. Первое из оставшихся - 5, так как 4 уже вычеркнуто. Поступим аналогично с числом 5 и всеми последующими числами до N. Подчеркнутые числа являются простыми.

2,3,4,5,6,7,8,9,10

Задание 75. Получить неповторяющуюся последовательность из 100 случайных чисел, значения которых лежат в промежутке [1,100].

1.7.2.5   Файловые типы

Файловый тип состоит из линейной последовательности компонент любого типа, кроме файлового или структурного типа, который содержит компоненту файлового типа.

Файловый_тип::= "text" | ("file" "of" тип) | "file".

Файлы TEXT являются текстовыми файлами последовательного доступа. Их обработку можно вести только последовательно, строка, за строкой, слово за словом, символ за символом. Таким образом, если требуется считать строку из середины текстового файла, придется считывать все ей предшествующие. Однако этот недостаток с лихвой перекрывают возможности форматированного вывода и ввода информации произвольного типа из файла стандартными процедурами ввода-вывода. По сути дела, экран и клавиатура сами являются текстовыми файлами соответственно для записи и чтения. В модуле System содержатся 2 текстовые файловые переменные с именами Output и Input, первая из которых сопоставлена с экраном, а вторая - с клавиатурой.

Файлы file of тип являются типированными файлами прямого доступа. Это означает, во первых, что компоненты файла имеют один и тот же тип, а о вторых, что доступ к содержимому файла ведется произвольно, в любую позицию. Таким образом, например, если нам требуется записать в середину файла вещественных чисел новое число, мы легко можем произвести установку файловой позиции в требуемое местоположение и произвести запись.

Осторожнее с файлами file of string. Такое определение подразумевает, что для каждой строки будет резервироваться 256 байт внутри файла. Это очень неэффективный подход с точки зрения экономии дискового пространства. Лучше в такой ситуации, хотя и в ущерб скорости, воспользоваться текстовыми файлами.

Файлы file являются нетипированными файлами прямого доступа. У нетипированных файлов отсутствует заранее определенная структура. Запись и чтение для таких файлов производится блоками, состоящими из последовательностей байтов. Размер блоков задается при открытии файла.

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

Принципы работы с файлами

Общий принцип обработки содержимого файлов состоит в следующей последовательности действий:

1. Сопоставление файловой переменной и конкретного файла на диске.

2. Открытие файла.

3. Чтение из файла или (и) запись в файл.

4. Закрытие файла.

Рассмотрим стандартные процедуры, позволяющие реализовать принципы работы с файлами.

Сопоставление файлов

procedure Assign(var f; name: string);

Процедура Assign сопоставляет файловую переменную F конкретному файлу на диске с именем Name.

Открытие файлов

procedure Reset(var f);

Процедура Reset открывает существующие текстовые файлы только для чтения, а типированные - для чтения и записи. F - файловая переменная, сопоставленная ранее с помощью процедуры Assign конкретному дисковому файлу.

procedure Reset(var f:file[; recsize: Word]);

Процедура Reset открывает существующий нетипированный файл F для чтения и записи. F - файловая переменная, сопоставленная ранее с помощью процедуры Assign конкретному дисковому файлу. Recsize - размер одной записи в файле. Если Recsize отсутствует, считается, что размер одной записи равен 128 байтам

procedure Rewrite(var f);

Процедура Rewrite создает текстовые файлы только для записи, а типированные - для чтения и записи. F - файловая переменная, сопоставленная ранее с помощью процедуры Assign конкретному дисковому файлу. Если файл с таким именем уже существовал на диске, он предварительно удаляется.

procedure Rewrite(var f:file[; recsize: Word]);

Процедура Rewrite создает нетипированный файл F для чтения и записи. F - файловая переменная, сопоставленная ранее с помощью процедуры Assign конкретному дисковому файлу. Recsize - размер одной записи в файле. Если Recsize отсутствует, считается, что размер одной записи равен 128 байтам. Если файл с таким именем уже существовал на диске, он предварительно удаляется.

procedure Append (var f:text);

Процедура Append открывает текстовый файл F для добавления в него информацией. F - файловая переменная, сопоставленная ранее с помощью процедуры Assign конкретному дисковому файлу.

Чтение и запись файлов

Для доступа к содержимому текстовых файлов используются процедуры Read, Readln, Write, Writeln, причем первым параметром передается имя файловой переменной. Методы обработки текстовых файлов с помощью этих процедур ничем не отличаются от методов обработки клавиатуры и экрана.

Для доступа к содержимому типированных файлов используются процедуры Read и Write, причем первым параметром также передается имя файловой переменной. Тип записываемых или считываемых переменных не должен отличаться от типа сохраненной в файле информации. В качестве параметров процедуры Write запрещено использовать выражения.

procedure BlockRead(var f: file; var Buf; Count: Word

[; var Result: Word])

Процедура BlockRead читает в переменную Buf Count записей из файла F (размер каждой записи определен при открытии файла). В переменной Result возвращается число действительно прочитанных записей.

procedure BlockWrite(var f: file; var Buf; Count: Word

[; var Result: Word])

Процедура BlockWrite Записывает в файл F Count записей из переменной Buf. В переменной Result возвращается число действительно сохраненных записей. Процедуры BlockWrite и BlockRead не могут обрабатывать более 64 килобайт информации за один раз.

Закрытие файла

procedure Close(var f);

Процедура Close закрывает файл F. Закрытие файлов требуется для освобождения всех файловых буферов в оперативной памяти, а также ресурсов, затребованных у операционной системы при открытии файла. По завершении работы программы все незакрытые файлы автоматически закрываются.

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

Решение:

{-----------первая программа------------}

var   f:file of integer;

i,v:integer;

s:string;

 begin

  write('Введите имя файла:'); readln(s);

  Assign(f,s); Rewrite(f); {сопоставляем файловую переменную и создаем текстовый файл}

  writeln('Эти значения будут сохранены в файле:');

  for i:=1 to 10 do begin {заполняем файл}

   v:=random(101)-50; {получаем случайное значение из промежутка
[-50,50]}

   write(f,v); {сохраняем полученное число в файле ...}

   write(v:4);{... а также выводим его на экран}

  end; {файл заполнен}

  writeln;

  close(f);{закрываем файл}

  readln;

 end.

 

{-----------вторая программа------------}

var   f:file of integer;

i,v:integer;

s:string;

 begin

  write('Введите имя файла:'); readln(s);

  Assign(f,s); Reset(f); {сопоставляем файловую переменную и открываем текстовый файл для чтения}

  write('Содержимое файла ',s,': ');

  for i:=1 to 10 do begin {считываем содержимое файла}

   Read(f,v);{считываем очередное число из файла}

   write(v:4);{выводим полученное значение на экран}

  end;{содержимое файла прочитано}

  writeln;

  close(f);{закрываем файл}

  readln;

 end.

Задача 39. Написать две программы. С помощью первой из них создать и заполнить типированный файл (file of byte) размером в 100000 байт. С помощью второй создать аналогичного размера нетипированный файл. Сравнить скорости работы обеих программ.

Решение:

{-----------первая программа------------}

var f:file of byte;

i:longint;

b:byte;

s:string;

 begin

  write('Введите имя файла:'); readln(s);

  Assign(f,s); Rewrite(f);{сопоставляем и открываем типированный файл для записи}

  for i:=1 to 100000 do write(f,b);{сохраняем 100000 байт в файле}

  close(f);{закрываем файл}

  writeln('Файл сохранен');

  readln;

 end.

 

{-----------вторая программа------------}

var f:file;

b:array [1..50000] of byte;

s:string;

 begin

  write('Введите имя файла:'); readln(s);

  Assign(f,s); Rewrite(f,1); {сопоставляем и открываем нетипированный файл для записи, устанавливая размер одной записи в 1 байт}

  BlockWrite(f,b,50000);{сохраняем первую половину требуемого содержимого файла}

  BlockWrite(f,b,50000);{сохраняем вторую половину требуемого содержимого файла}

  close(f);

  writeln('Файл сохранен');

  readln;

 end.

Первая программа на компьютере с процессором Pentium-120 выполняется около 20 секунд, вторая - практически мгновенно. Выводы сделайте сами.

Задача 40. Написать две программы. С помощью первой из них создать и заполнить типированный файл сотней чисел. С помощью второй создать аналогичный текстовый файл. Сравнить содержимое и размеры первого и второго файлов.

Решение:

{-----------первая программа------------}

var f:file of integer;

    i,v:integer;

    s:string;

 begin

  randomize;

  write('Введите имя файла:'); readln(s);

  Assign(f,s); Rewrite(f);

  for i:=1 to 100 do begin

   v:=random(300)-200;

   write(f,v);

  end;

  close(f);

  writeln('Файл сохранен');

  readln;

 end.

 

{-----------вторая программа------------}

var f:text;

i,v:integer;

s:string;

 begin

  randomize;

  write('Введите имя файла:'); readln(s);

  Assign(f,s); Rewrite(f);

  for i:=1 to 100 do writeln(f, integer(random(300)-200));

{Обратите внимание на приведение типов (см. параграф 1.7.5) в процедуре Writeln. Дело в том, что функция Random возвратит значение типа Word, который не имеет отрицательного диапазона. Без приведения типов мы получим множество значений в промежутках [0,100) и [65335,65535], а не в промежутке [-200,100), как должно бы быть, судя по программе}

  close(f);

  writeln('Файл сохранен');

  readln;

 end.

После выполнения обеих программ вы наверняка заметите, что размер первого (типированного) файла постоянен и равен 200 байт (100 значений типа Integer умножить на 2 байта памяти для каждого числа), в то время как размер текстового файла изменяется от запуска к запуску второй программы и превышает размер первого файла по крайней мере в полтора раза.

Файловая позиция

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

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

Файловая позиция отсчитывается от 0.

Получение файловой позиции

function FilePos(var f): Longint;

Функция FilePos возвращает файловую позицию в файле F.

function FileSize(var f): Longint;

Функция FileSize возвращает последнюю возможную файловую позицию (ее можно рассчитать по формуле: размер файла в байтах/размер одной записи). Для файлов, размер одной записи которых составляет 1 байт, FileSize возвращает размер файла. В общем случае, FileSize возвращает количество компонентов файла.

Установка файловой позиции

procedure Seek(var F; N: Longint);

Процедура Seek устанавливает файловую позицию файла F в N.

Операции над файлом, зависящие от текущей
файловой позиции

function Eof(var F): Boolean;

Возвращает True, если файловая позиция находится за концом типированного или нетипированного файла.

function Eof [ (var F: Text) ]: Boolean;

Возвращает True, если файловая позиция находится за концом текстового файла. Если файловая переменная отсутствует, то используется переменная Input (клавиатура).

function Eoln [(var F: Text) ]: Boolean;

Возвращает True, если файловая позиция находится за концом строки текстового файла. Если файловая переменная отсутствует, то используется переменная Input (клавиатура).

procedure Truncate(var F);

Отсекает конец файла, начиная с текущей файловой позиции.

Задача 41. Ввести набор из N чисел, сохранить его в файле. Организовать цикл, в котором по введенному индексу определяется и выводится на экран числовое значение из файла. Предусмотреть выход их цикла.

var   f:file of real;

v:real;

i,n:integer;

 begin

  write('Введите количество чисел: '); readln(n);

  assign(f,'Real.fil');

  Rewrite(f);

  for i:=1 to n do begin {заполняем файл вещественными числами}

   v:=random*1000-500;

   write(f,v);

  end;

  close(f);{файл заполнен, закрываем его}

  reset(f);{снова открываем файл (закрытие и повторное открытие файла использовано здесь только для того, чтобы показать возможность повторного открытия файла без повторного использования процедуры Assign)}

  write('Введите номер: '); readln(N); {ввели индекс}

  while (n>=0) and (n<=FileSize(f)-1) do begin {пока введенный индекс не вышел за пределы файла}

   seek(f,n);{устанавливаем файловую позицию}

   Read(f,v);{читаем число из файла}

   writeln(v);{выводим его на экран}

   write('Введите номер: '); readln(N); {снова вводим индекс}

  end;

{вышли из цикла, так как введен недопустимый индекс}

  Close(f);{закрываем файл}

  readln;

 end.

Задача 42. Создать программу, которая выводит на экран саму себя.

Решение данной задачи становится тривиальным, если воспользоваться файлом текста программы.

var   f:text; s:string; i:byte;

 begin

  s:=paramstr(0); {получаем имя исполняемого файла}

  s:=copy(s,1,length(s)-3)+'pas'; {заменяем расширение "exe" исполняемого файла на расширение "pas"}

  assign(f,s);

  Reset(f);

  while not eof(f) do begin {пока не конец файла ...}

   readln(f,s);{считываем строку}

   writeln(s);{выводим ее на экран}

  end;

  close(f); {обработка завершена, закрываем файл}

  readln;

 end.

Задача 43. Каждая строка (до 1000) текстового файла содержит в себе набор целых чисел (до 100), разделенных пробелами. Вывести на экран содержимое файла с использованием форматирования, а так же подсчитать среднее арифметическое каждой строки файла.

Решение:

var   f:text;{текстовый файл}

Ac: array[1..100] of integer; {массив чисел текущей строки}

Ar: array[1..1000] of real;{массив средних арифметических строк}

Row, Col : integer;{количество строк в файле и количество чисел в текущей строке}

i:integer;

begin

 assign(f,'numbers.txt');{сопоставление}

 reset(f);{открытие для чтения}

 writeln('Содержимое файла NUMBERS.TXT:');

 Row:=0;

 while not eof(f) do begin {пока не конец файла ...}

  Col:=0;

  while not eoln(f) do begin {пока не конец строки файла ...}

   inc(Col); {переходим к следующему числу строки}

   read(f, ac[Col]); {считываем очередное числовое значение из файла в массив AC}

   write(ac[Col]:6); {производим форматированный вывод}

  end;

{информация из строки считана}

  readln(f);{считываем маркер конца строки из файла}

  writeln;{разделяем выводимые на экран строки}

 

inc(Row);{переходим к следующей строке массива AR для подсчета среднего арифметического}

  ar[row]:=0;

  for i:=1 to Col do ar[row]:=ar[row]+ac[i]; {подсчитываем сумму элементов строк}

  ar[row]:=ar[row] / col; {рассчитываем среднее арифметическое}

 end;

{обработка файла закончена}

Close(f);

 writeln('Средние арифметические строк:');

 for i:=1 to Row do write(ar[i]:7:2); {выводим на экран средние арифметические строк файла}

 writeln;

 readln;

end.

Если, например, файл number.txt имеет следующее содержимое:

1 34 -2 35 43 1 2 -2 -2 7 -8

23 3 45 -3 2

2 12 -21 2 43 -43 5 6 7

2 3

7 89 7

то в результате работы программы на экране появится:

Содержимое файла NUMBERS.TXT:

     1    34    -2    35    43      1     2    -2    -2     7    -8

    23     3    45    -3      2

     2    12   -21     2    43   -43     5     6     7

     2      3

     7    89      7

Средние арифметические строк:

   9.91  14.00   1.44   2.50  34.33

Дополнительные процедуры и функции для работы с файлами

Обработка ошибок

function IOResult: Integer;

Во время работы с файлами могут возникать различные ошибки. Если установлена директива компилятора {$I+}, то работа программы при этом аварийно завершается. Если установлена директива компилятора {$I-}, то с помощью функции IOResult можно определить номер последней ошибки ввода-вывода. IOResult возвращает 0, если ошибки не произошло. Если произошла ошибка, то до вызова IOResult все операции с файлами будут заблокированы

Обработка файлов, не связанная с их содержимым

procedure Rename(var F; Newname:string);

Переименовывает файл, сопоставленный с файловой переменной F в Newname. Файл не должен быть открыт.

procedure Erase(var F);

Удаляет файл, сопоставленный с файловой переменной F. Файл не должен быть открыт.

Обработка каталогов

procedure ChDir(S: String);

Переходит в каталог с именем S.

procedure MkDir(S: String);

Создает каталог с именем S.

procedure RmDir(S: String);

Удаляет каталог с именем S.

Задача 44. Дано имя файла. Проверить, существует ли файл с таким именем он на диске.

var f:file;

s:string;

begin

 write('Введите имя файла:'); readln(s);

 {$I-}{отключаем проверку ошибок ввода-вывода}

 assign(f,s);

 Reset(f);{пытаемся открыть файл}

 {$I+}{включаем проверку ошибок ввода-вывода}

 if ioresult<>0 then {попытка открытия файла окончилась неудачно}

writeln ('Такого файла на диске нет')

 else begin {файл открыт успешно ...}

  writeln('Файл существует');

  close(f);{... не забудем его закрыть}

 end;

 readln

end.

Задачи для раздела "Файловые типы"

В задачах данного раздела подразумевается, что результат работы программ оценивается, по возможности, с экрана.

Задание 76. Создать программу, в которой вводятся с клавиатуры произвольные числа, пока не будет введен 0, и сохраняются в текстовом файле только те из них, которые кратны 7.

Задание 77. Дан текстовый файл, заполненный целыми числами (создать в любом текстовом редакторе). Вывести на экран только те из них, порядковый номер которых равен значению числа.

Задание 78.  Дан текстовый файл, заполненный целыми числами и одномерный массив из 5 целых чисел. Вывести на экран в только те числа из файла, которые равны одному из чисел массива.

Задание 79. Дан текстовый файл, заполненный целыми числами (создать в любом текстовом редакторе). Вывести их на экран в обратном порядке.

Задание 80. Создать и заполнить одними и теми же строками 2 файла. Первый - текстовый файл, второй - типированный файл строк. В операционной системе сравнить размеры и содержимое обоих файлов. Сделать выводы.

 Задание 81. Дан файл, компоненты которого являются целыми числами. Найти сумму всех компонент файла.

Задание 82. Создать две программы. Первая из них заполняет файл геометрическими фигурами (см. Задание 67). Вторая программа по введенному номеру считывает из файла запись и выводит ее содержимое на экран.

Задание 83. Создать программу копирования одного файла в другой. Оптимизировать по скорости выполнения.

1.7.2.6   Объектные типы (обзор)

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

Объектный тип ::=

  "object" ["("объект_предок")"]

   {["private" | "public"]}

   поля_объекта

   заголовки_методов_объектов

"end".

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

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

Пример:

type

Point=object

    {поля объекта}

  x, y : integer;

    {заголовки методов объекта}

    constructor Init(new, NewY:integer);

destructor Done; virtual;

    procedure SetXY(new, NewY:integer);

    {скрытые поля и заголовки методов объекта}

  private

     OldX,OldY:integer;

   Procedure ClearXY;

end;

TCircle= object(point) {наследование от point}

    {поля объекта}

    R : integer;

    {заголовки методов объекта}

  constructor Init(new, NewY, NewR:integer);

    procedure SetRadius(NewR:integer);

    {скрытые поля и заголовки методов объекта}

  private

     OldR: integer;

   Procedure ClearR;

end;

В разделе объявления процедур и функций все методы объекта должны быть определены.

Например:

constructor TCircle.Init(new, NewY, NewR:integer);

 begin

inherited Nit(new, NewY); {вызов метода предка}

R:=NewR;

 end;

Определение переменных объектного типа и обращение к ним происходит аналогично записям (то есть с помощью символа "." или оператора With).

Пример:

var S:Tpoint;

begin

 

 s.Init(10,20);

 

with s do begin

 SetXY(x*10,y*10);

end;

 

 s.done;

end.

Подробно данный тип, его свойства и область применения мы рассмотрим во второй части нашего пособия.

1.7.3        Указатели

Тип указатель определяет адрес памяти для переменной базового типа.

Тип_указатель::= ("^" базовый_тип) | "Pointer"

Базовый_тип::= ИД типа.

Указатель может быть типированным, то есть указывать на переменную некоторого типа, и может быть нетипированным (pointer), то есть указывать на переменную произвольного типа. Если указатель имеет нулевое значение, его значение равно nil. В независимости от типа указателя, его размер в памяти компьютера постоянен и равен 4 байтам. Для обращения к переменной, адрес которой хранится в указателе, требуется после имени указателя ставить знак "^".

В ТП существует специальная операция @ (см. параграф 1.4.3.6), с помощью которой можно получить указатель на любую переменную, процедуру или функцию

Пример:

var   a:integer;

pa1,pa2:^integer;

begin

 pa1:=@a; {получаем адрес переменной a и присваиваем его
указателю
Pa1}

 pa2:=pa1; {присвоили указателю Pa2 значение указателя Pa1. Теперь Pa1 и Pa2 указывают на одну и ту же область памяти}

 pa1^:=7;

 pa2^:=6;

 writeln(a,' ',pa1^,' ',pa2^);

end.

В результате работы программы на экране появятся три числа 6.

Оперативная память компьютера, выделенная операционной системой для программы, делится на несколько частей:

1)     Область кода, где хранится исполняемая часть программы (максимум 64К на модуль).

2)     Область стека, где хранятся вызовы и локальные переменные процедур и функций (максимум 64К).

3)     Область статических данных, где хранятся все глобальные переменные и размер которой равен 64К.

4)     Область динамических данных или куча (вся оставшаяся память, запрошенная программой у операционной системы и объем которой в реальном режиме может в 8-10 раз превышать объем области статических данных, а в защищенном - достигать 16 мегабайт). ТП позволяет произвольно выделять память из кучи блоками любого размера до 64К, а после использования - возвращать обратно.

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

Процедуры для работы с динамической памятью

Выделение памяти для динамических переменных

procedure New(var P: Pointer);

Процедура New выделяет память для динамической переменной P. Объем выделяемой памяти рассчитывается на основе типа переменной. Таким образом, требуется, чтобы переменная P была типированным указателем.

procedure GetMem(var P: Pointer; Size: Word);

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

Процедуры New и GetMem могут стать причиной аварийной остановки программы, если размер затребованной памяти превышает размер максимального свободного блока.

Освобождение памяти, выделенной ранее для динамических
переменных

procedure Dispose(var P: Pointer);

Процедура Dispose освобождает динамическую переменную P. Используется в паре с процедурой New.

procedure FreeMem (var P: Pointer; Size: Word);

Процедура FreeMem освобождает динамическую переменную P объемом в Size байт. Используется в паре с процедурой GetMem.

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

Альтернативный метод освобождения динамической памяти

procedure Mark (var p: pointer);

Процедура Mark сохраняет состояние кучи в указателе P. Используется совместно с процедурами New и GetMem.

procedure Release(var p: pointer);

Процедура Release освобождает все динамические переменные, память для которых была выделена после использования Mark с указателем P.

Процедуры Mark и Release не должны использоваться совместно с Dispose и FreeMem. Мы вообще не советуем пользоваться этими процедурами, как устаревшими и включенными в язык только для совместимости с предыдущими версиями.

Состояние динамической памяти

function MemAvail: Longint;

Процедура Memavail возвращает объем свободной памяти в куче.

function MaxAvail: Longint;

Процедура MaxAvail возвращает объем максимального свободного блока памяти в куче.

В идеальном случае значения, возвращаемые процедурами MemAvail и MaxAvail, равны. Однако при частом выделении и освобождении блоков возможна дефрагментация кучи. В самом худшем случае может оказаться. что нет возможности выделить память для указателя, несмотря на то, что общий объем кучи превышает затребованный в несколько десятков раз.

Адресация памяти

ТП позволяет напрямую взаимодействовать с адресами памяти компьютера. Каждый байт памяти IBM PC адресуется в реальном режиме с помощью двух значений - сегмента и смещения. Следующие функции преобразовывают адреса переменных в сегмент и смещение, а сегмент и смещение - в указатели. Получение сегмента и смещения любого объекта ТП может оказаться полезным при прямом обращении к памяти с помощью предопределенных массивов Mem, MemW и MemL (см. параграф 1.7.2.1)

function Seg(X): Word;

Функция Seg возвращает сегментную часть адреса объекта X

function Ofs(X): Word;

Функция Ofs возвращает смещение объекта X

function Ptr(Seg, Ofs: Word): Pointer;

Функция Ptr возвращает указатель, сформированный из сегмента и смещения

function Addr(X): pointer;

Функция Addr возвращает указатель на объект X

Этапы работы с динамической переменной:

1) Определение ДП.

2) Выделение памяти для ДП или присваивание ей адреса некоторого объекта (переменной, процедуры или функции, произвольной области памяти и т.д.).

3) Работа с динамической переменной.

4)  Освобождение памяти, выделенной для динамической переменной.

Например:

type Arr=Array[1..20000] of integer; {определяем тип массива размером в 40000 байт }

var   a:array[1..10000]of ^arr;{определяем массив указателей на массив}

i,n:integer;

begin

 n:=0;

 writeln('Исходный объем динамической памяти: ', MemAvail,

' байт');

 while MaxAvail>=sizeof(arr) do begin {пока объем кучи достаточен}

  inc(n);{переходим к следующему элементу массива}

  New(a[n]);{выделяем память}

 end;

 writeln('Удалось выделить память только для ', n,

' элементов массива');

 writeln('Осталось памяти ', MemAvail, ' байт');

 for i:=1 to n do dispose(a[i]);{освобождаем массив динамических переменных}

 readln;

end.

 

Создание и обработку динамических структур данных мы подробно рассмотрим во второй части нашего пособия.

Задачи для раздела "Указатели"

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

Задание 84. Заполнить случайными числами 2 байтовых массива, каждый размером в 35000 байт. Сравнить содержимое массивов и подсчитать количество элементов первого из них, равных соответствующим элементам (с тем же индексом) второго.

Задание 85. Дан текстовый файл размером не менее 1000 строк. Сохранить его в другом текстовом файле задом наперед (то есть первая строка становится последней, вторая - предпоследней и т.д.). Предусмотреть обработку ошибок в случае нехватки оперативной памяти.

Задание 86. Дана действительная матрица размером 200 на 200 элементов. Заполнить ее случайными числами и подсчитать сумму элементов главной и побочной диагоналей.

Задание 87. Дана целочисленная матрица размером 20 на 5000 элементов. Заполнить ее случайными числами и определить средние арифметические элементов строк.

1.7.4        Процедурные типы (обзор)

В ТП процедуры и функции рассматриваются как объекты, которые можно присваивать переменным, передавать в качестве параметров и т.д. Для этого существуют процедурные типы.

Процедурный_тип ::=

("procedure" [список_формальных_параметров]) |

("function" [список_формальных_параметров] ":" тип).

После его определения можно объявлять переменные процедурного типа.

Пример:

type

        Proc = procedure;

        SwapProc = procedure(var X, Y: Integer);

        StrProc = procedure(var S: String);

        MathFunc = function(X: Real): Real:

        DeviceFunc = function(var F: Text): Integer;

        MaxFunc = function(A, B: Real; F: MathFunc): Real;

var A: SwapProc;

      B: StrProc;

      С: MaxFunc;

Этим переменным можно присваивать идентификаторы глобальных процедур и функций, скомпилированных по дальней модели вызова (см. параграф 1.8.5.1)

Более подробно процедурные типы мы рассмотрим в параграфе 1.8.4.

1.7.5        Приведение типов

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

Например, нельзя преобразовать один в другой два типа записи, если первый содержит 3 поля типа Char, а второй - 2 поля типа Word. В то же время можно преобразовать запись с двумя байтовыми полями к типу Integer и тип Longint к типу Boolean.

Синтаксис приведения типа аналогичен вызову функции с одним параметром.

Приведение_типа::=

тип_результата "(" выражение_исходного_типа ")"

Например:

Вместо функции Ord можно использовать преобразование типа:

var a:word;

begin

a:=word('0');

writeln(a);

end.

Для доступа к младшему и старшему байтам значения типа Word можно воспользоваться следующим методом:

type

ByteRec = record {тип записи для получения доступа к байтам слова}

Lo,Hi : Byte;

end;

var

W: Word;

begin

W := $709;

writeln(ByteRec(W).Lo);

writeln(ByteRec(W).Hi);

readln;

end.

После запуска на экране появятся числа 9 (младший байт) и 7(старший байт). Аналогичного результата можно добиться следующей программой:

var W: Word;

p:^byte; {объявляем переменную - указатель на байт}

begin

p:=@w; {присваиваем указателю адрес слова}

W := $709;

writeln(p^); {выводим младший байт}

inc(longint(p)); {увеличиваем указатель, прибегая к преобразованию его в тип longint, так как процедура Inc ожидает передачи ей значения порядкового типа}

writeln(p^); {выводим старший байт}

readln;

end.

1.7.6        Совместимость типов

В любом выражении ТП требуется совместимость типов. Совместимость типов имеет место, если выполняется хотя бы одно из следующих условий:

1) оба типа являются одинаковыми

2) оба типа являются вещественными типами

3) оба типа являются целочисленными

4) один тип является поддиапазоном другого

5) оба типа являются поддиапазоном одного и того же основного типа

6) оба типа являются типами множеств с совместимыми базовыми типами

7) оба типа являются упакованными строковыми типами с одинаковым числом компонент

8) один тип является строковым, а другой - либо строковым, либо упакованным строковым типом, либо типом Char

9) Один тип - Pointer, а другой - любой тип указателя

10) Оба типа являются процедурными типами с тождественными типами результата, с тождественным числом параметров, с тождественными (один в один) типами параметров.

Совместимость по присваиванию необходима с случае использования оператора присваивания или при передаче значений параметров процедурам или функциям.

Значение типа Т2 является совместимым по присваиванию с типом Т1 (т.е. допустимо Т1:=Т2), если выполняется одно из следующих условий:

1.   - Т1 и Т2 являются тождественными типами, и ни один из них не является файловым типом или структурным типом, содержащим компоненту с файловым типом на одном из своих уровней.

2.   - Т1 и Т2 являются совместимыми порядковыми типами и значения типа Т2 попадают в диапазон возможных значений Т1.

3.   - Т1 и Т2 являются вещественными типами и значения типа Т2 попадают в диапазон возможных значений Т1.

4.   - Т1 является вещественным типом, а Т2 является целочисленным типом.

5.   - Т1 и Т2 являются строковыми типами.

6.   - Т1 является строковым типом, а Т2 является типом Char.

7.   - Т1 является строковым типом, а Т2 является упакованным строковым типом.

8.   - Т1 и Т2 являются совместимыми упакованными строковыми типами.

9.   - Т1 и Т2 являются совместимыми типами множеств, и все значения типа Т2 попадают в диапазон возможных значений Т1.

10.- Т1 и Т2 являются совместимыми типами указателей.

11.- Т1 и Т2 являются совместимыми процедурными типами.

12.- Т1 является процедурным типом, а Т2 является процедурой или функцией с тождественным типом результата, с идентичным числом параметров и тождественностью один-в-один между типами параметров.

13.- Объект типа Т1 совместим по присваиванию с объектом типа Т2, если Т2 находится в области определения Т1.

14.- Указатель типа Р2, указывающий на тип объекта Т2, совместим по присваиванию с указателем типа Р1, указывающим на тип объекта Т1, если Т2 лежит в области определения Т1.

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


 

1.8 Процедуры и функции

Процедуры и функции в ТП используются для сокращения записи повторяющихся частей программы. Каждое объявление процедуры включает в себя заголовок и тело процедуры. Процедура вызывается с помощью оператора процедуры. Функции в ТП отличаются от процедур тем, что возвращают значение любого типа размером в 1, 2, 4 байта или типа String и вызываются внутри выражений.

1.8.1        Объявление процедур и функций

Каждая процедура или функция в ТП должна быть объявлена в разделе объявлений процедур и функций перед ее вызовом, как было показано в параграфе 1.3.3.

Обявление_процедуры ::= заг_проц тело_проц ";".

В заголовке процедуры описывается имя процедуры и список формальных параметров:

Заг_проц ::= "procedure" ИД

["(" объявление_параметра

 {";" объявление_параметра }")"] ";".

объявление_параметра::=["var" | "const"] список_ИД

 [":" тип_параметра].

тип_параметра::= ИД_типа | "string" | "file" |

("array" "of" ИД_типа).

Тело_проц ::= ([("near" | "far" | "interrupt") ";"] (БЛОК | "forward"| "external" | блок_asm)) | директива_inline.

Тело процедуры содержит в себе либо блок объявлений и операторов (блок, см. параграф 1.3.3), либо блок программы на ассемблере (блок_asm), либо процедурные директивы, указывающие, что блок будет определен в другом месте программы (forward, external). Дополнительно могут использоваться директивы, определяющие некоторые внутренние свойства и область применения процедуры (near, far, interrupt).

Примеры объявления процедур:

procedure Exec(Path, CmdLine: string);

begin ... end;

procedure FindFirst(Path: PChar; Attr: Word; var F: TSearchRec);

{ TSearchRec - ранее определенный тип}

begin ... end;

procedure S(var T:real; const x: array of real);

begin ... end;

{$L my.obj}

procedure X(a:byte);external; {X - процедура на ассемблере в файле my.obj}

procedure Y(b:integer);forward; {предопределение процедуры Y}

procedure Z(c:longint);far;

begin ... end;

procedure Y(b:integer); {фактическое определение тела процедуры Y}

begin ... end;

Тело процедуры также может состоять только из директивы inline, которая является макроопределением на машинном коде.

Объявление функции отличается от объявления процедуры заголовком и невозможностью применения к телу функции директивы interrupt:

Заголовок_функции::= "function" ИД

["(" объявление_параметра

 {";" объявление_параметра }")"] ":" ИД_типа ";".

Идентификатор типа, идущий за двоеточием, определяет тип значения, возвращаемого функцией.

Примеры объявления функций:

function Max(x,y:real):real; begin ... end;

function N(x,y:longint):string; asm ... end;

function D(a:byte):string; near; begin ... end;

function E(var S:string):byte; inline ( ... );

Функции в ТП отличаются от процедур тем, что возвращают некоторый результат и могут быть использованы в выражениях. В составном операторе тела функции должен быть хотя бы один оператор присваивания, слева от которого стоит ИД функции (см. параграф 1.3.3.2). Присваивание значения функции может производиться произвольное количество раз.

Пример:

function Max(x,y:real):real; {определение максимального из двух чисел}

begin

if x>y then

max:=x {присваивание результата функции}

else

max:=y; {присваивание результата функции}

end;

Функции не могут быть функциями обработки прерываний.

1.8.2        Формальные и фактические
параметры.

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

Например:

var a: byte; {определяем глобальную переменную с именем A}

procedure One(a:byte); {определяем процедуру One с аргументом A}

  var b:byte; {определяем локальную переменную B процедуры One}

 procedure Two;

   var a:byte; {определяем локальную переменную A процедуры Two}

  begin

a:=89;{значение присваивается локальной переменной A процедуры Two}

b:=a; {значение локальной переменной A процедуры Two присваивается локальной переменной B процедуры One }

  end; {процедура Two завершена …}

 begin {… начинается процедура One }

a:=98;{значение присваивается переменной A из процедуры One}

 end; {процедура One завершена …}

begin {… начинается основная программа }

a:=0; {значение присваивается глобальной переменной A}

end.

Существует 3 вида формальных параметров: Параметр-значение, Параметр-переменная, Параметр-нетипированная переменная.

1) Параметр-значение - без "var" или "const", но с типом параметра.

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

Фактический параметр, соответствующий параметру-значению в операторе процедуре или вызове функции может быть выражением любого типа, совместимого по присваиванию с типом формального параметра.

Например:

В результате работы программы на экране появится число 8.

2) Параметр-переменная - с "var" или "const", и с типом параметра. Компилятор запрещает изменять параметр, предваряемый зарезервированным словом const.

Параметр-переменная используется как для передачи информации в процедуру или функцию, так и обратно. Любое изменение формального параметра-переменной сказывается и на фактическом параметре.

Фактический параметр, соответствующий формальному параметру-переменной, должен быть переменной с типом, тождественным формальному параметру.

Например:

Если заголовок процедуры определен следующим образом:

procedure A(var b:byte);

то компилятор выдаст ошибку при попытке ее вызова с константным значением или выражением в качестве аргумента.

procedure A(var b:byte);

begin

end;

var   x:integer; y:byte;

begin

a(7); a(4+y); a(x); {неверные вызовы!!!}

a(y); {правильный вызов}

end.

Файловые типы могут передаваться только как параметры-переменные.

Параметры-переменные отличаются от параметров-значений методом передачи данных. Если параметр-значение - это копия фактического параметра, то параметр-переменная - это ссылка на фактический параметр, его адрес в памяти компьютера. Из этого вытекает еще одна особенность параметров-переменных: объем памяти, выделяемый для них, неизменен и равен 4 байтам. Таким образом, если в процедуру или функцию требуется передать, например, массив большого размера, то для ускорения работы программы и экономии памяти желательно осуществить его передачу как параметра-переменной. В то же время, если требуется запретить изменение переданного массива, можно вместо зарезервированного слова var воспользоваться словом const.

Например:

procedure X(var A:integer); begin a:=7; end;

  var d:integer;

begin {основная программа}

  d:=8;

  x(d);

  writeln(d);

end.

В результате работы программы на экране появится число 7.

3) Параметр-нетипированная переменная - с "var" или "const", и без типа параметра.

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

Задача 45.

1) Создать функцию - аналог стандартной процедуры Val, которая будет возвращать целочисленный эквивалент переданной ей строки. Проверку на ошибки в тело функции не включать.

2) Создать функцию - аналог стандартной процедуры Val, которая будет возвращать как параметр-переменную целочисленный эквивалент переданной ей строки. В качестве результата функции возвращать логическое значение, показывающее, удачно произошел перевод, или нет.

3) Создать функцию перевода из целого числа в строку.

Проверить результат действия всех трех функций.

Решение:

{-------- функция StrToInt ---------}

function StrToInt(S:string):longint;{функция перевода из строки в число}

  var V:longint; Code:integer;

 begin

  val(s,v,code);

  StrToInt:=v; {результат перевода присвоили значению функции}

 end;

{-------- функция StrToIntB ---------}

function StrToIntB(S:string; Var V: longint):boolean;

{функция перевода из строки в число с проверкой}

  var Code:integer;

 begin

  val(s,v,code);

  StrToIntB:=code=0;{При отсутствии ошибок перевода функция возвращает True }

 end;

{-------- функция IntToStr ---------}

function IntToStr(v:longint):string; {функция перевода из числа в строку}

  var S:string;

 begin

  str(v,s);

  IntToStr:=s; {результат перевода присвоили значению функции}

 end;

{---------------начинается основная программа-----------------------}

  var s:string; v:longint;

 begin {Начало основной программы, проверка действий разработанных функций}

  write('Введите целое число  '); readln(v);

  writeln('Вот его перевод в строку - ',IntToStr(v));

  writeln('А вот обратный перевод - ',StrToInt(IntToStr(v)));

  write('Введите строку '); readln(s);

  if not StrToIntB(s,v) then

   writeln('Строка не может быть переведена в целое число')

  else

   writeln('Результат перевода - ',v);

  readln;

 end.

 

Задача 46*. Дано натуральное число N<10000. Вычислить и вывести на экран 2N.

Решение:

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

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

При таких больших числах возникает еще одна проблема. Если мы желаем манипулировать при выводе рассчитанным значением (например, ограничить кавычками, предварить фразой, увидеть результат в требуемой системе счисления и т.д.), то желательно преобразовать его в строку. На первый взгляд, никакой проблемы не существует. Можно преобразовывать каждую цифру в символ и добавлять к концу получаемой строки. Однако число 2848 занимает 256 символов, что превышает максимальный размер стандартной паскалевской строки. Единственный выход из такой ситуации - использовать тип PChar, что и сделано в следующей программе:

{$X+}

uses strings;

{----------------процедура умножения массива на цифру--------------------}

procedure Mul(var Massiv; Var Count:integer;

N:byte; NumSystem:byte);

{Massiv     - нетипированная параметр-переменная. Должна представлять собой исходный массив байт (цифр) для перемножения.

 Count       - количество цифр в массиве

 N             - цифра - множитель.

 NumSystem - система счисления, в которой производится расчет}

var i,v,p:integer;

A:array[1..32767]of byte absolute Massiv; {абсолютная переменная для адресации массива}

 begin

  p:=0; {перенос, начальное значение равно 0}

  for i:=1 to count do begin {перебираем все цифры массива, эмулируя перемножение в столбик}

   v:=a[i]*n; {получаем произведение очередной цифры массива и множителя}

   inc(v,p); {увеличиваем полученный предыдущим действием результат на старый перенос}

   a[i]:=v mod NumSystem; {заменяем текущий элемент массива младшей цифрой рассчитанного выражения}

   p:=v div NumSystem; {получаем новый перенос}

  end;

  if p>0 then begin {если при выходе из цикла перенос не равен нулю, ...}

   inc(count); a[count]:=p; {... то увеличиваем количество элементов массива и заносим в последний из них перенос}

  end;

 end;

 

{------------функция получения строки символов из массива---------------}

function OutString(var Massiv; Count:integer; Dest:PChar):PChar;

{Massiv    - нетипированная параметр-переменная. Должна являться исходным массивом байт (цифр) для перемножения.

 Count       - количество цифр в массиве

 Dest        - результирующая строка, должна иметь достаточный объем для сохранения результата.}

{-------универсальная функция перевода числа в символ---------}

function IntToChar(v:byte):Char;

begin

if v>9 then IntToChar:=chr(v-10+ord('A')) {если цифра находится в системе счисления, основание которой больше 10, то преобразуем ее в символ-букву, начиная с латинской "A"}

else IntToChar:=chr(v+ord('0')); {если цифра - в десятичной системе счисления, то преобразуем ее в символ-цифру, начиная с "0"}

end;

{------------------функция IntToChar завершена---------------------}

{------------------начинается функция OutString---------------------}

var i:integer;

A: array[1..32767]of byte absolute Massiv; {абсолютная переменная для адресации массива}

begin

for i:=Count downto 1 do {заполняем строку преобразованными в символы цифрами из массива (с учетом того, что цифры в массиве расположены в обратном порядке, т.е. самая младшая цифра - слева, самая старшая- справа )}

Dest[Count-i]:=IntToChar(a[i]);

Dest[Count]:=#0; {добавляем признак конца строки}

OutString:=Dest; {возвращаем полученную строку как результат функции}

end;

{------------------функция OutString завершена---------------------}

{----------------начинается основная программа---------------------}

var   a:array[1..10000] of byte;{массив цифр}

S:array[0..10000] of char;{массив символов, эквивалент типа PChar}

i:integer;

const Count:integer=1;{первоначальное количество цифр - 1}

begin

 fillchar(a,sizeof(a),0); {заполняем нулями исходный массив цифр}

 a[1]:=1; {первый элемент массива - 1}

 Readln(n);

 for i:=1 to N do begin {возводим двойку в N-ю степень}

  mul(a,count,2,10); {умножаем двойку на массив А длинною в Count цифр, ведя расчеты в десятичной системе счисления}

 end;

 Writeln(OutString(a,count,s)); {выводим на экран полученный результат}

 readln;

end.

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

1.8.3        Открытые массивы и строки

В ТП версии 7.0 было введено понятие открытого массива.

Открытый массив - это формальный параметр процедуры или функции, который является одномерным массивом переменной длины, индексируемым с 0.

Открытые массивы описываются как array of ИД_типа, без использования диапазона значений. Фактическим параметром, сопоставленным открытому массиву, может быть любой одномерный массив с элементами типа ИД_типа. Аналогично открытым массивам ведется обработка строк PChar, когда последние являются формальными параметрами процедур и функций.

Для получения верхней границы открытого массива можно воспользоваться функцией High (см. параграф 1.5.1.3).

Например:

procedure HighA(f:array of integer); {процедура для констатации верхней границы массива}

begin

writeln('Верхняя граница массива - ',high(f));

end;

{---------основная программа------------}

var   x:array[1..100]of integer;{массив на 100 элементов}

y:array[-100..300] of integer; {массив на 401 элемент}

begin

 HighA(x);

 HighA(y);

 readln;

end.

В результате выполнения данной программы на экране появится:

Верхняя граница массива - 99

Верхняя граница массива - 400

Так как индексация открытых массивов ведется от 0, то значение верхней границы на единицу меньше количества элементов массива.

Задача 47. Создать две процедуры. Первая из них заполняет случайными числами переданный ей одномерный вещественный массив. Вторая выводит на экран содержимое переданного ей вещественного массива.

procedure Fill(var f:array of real); {процедура заполнения}

 var i:word;

begin

 for i:=low(f) to High(f) do f[i]:=random;

end;

procedure Out(var f:array of real); {процедура вывода на экран}

 var i:word;

begin

 for i:=low(f) to High(f) do write(f[i]:5:2);

 writeln;

end;

{---------основная программа------------}

var x:array[1..5]of real; {массив на 5 элементов}

y:array[1..10] of real; {массив на 10 элементов}

begin

 randomize;

 Fill(x); Fill(y); {заполняем оба массива}

 Out(x); Out(y); {выводим их на экран}

 readln;

end.

В результате выполнения программы на экране появится две строки, первая из которых содержит 5 чисел, вторая - 10.

Например:

0.62 0.98 0.74 0.98 0.87

0.63 0.97 0.03 0.68 0.31 0.94 0.72 0.13 0.18 0.04

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

Открытыми строками в ТП называются строковые параметры-переменные типа string. Открытые строки обладают свойством менять свою максимальную длину в соответствии максимальной длиной фактически переданной строки. При использовании директивы компилятора {$P+} строковые параметры-переменные превращаются в открытые строки.

Например

{$P+}

procedure MaxLen(var f:string);

begin

 writeln('Максимальная длина строки = ',high(f));

end;

{---------основная программа------------}

var x:string[70]; {строковая переменная на 70 символов}

y:string[50]; {строковая переменная на 50 символов}

begin

 MaxLen(x);

 MaxLen(y);

 readln;

end.

В результате выполнения данной программы на экране появится:

Максимальная длина строки = 70

Максимальная длина строки = 50

Если отказаться от поддержки открытых строк с помощью директивы {$P-}, и не забыть установить директиву {$V-}, которая позволяет отключить проверку компилятором совпадения максимальных длин строк для формальных и фактических параметров ({$P-,V-}), то результат выполнения данной программы будет другим:

Максимальная длина строки = 255

Максимальная длина строки = 255

1.8.4        Процедурные типы

Как было показано в параграфе 1.7.4, процедурные типы можно использовать в разделе объявления типов, переменных, а также типированных констант. Значения процедурного типа также могут передаваться в качестве параметров процедурам и функциям.

Задача 48. Вывести на экран таблицы сложения, вычитания, умножения, деления и возведения в степень.

Решение задачи не представляет особой сложности. Однако без использования процедур и процедурных типов оно будет громоздким и лишенным универсальности:

uses crt;

type MathFunc= Function (a,b:real):real; {определяем процедурный тип}

     MathRecord=record {определяем запись для хранения информации о соответствующей математической функции}

      Name:string[20];{имя функции}

      Func:MathFunc;{сама функция}

     end;

{$F+}{устанавливаем дальнюю модель вызова}

 Function Add(a,b:real):real; {функция сложения}

  begin Add:=a+b; end;

 function Sub(a,b:real):real; {функция вычитания}

  begin Sub:=a-b; end;

 Function Mul(a,b:real):real; {функция умножения}

  begin Mul:=a*b; end;

 function Division(a,b:real):real; {функция деления}

  begin Division:=a/b; end;

 function Power(a,b:real):real; {функция возведения в степень}

  begin Power:=exp(b*ln(a)); end;

const Funcs:array[1..5] of MathRecord={константный массив для хранения информации о математических функциях}

     ((Name:'сложение';func:Add),

      (Name:'вычитание';func:Sub),

      (Name:'умножение';func:Mul),

      (Name:'деление';func:Division),

      (Name:'возведение в степень';func:Power));

procedure PrintTable(dx,dy:integer; Func:MathFunc);

{--------------процедура для вывода на экран таблицы--------------}

{ dx, dy - размеры таблицы (не советуем выводить таблицы с количеством столбцов более семи, не хватает ширины экрана для универсального форматирования)

 func - соответствующая математическая функция}

var i,j:integer;

  begin

   for i:=1 to dy do begin

    for j:=1 to dx do write(' ',func(i,j):9:2);

    writeln;

   end;

  end;

{------основная программа--------}

var   a,b:integer;

i:byte;

begin

 clrscr;

 write('Введите размеры таблицы (2 числа): '); readln(a,b);

 for i:=1 to 5 do begin

  with funcs[i] do begin {ведем обработку очередной записи информации о математической функции}

   writeln('Вывод таблицы, операция - ',Name);

   PrintTable(b,a,func);

   readkey; {после вывода каждой таблицы ожидаем нажатия клавиши}

  end;

 end;

end.

Например, при выводе таблицы размером в 3 строки и 7 столбцов мы получим следующий результат:

Введите размеры таблицы (2 числа): 3 7

Вывод таблицы, операция - сложение

      2.00      3.00      4.00      5.00      6.00      7.00       8.00

      3.00      4.00      5.00      6.00      7.00      8.00       9.00

      4.00      5.00      6.00      7.00      8.00      9.00     10.00

Вывод таблицы, операция - вычитание

      0.00     –1.00     –2.00    –3.00     –4.00     –5.00     –6.00

      1.00       0.00     –1.00    –2.00     –3.00     –4.00     –5.00

      2.00       1.00       0.00    –1.00     –2.00     –3.00     –4.00

Вывод таблицы, операция - умножение

      1.00      2.00      3.00       4.00       5.00       6.00       7.00

      2.00      4.00      6.00       8.00     10.00     12.00     14.00

      3.00      6.00      9.00     12.00     15.00     18.00     21.00

Вывод таблицы, операция - деление

      1.00      0.50      0.33      0.25      0.20      0.17      0.14

      2.00      1.00      0.67      0.50      0.40      0.33      0.29

      3.00      1.50      1.00      0.75      0.60      0.50      0.43

Вывод таблицы, операция - возведение в степень

      1.00     1.00      1.00     1.00       1.00      1.00       1.00

      2.00     4.00      8.00   16.00     32.00    64.00   128.00

      3.00     9.00    27.00   81.00  243.00  729.00  2187.00

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

1.8.5        Процедурные директивы

1.8.5.1   Директивы Near и Far

ТП поддерживает две модели вызова процедур и функций - ближнюю и дальнюю. Ближняя более эффективна, но может использоваться только внутри одного модуля. Дальние процедуры и функции могут вызываться из любого модуля, но их код менее эффективен. В тех случаях, когда требуется принудительно объявить процедуру или функцию как дальнюю или ближнюю, используют near и far объявления.

Для тех же целей служит специальная директива компилятора. После того, как в программе встретится директива {$F+}, все объявляемые процедуры и функции (не содержащие директивы near) будут определены по дальней модели вызова. Аналогично, после директивы {$F-}, все объявляемые процедуры и функции (не содержащие директивы far) будут определены по ближней модели вызова.

1.8.5.2   Опережающие объявления

Директива forward позволяют описать процедуру или функцию до описания операторов, составляющих ее тело. Это требуется, когда из одной процедуры происходит вызов другой, еще не определенной. Однако forward-процедура должна быть обязательно полностью определена в этом же блоке.

Невозможно обойтись без директивы forward при решении следующей задачи:

Задача 49. Дано множество поочередно вложенных друг в друга окружностей и равносторонних треугольников, начиная с окружности. Радиус первой окружности равен R, общее количество фигур - N. Вывести на экран площади каждой из N фигур.

Решение.

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

Площадь окружности вычисляется по формуле . Сторона вписанного в окружность равностороннего треугольника зависит от радиуса окружности по формуле .

Площадь равностороннего треугольника вычисляется по формуле . Радиус вписанной в равносторонний треугольник окружности .

{-------опережающее объявление-------}

procedure AreaOfTriangle(a:real; Number:word);forward;

{-------процедура нахождения площади окружности---------}

procedure AreaOfCircle(R:real; Number:word);

{R - радиус окружности, Number - количество оставшихся фигур}

 begin

  writeln('Площадь окружности с радиусом  ',R:5:2,

' равна ',pi*sqr(R):5:2);

  if Number<>0 then {если количество фигур не исчерпано, то рассчитываем площадь треугольника, уменьшая при этом количество фигур}

AreaOfTriangle(sqrt(3)*R,Number-1);

end;

{-------процедура нахождения площади треугольника---------}

procedure AreaOfTriangle(a:real; Number:word);

{  a - сторона треугольника,

Number - количество оставшихся фигур}

 begin

  writeln('Площадь равностороннего треугольника со стороной  ',

a:5:2,' равна ',sqrt(3)*sqr(a)/4:5:2);

  if Number<>0 then {если количество фигур не исчерпано, то рассчитываем площадь окружности, уменьшая при этом количество фигур}

AreaOfCircle(a/(2*sqrt(3)),Number-1);

 end;

{---------------основная программа-----------------}

var FirstR:real;

Number:word;

 begin

  write('Введите радиус первой окружности  '); readln(FirstR);

  write('Введите количество фигур  '); readln(Number);

  AreaOfCircle(FirstR,Number);

  readln;

 end.

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

1.8.5.3   Interrupt объявления

Interrupt объявления используются, когда процедура применяется в качестве замены одного из векторов прерываний DOS или BIOS. Такие процедуры нельзя вызывать с помощью оператора вызова процедуры, количество параметров и их тип строго определены и не могут быть изменены.

1.8.5.4   Внешние объявления (external)

Если процедура или функция описана как external, это означает, что ее тело находится в подключаемом модуле, написанном на языке ассемблера (файл с расширением OBJ). Затем, с помощью директивы компилятора {$L имя_модуля_на_ассемблере}, модуль должен быть подключен.

1.8.5.5   Блок asm

Блок asm используется для описания процедуры или функции, написанной на встроенном ассемблере.

блок_asm::=

"assembler" ";" раздел_объявления asm_оператор.

asm_оператор::=

"asm" операторы_встроенного_ассемб "end".

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

1.8.5.6   Объявление inline

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

Директива_inline::=

"inline" "(" набор_чисел_разделяемых_слешем ")"

Директива inline может использоваться и как оператор.

Задачи для раздела "Процедуры и функции"

Во всех задачах данного параграфа обязательна проверка создаваемых процедур и функций. Использование глобальных переменных внутри процедур и функций не допускается.

Задание 88. Создать функцию, которая возвращает true, если переданное ей число является простым. На ее основе создать процедуру, которая выводит на экран все простые числа от 1 до N, где N - переданное процедуре число.

Задание 89. Создать процедуру, которая по переданному двумерному массиву целых чисел и числам M и N, представляющим собой количество столбцов и строк массива, возвращает одномерный массив, заполненный элементами первого столбца переданного массива.

Задание 90. Создать функцию, которая возвращает true, если в текстовом файле, имя которого передано функции, хотя бы одна из строк является числом.

Задание 91. Создать процедуру, которая в переданной строке заменяет все точки восклицательными знаками, а все восклицательные знаки точками.

Задание 92. Создать функцию, которая возвращает true, если произведение элементов главной диагонали переданной квадратной матрицы равно последнему элементу первого столбца.

Задание 93. Создать функцию, которая возвращает true, если в переданном одномерном массиве длиной N все числа равны.

Задание 94. Создать процедуру, которая обнуляет диагонали переданного двумерного массива, если 4 угловых элемента данного массива равны друг  другу.

Задание 95. Создать процедуру, которая дополняет переданную ей строку таким количеством пробелов, чтобы ее длина была равна числу N, которое также передается в качестве параметра (такая процедура полезна, например, для выравнивания при выводе на экран или в файл столбцов таблицы).

Задание 96[1, № 443]. Даны действительные числа a, b, c, d. Определить площадь пятиугольника, изображенного на рисунке.

Задание 97[1, № 444]. Даны натуральное число n, действительные числа X1,Y1, X2,Y2, ..., Xn,Yn. Найти площадь n-угольника, вершины которого при некотором последовательном обходе имеют координаты (X1,Y1), (X2,Y2), ..., (Xn,Yn) (определить процедуру вычисления площади треугольника по координатам его вершин).


 

1.9  Модули

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

Как и программа, файл текста модуля имеет расширение ".pas". Расширение модуля после компиляции - ".tpu". Все модули, подключаемые к программе, в процессе ее компоновки объединяются друг с другом и с программой в один исполняемый файл с расширением ".exe".

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

Каждый модуль состоит из 3 частей - интерфейсной секции, а также секций реализации и инициализации, которые могут быть пустыми или отсутствовать.

Если модуль содержит в интерфейсной части только переменные, типы и константы, и не содержит процедур и функций, то секция реализации может быть пуста.

Если модуль не требует никаких инициирующих действий, то секция инициализации может отсутствовать.

Модуль::="Unit" ИД_модуля ";"


"Interface"

[подключение_модулей]

Подпись: Интерфейсная секция

{  раздел_объявления_переменных

|  раздел_объявления_констант

|  раздел_объявления_типов

|  раздел_объявления_заг_проц_и_функц}

Подпись: Секция
реализации

"Implementation"

[подключение_модулей]

[раздел_объявлений]

["begin" оператор {";" оператор } [";"]]

 "end" ".".

Подпись: Секция
инициализации

 


ИД_модуля (идентификатор, или имя модуля) всегда должен совпадать с именем файла, в котором хранится модуль.

Для подключения в основной программе или модуле других модулей используется следующий синтаксис (см. также параграф 1.3.3.3):

Подключение_модулей::= "uses" список_модулей ";".

Под списком модулей понимаются имена модулей, перечисленных через запятую.

1.9.1        Интерфейсная секция

В интерфейсной секции модуля находятся: подключение требуемых модулей (то есть тех, на содержимое которых имеются ссылки в данной секции), разделы объявления переменных, типов, констант, а также заголовков процедур и функций, доступных для других модулей и главной программы. Так как метки могут использоваться только внутри того блока, в котором они определены, то их объявление в интерфейсной секции лишено смысла и запрещено синтаксисом ТП.

Объявление заголовков процедур и функций в интерфейсной секции эквивалентно forward и far объявлениям.

Например:

UNIT MyUnit; {заголовок модуля}

 interface {начало интерфейсной секции}

uses Graph; {подключение модуля Graph }

 Const DefaultColor=White;{определение константы, значение которой является, в свою очередь, константой из модуля Graph}

Type Point3D=record x,y,z:real end; {определение типа}

 var Point1,Point2: Point3D;{объявление переменных}

VarR:Real;

 procedure Proc1(x,y:byte);{объявление заголовков процедур и функций}

 function Func:boolean;

implementation {завершение интерфейсной секции, начало секции реализации}

...

1.9.2        Секция реализации

После интерфейсной секции следует секция реализации, предваряемая ключевым словом Implementation.

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

Продолжаем предыдущий пример:

Implementation {начало секции реализации}

uses dos; {подключение модулей, требуемых для данной секции}

var Var2:integer; { объявление внутренних переменных}

Procedure Proc2; { определение внутренних процедур и функций}

begin ... end;

procedure Proc1(x,y:byte);{определение процедур и функций, заголовки которых находятся в интерфейсной секции}

begin ... end;

function Func:boolean;

begin ... end;

... {секция реализации завершена}

Как показано в данном примере, процедура Proc2 является внутренней для модуля и может использоваться только в его пределах. Аналогично переменная Var2 невидима вне модуля MyUnit.

1.9.3        Секция инициализации

Секция реализации заканчивается либо ключевым словом end, либо секцией инициализации в виде составного оператора. В конце модуля, как и в конце программы, ставится точка.

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

Продолжаем предыдущий пример:

Если секция инициализации отсутствует, то модуль закончится так:

 ...

 end.

Если секция инициализации присутствует, то модуль может быть завершен следующим образом:

 ...

 begin

Var2:=122;

VarR:=28.456;

proc2;

 end.

Одним из недостатков секции инициализации является невозможность использовать в ней выделение памяти для динамических переменных.

 

1.9.4        Косвенные ссылки на модули

Косвенные ссылки на модули возникают, например, когда программа или модуль A подключает модуль B, который, в свою очередь, использует модуль C. В этом случае считается, что A имеет косвенную ссылку на модуль С. Хотя содержимое C не будет доступно A, компилятору для создания исполняемого файла потребуются не только A и B, но и модуль C.

Например:

Основная программа A:

program A;

 uses B; {подключаем модуль В}

begin

 writeln(StringFromB); {выводим на экран строку из модуля В}

end.

Модуль B:

unit B;

interface

var StringFromB : string; {строка модуля В}

implementation

uses C; { подключаем модуль С }

begin

{формируем строку для модуля В с использованием строковой константы из модуля С}

 StringFromB:= StringFromC+' и модуля B'

end.

Модуль C:

unit C;

interface

const StringFromC='Это строка из модуля С'; { строковая константа модуля С }

implementation

end.

В результате выполнения данной программы на экране появится фраза:

Это строка из модуляС и модуля B.

Таким образом, не смотря на то, что основная программа использует только модуль В, модуль С также обязан присутствовать на диске при компиляции программы.

 

1.9.5        Циклические ссылки модулей

Циклические ссылки модулей - это ситуация, которая возникает при одновременном подключении двумя или несколькими модулями друг друга. В случае взаимных ссылок между двумя модулей возникают прямые циклические ссылки. Если же три или более модуля ссылаются друг на друга (см. рисунок ниже) то возникают косвенные циклические ссылки.

Например:

Пусть нам требуется автоматизировать вывод сообщений и информацию об ошибках. Создадим для этого два модуля.

Процедуры модуля Display позволяют выводить на экран информацию об ошибках и сообщения, содержимое которых находится в модуле MSConst.

Модуль MSConst содержит два константных строковых массива с информацией об ошибках и сообщениями, а также процедуру, объединяющую в себе возможности процедур модуля Display с проверкой на корректность передаваемого аргумента.

Таким образом, оба модуля ссылаются друг на друга.

 

Модуль Display:

unit Display;

interface

 procedure DisplayError(Num:integer);

 procedure DisplayMessage(Num:integer);

 

implementation

 uses msconst; { циклическая ссылка на модуль msconst }

 procedure DisplayError(Num:integer);

{Процедура DisplayError выводит на экран информацию об ошибке по ее номеру, предваряя словом 'Ошибка!'}

  begin writeln('Ошибка! ',Errors[Num]); end;

 

 procedure DisplayMessage(Num:integer);

{Процедура DisplayMessage выводит на экран сообщение по его номеру, предваряя словом 'Сообщение:'}

  begin writeln('Сообщение: ',Messages[Num]); end;

 

end.

Модуль MSConst:

unit MSConst;

 interface

 const Errors:array[1..3]of string=

      ('Неправильное выражение',

       'Неверный вызов функции',

       'Неверный номер сообщения');

 const Messages:array[4..6]of string=

      ('Программа работает нормально',

       'Программа начала работу',

       'Программа закончила работу');

 procedure ShowInfo(Num:integer);

 

 implementation

  uses display; { циклическая ссылка на модуль display }

 procedure ShowInfo(Num:integer);

{Процедура ShowInfo вызывает процедуры DisplayError и
DisplayMessage
из модуля Display, которые, в свою очередь, используют содержимое массивов из модуля MSConst.}

  begin

   if Num in [low(Errors)..high(Errors)] then DisplayError(Num)

   else

   if Num in [low(Messages)..high(Messages)] then DisplayMessage(Num)

   else DisplayError(high(Errors));

  end;

end.

Основная программа, используемая для проверки возможностей созданных процедур:

uses msConst;

begin

 ShowInfo(1);

 ShowInfo(4);

 ShowInfo(8);

 readln;

end.

В результате выполнения программы на экране появятся 3 строки:

Ошибка! Неправильное выражение

Сообщение: Программа работает нормально

Ошибка! Неверный номер сообщения

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

Если бы при реализации модулей MSConst и Display мы воспользовались циклическими ссылками в интерфейсной секции, откомпилировать бы программу нам не удалось.

 

1.9.6        Пример разработки модуля

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

Постараемся сделать решение независимым от особенностей операционной системы (т.е. воспользуемся процедурой получения текущего времени, а не счетчиком времени из области данных BIOS по адресу $40:$6C).

unit UTimeOut; {модуль поддержки таймера}

                 interface

 Procedure SetTimeOut(TMilli:longint); {Установка счетчика таймера, TMilli - количество миллисекунд ("завели будильник")}

 Function GetTimeOut:boolean; {Возвращает True, если с момента установки таймера прошло не менее чем TMilli миллисекунд ("будильник звенит")}

 procedure SetTimer; {фиксирует счетчик таймера ("запустили секундомер")}

 function GetTimer:longint; {возвращает количество миллисекунд, прошедших с момента фиксации счетчика }

 procedure Wait(M:longint); {задержка на M миллисекунд}

                 Implementation

uses dos; {подключаем модуль Dos для доступа к процедуре получения текущего времени }

var   Timer:longint;{момент фиксации таймера}

TTmilli:longint;{счетчик таймера}

{------------функция ConvertTime -------------}

function ConvertTime(H,M,S,Milli:integer):longint;

 begin {функция переводит часы, минуты, секунды и миллисекунды в миллисекунды}

  ConvertTime:= longint(h)*0360000+longint(M)*0006000+

longint(s)*0000100+longint(Milli);

 end;

{------------функция GetCurrentTime -------------}

function GetCurrentTime:longint;{возвращает текущее время в миллисекундах}

   var h,m,s,milli:word;

  begin

   GetTime(h,m,s,milli);{вызов процедуры из модуля Dos для получения текущего времени}

   GetCurrentTime:=ConvertTime(h,m,s,milli);

  end;

{------------процедура Wait -------------}

procedure Wait(M:longint);

  begin

   SetTimeOut(M);{устанавливаем счетчик}

   while not GetTimeOut do;{ожидаем "звонка"}

  end;

{------------процедура SetTimer -------------}

Procedure SetTimer;

 begin Timer:=GetCurrentTime; end;

{------------функция GetTimer -------------}

function GetTimer:longint;

   var NewTime:longint;

 begin

   NewTime:=GetCurrentTime;{получаем текущее время}

{Рассчитываем количество прошедших миллисекунд с момента фиксации таймера. Учитываем возможность перехода за полночь.}

   if newTime>=Timer then Gettimer:=Newtime-Timer

   else Gettimer:=Newtime-Timer+8640000;

end;

{------------процедура SetTimeOut -------------}

 Procedure SetTimeOut(TMilli:longint);

  begin TTMilli:=TMilli; SetTimer end;

{------------функция GetTimeOut -------------}

Function GetTimeOut:boolean;

 begin

{возвращаем True, если количество времени, прошедшего с момента фиксации таймера, превышает значение счетчика}

   if TTMilli<GetTimer then GetTimeOut:=true

   else GetTimeOut:=false;

 end;

end. {модуль завершен}

Для проверки действия процедур и функций модуля создадим несколько программ.

Задача 51. Сравнить скорость работы циклов For, While и Repeat.

Решение:

uses crt,uTimeOut;

var i:longint;

 begin

  clrscr;

  write('Время работы цикла For ');

  Settimer;

  for i:=1 to 100000000 do;

  writeln(getTimer,' миллисекунд');

  write('Время работы цикла While ');

  Settimer;

  i:=1; while i<=100000000 do inc(i);

  writeln(getTimer,' миллисекунд');

  write('Время работы цикла Repeat ');

  Settimer;

  i:=1; repeat inc(i) until i>100000000;

  writeln(getTimer,' миллисекунд');

  readln;

 end.

В результате выполнения данной программы (компьютер с процессором Pentium-120) на экране появятся три строки:

Время работы цикла For 769 миллисекунд

Время работы цикла While 852 миллисекунд

Время работы цикла Repeat 856 миллисекунд

На самом деле, при каждом запуске программы мы получим чуть отличные результаты, однако общая тенденция достаточно ясна.

Задача 52. Создайте программу, с помощью которой покажите, насколько сильно замерз главный герой одного из стихотворений Некрасова.

Решение проанализируйте сами:

uses crt,uTimeOut;

const TOut:real=1;

s:string='Однажды в студ-д-д-д-еную зимнюю пору '+

'я из-з-з лес-с-су в-в-в-вышел ...';

var i:integer;

 begin

  clrscr;

  for i:=1 to length(s) do begin

   write(s[i]);

   wait(round(TOut));

   TOut:=TOut*1.05;

  end;

  readln;

 end.

 

1.9.7        Стандартные модули (обзор)

Турбо-Паскаль имеет 5 основных модулей: System.tpu, Crt.tpu, Overlay.tpu, Dos.tpu и Printer.tpu.

Данные модули объединены в библиотеку Turbo.tpl, которая для ускорения обращения обычно загружается в память компьютера при запуске среды ТП. В то же время любой из них с помощью утилиты tpumover.exe может быть выделен из библиотеки и использован отдельно.

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

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

Модуль Overlay используется для разработки больших программ, исполняемый код которых не умещается в оперативной памяти компьютера.

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

Очень маленький модуль Printer делает, тем не менее, большую работу. Он обеспечивает взаимодействие с принтером, как с обычным текстовым файлом.

ТП имеет еще несколько модулей, представляющих значительный интерес для программиста. Это модуль Graph, содержащий все необходимое для создания графических программ, модуль Graph3, реализующий "черепашью" графику, модуль WinDos, поддерживающий и расширяющий набор функций модуля Dos c заменой всех стандартных строковых параметров на строки PChar, модуль Strings, содержимое которого раскрыто в параграфе 1.7.2.2, при анализе длинных строк, модуль Objects, в котором сосредоточены процедуры, функции, типы, переменные и константы, предназначенные для поддержки объектно-ориентированного программирования.

Хотелось бы упомянуть еще библиотеку Turbo Vision, в состав которой входит и модуль Objects. Благодаря ей можно создавать событийно-ориентированные программы с многооконным текстовым интерфейсом.

 

Задачи для раздела "Модули"

Задание 98. Создать 3 модуля и основную программу. Первый модуль содержит 3 переменных в интерфейсной части, инициализируемых в его секции инициализации. Второй модуль содержит функцию, возвращающую сумму трех переменных из первого модуля. Третий модуль содержит функцию, возвращающую произведение трех переменных из первого модуля и результата, возвращаемого функцией из второго модуля. Основная программа выводит на экран значение функции из третьего модуля.

Задание 99. Создать 2 модуля и основную программу.  Первый  модуль  содержит  массив из пяти элементов в интерфейсной  части,  инициализируемых случайными числами в его секции инициализации. Второй модуль со держит функцию, возвращающую среднее  арифметическое  массива  из  первого модуля. Основная программа выводит на экран значение функции из второго модуля.

Задание 100. Создать 2 модуля и основную программу. Первый модуль  содержит массив из N элементов в интерфейсной части (где N - константа, определенная также в интерфейсной части), инициализируемых случайными числами в его секции инициализации. Второй модуль  содержит функцию, возвращающую сумму N элементов массива из первого модуля. Основная программа выводит на экран значение функции из второго модуля.

Задание 101. Создать 2 модуля и основную программу. Первый модуль содержит целочисленную переменную, инициализируемую вводимым с клавиатуры числом в секции инициализации. Второй модуль содержит процедуру, выводящую на экран слово "один", если значение этой переменной = 1, слово "два", если значение этой переменной = 2 и т.д. до значения переменной = 9. Если значение переменной не входит в исследуемый диапазон, то процедура выводит на экран "переменная вне диапазона". Основная программа вызывает процедуру из второго  модуля.

Задание 102. Создать модуль распространенных арифметических функций, отсутствующих в ТП (см. параграф 1.5.1.1).

Задание 103. Создать модуль функций перевода из числа в строку и обратно (см. параграф 1.8.2)

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

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


 

Список литературы.

1.   Абрамов С.А., Гнездилова Г.Г., Капустина Е.Н., Селюн  М.И. Задачи по программированию. - М.: Наука, 1988. - 224 с.

2.   Вирт Н. Алгоритмы + структуры данных = программы / Пер.  с англ. - М.: Мир, 1985. - 406 с., ил.

3.   Вирт Н. Систематическое программирование. Введение /  Пер. с англ. - М.: Мир, 1977. - 183 с.

4.   Вьюкова Н.И., Галатенко В.А., Ходулев А.Б. Систематический подход к программированию. - М.: Наука, 1988. - 208 с.

5.   Епанешников А., Епанешников В. Программирование в среде Turbo-Pascal 7.0. - 4-е изд. испр. и дополн. - М.:"Диалог-МИФИ", 1998. - 367с.

6.   Справочник программиста и пользователя/Под ред. А.Г. Шевчика, Т.В. Демьянкова. - М.: "Кварта", 1993. - 128с.

7.   Толковый словарь по вычислительным системам/Под ред. В.Иллингуорта и др.: Пер. с англ. А.К. Белоцкого и др.; Под ред. Е.К. Масловского. - М.: Машиностроение, 1990. - 560с.: ил.


 

 

 

 

 

 

Учебное пособие для студентов вузов

 

 

 

 

 

 

 

 

 

Слинкин Дмитрий Анатольевич

 

Программирование.

ЧАСТЬ 1

язык программирования турбо-паскаль

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Лицензия ЛР № 020057 от 7 апреля 1997 г.

Формат 60´90 / 1/16, усл. печ. л. 4.4, тираж 100 экз.