Министерство общего и профессионального образования Российской Федерации Воронежская государственная лесотехническая ака...
3 downloads
226 Views
406KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Министерство общего и профессионального образования Российской Федерации Воронежская государственная лесотехническая академия
ИНФОРМАТИКА Основы программирования на языке Паскаль Тексты лекций Часть 2
Воронеж − 1999
УДК 519.682.1 Стариков А.В. ИНФОРМАТИКА. Основы программирования на языке Паскаль : Тексты лекций в 3-х частях. Часть 2. Воронеж, 1999. − 64 с.
Вторая часть текстов лекций содержит информацию о структурных типах данных, файловом вводе-выводе, подпрограммах и механизмах передачи параметров в подпрограммы. Тексты лекций по основам программирования на языке Паскаль предназначены для студентов специальности 060800 − “Экономика и управление на предприятиях лесного комплекса”. Ил. 3. Табл. 2.
Печатается по решению редакционно-издательского совета ВГЛТА
Рецензенты:
кафедра прикладной математики и экономико-математических методов ВГТА, д-р техн. наук, проф. ВГТУ Кравец О.Я.
УДК 519.682.1 Стариков А.В., 1999 Оформление. Воронежская государственная лесотехническая академия, 1999
3 Лекция 6. Составные типы данных языка Паскаль. Регулярные типы (массивы). Примеры программ, использующих регулярные типы данных. Строковый тип данных (строки символов). Пример программы, использующей строковый тип данных. Составные типы данных До сих пор мы рассматривали простые типы данных (порядковые и вещественные), которые не имеют выраженной структуры, а определяют различные множества унитарных (или атомарных), т.е. не поддающихся дальнейшему дроблению, значений. Однако в Паскале предусмотрена возможность создания и использования так называемых составных (сложных или структурных) типов, которые состоят из других типов – подобно тому, как составные операторы состоят из других операторов. В данной лекции мы рассмотрим два таких типа: регулярный тип (или массив) и строковый тип (или строка символов). Массивы Массив – это одна из наиболее распространенных структур данных, использующихся в языках программирования. Массив представляет собой регулярную структуру данных, так как все элементы массива имеют один и тот же тип, называемый базовым типом. Кроме того, массив является также структурой с так называемым произвольным доступом, поскольку все его элементы одинаково доступны и могут выбираться в произвольном порядке. Для обозначения отдельного элемента к имени всего массива (групповое имя) добавляется индекс, позволяющий выбрать необходимый элемент. Для корректного определения регулярного типа (массива) требуется указать две характеристики: тип элементов массива (базовый тип) и тип индекса массива (последняя характеристика определяет количество и “способ нумерации” элементов массива). Определение регулярного типа имеет следующий вид: type регулярный-тип = array [тип-индекса] of базовый-тип; где регулярный-тип – определяемый регулярный тип (массив), тип-индекса – тип индекса массива, базовый-тип – тип элементов массива. Слова array и of являются зарезервированными (служебными) словами языка Паскаль. В качестве типа индекса может выступать любой дискретный тип, кроме longint и ограниченных типов, построенных из типа longint; в частности, допустимы перечислимые и ограниченные типы данных. Элементы массива могут иметь любой тип данных. Например, type Direction = (x, y, z);
4 Vector = array [Direction] of real; var v : Vector; Norm : real; begin v[x] := 1.5; v[y] := 2.1; v[z] := 3.7; Norm := sqr(v[x])+sqr(v[y])+sqr(v[z]); { Норма вектора } writeln(Norm) end. Типом индексов регулярного типа Vector (вектор) является перечислимый тип Direction (направление), типом элементов массива – real (вещественный). Отметим некоторые особенности массивов в языке Паскаль. В Паскале количество элементов массива всегда должно быть фиксировано, т.е. определено при компиляции программы. С одной стороны это можно расценивать как некоторый недостаток языка, поскольку не во всех программах можно заранее предсказать необходимый размер массива (который может определяться в зависимости от тех или иных условий, возникающих в процессе исполнения программы) и поэтому приходится выделять память под массив с “запасом”. С другой стороны подобное ограничение обеспечивает возможность более строгого контроля программы на этапе компиляции, что способствует, в конечном итоге, повышению ее надежности. В Паскале для задания количества элементов массива используется тип данных индекса. Количество элементов массива определяется множеством значений указанного типа, что отличает Паскаль от большинства других языков программирования, в которых размер массива задается либо целым числом (или выражением целого типа), либо диапазоном целых чисел. Такая особенность языка Паскаль придает ему дополнительную гибкость, позволяя “нумеровать” элементы массива не только целыми числами, но и значениями произвольного дискретного типа. Например, type LogicTable = array [char] of boolean; var Code : integer; Alpha : LogicTable; { Булевский массив, у которого индексом является символьное значение } begin for Code := 0 to 255 do if ((chr(Code) >= 'A') and (chr(Code) = 'a') and (chr(Code) Max then Max := List[Index]; if List[Index] < Min then Min := List[Index] end; writeln('Min = ', Min); writeln('Max = ', Max) end. Примечание. Эту программу можно реализовать более эффективно, если совместить циклы ввода и поиска минимального и максимального элементов, т.е. записать ее так, как показано ниже. { Поиск мин. и макс. значений в массиве − модифицированная версия } program MinMax; const MaxSize = 20; { Количество элементов массива } type ListSize = 1..MaxSize; var Index : ListSize; { Индекс элемента массива } Min, Max : integer; List : array [ListSize] of integer; begin Min := MaxInt; Max := -MaxInt; for Index := 1 to MaxSize do { Ввод значений элементов массива } begin write('List[',Index:2,']= ');
8 readln(List[Index]) if List[Index] > Max then Max := List[Index]; if List[Index] < Min then Min := List[Index] end; writeln('Min = ', Min); writeln('Max = ', Max) end. Задача 4. Так же, как и в предыдущей задаче, отыскать минимальный и максимальный элемент массива, но дополнительно указать их местоположение в массиве, т.е. распечатать их индексы. { Поиск минимального и максимального значений элементов массива } program MinMax2; const MaxSize = 20; { Количество элементов массива } type ListSize = 1..MaxSize; var Index : ListSize; { Индекс массива } Min, Max : integer; Imax, Imin : 1..ListSize; List : array [ListSize] of integer; begin for Index := 1 to MaxSize do { Ввод значений элементов массива } begin write('List[',Index:2,']= '); readln(List[Index]) end; Min := List[1]; Max := Min; for Index := 2 to MaxSize do begin if List[Index] > Max then begin Max := List[Index]; Imax := Index end; if List[Index] < Min then begin Min := List[Index]; Imin := Index end end; writeln('Min = ', Min, ' Imin = ', Imin); writeln('Max = ', Max, ' Imax = ', Imax)
9 end. В задачах 3 и 4 поиск выполнялся в неупорядоченных списках. Часто списки делаются упорядоченными, т.е. значения, которые в них содержатся, организуются согласно алфавитному или цифровому порядку. Поиск в упорядоченных списках может быть выполнен гораздо эффективнее, чем в неупорядоченных списках. Упорядочение информации в списке называется сортировкой. Если значения в списке увеличиваются с начала списка к его концу, то говорят, что список отсортирован в порядке возрастания (или по возрастанию) значений; если значения уменьшаются, то говорят, что список отсортирован в порядке убывания (или по убыванию) значений. Существует множество методов сортировки списков. Ниже приведены тексты двух программ, выполняющих сортировку списка значений по возрастанию и поиск заданной величины в отсортированном списке. Задача 5. Пусть дан числовой массив x1, x2, ..., xn, элементы которого попарно различны. Используя алгоритм сортировки выбором, упорядочить элементы массива по возрастанию, т.е. так, чтобы x1 < x2 < ...< xn. Алгоритм сортировки выбором основывается на том, что каждый раз в подсписке (первоначально − это весь список) отыскивается минимальный элемент и перемещается в начало списка; при этом подсписок для очередного поиска укорачивается на 1, т.е. из него исключается уже использованный элемент. program SelectSort; const N = 10; { Количество элементов списка } var List : array [1..N] of real; Temp : real; i, j, k : 1..N; begin for i := 1 to N do { Ввод элементов списка } begin write('List[', i, ']='); read(List[i]) end; for i := 1 to N do { Сортировка элементов списка } begin k := i; for j := i+1 to N do if List[j] < List[k] then k := j; Temp := List[i]; { Минимальный в начало подсписка } List[i]:=List[k]; List[k] := Temp end; for i := 1 to N do { Вывода элементов отсортированного списка } writeln('List[', i, ']=', List[i])
10 end. Задача 6. Написать программу для отыскания указанного элемента в упорядоченном списке значений, т.е. эта программа должна либо указать местоположение значения в списке, либо выдать сообщение о том, что такое значение в списке отсутствует. Для решения задачи используем метод двоичного поиска, который основывается на последовательном разбиении области поиска (первоначально − это весь список) на два равных интервала, сравнении элемента в точке разбиения с заданным значением и, если они различны, выборе очередного интервала для разбиения. Процесс деления области поиска заканчивается в двух случаях, а именно: 1) область поиска “стянута” в точку, т.е. далее не может подвергнуться делению на интервалы, или 2) в списке обнаружен искомый элемент. program BinarySearch; const N = 10; { Количество элементов в списке } var List : array [1..N] of real; { Список значений } Low, High : 1..N; { № элементов, образующих границы интервала } Found : boolean; { Флажок “Элемент найден” } X : real; { Для хранения введенного значения } Pos : 1..N; { Индекс “срединного” элемента } begin for i := 1 to N do { Ввод упорядоченных элементов списка } read(List[i]); read(X); { Ввод искомого значения } Low := 1; { Первоначально область поиска - весь список } High := N; Found := FALSE; { Искомый элемент пока не обнаружен } { Пока область поиска не “стянута” в точку и не найдено введенное значение выполнять ... } while (Low List[Pos] then { справа } Low := Pos + 1 { Исключить левую половину интервала } else Found := TRUE { Элемент найден! } end; if Found then { Если элемент найден, то ... }
11 writeln('Элемент ', X, ' находится в ', Pos, '-й позиции списка ') else { Элемент не найден } writeln('Элемент ', X, ' отсутствует в списке ') end. Строковый тип Одним из наиболее полезных и часто используемых расширений стандартного языка Паскаль, реализованных в Турбо-Паскале, является строковый тип данных. Строковый тип данных обобщает понятие символьных массивов (array [1..N] of char) за счет обеспечения возможности динамического изменения длины символьной строки. Строковый тип данных определяет множество символьных последовательностей произвольной длины (от 0 символов до заданного количества). В определении строкового типа данных используется зарезервированное слово string (строка), вслед за которым в квадратных скобках указывается максимальная длина строки; например: type Line : string [80]; var ScreenLine : Line; В данном примере строковая переменная ScreenLine может иметь в качестве своего значения любую последовательность символов, длина которой лежит в диапазоне от 0 до 80 символов. Любое из этих значений может быть присвоено строковой переменной ScreenLine в операторе присваивания или введено с клавиатуры: ScreenLine := 'Это пример строки'; readln(ScreenLine); Максимальная длина символьной строки, определенная в Турбо-Паскале, равна 255 символам. Если в определении строки длина опущена, то в этом случае подразумевается (говорят также – используется по умолчанию) число 255, т.е. следующие два определения строковых типов эквивалентны: type Line1 = string; Line2 = string [255]; Важнейшее отличие символьных строк от одномерных символьных массивов состоит в том, что строки могут динамически изменять свою длину. Например, если после выполнения оператора присваивания Line1 := 'Короткая строка'; длина строки Line составляет 15 символов, то следующий оператор присваивания: Line1 := Line1 + ' стала длиннее'; увеличит ее длину до 29 символов.
12 Если строковой переменной присваивается строковое выражение с длиной, превышающей максимально допустимую длину для данной переменной, то происходит отсечение строки до максимально возможной. Эта ситуация не расценивается как ошибочная и выполнение программы не прерывается; например, следующая программа: var ShortStr : string [5]; begin ShortStr := 'Очень длинная строка'; writeln(ShortStr) end. выведет на экран слово “Очень”. Механизм динамических строк, реализованный в Турбо-Паскале, несложен и заключается в том, что память под переменные строкового типа отводится по максимуму, а используется лишь часть этой памяти, реально занятая символами в данный момент. Так, например, при задании в программе строковой переменной с длиной, равной N символам, компилятор распределяет для нее N+1 байт памяти, из которых N байт предназначены для хранения символов строки, а один байт – для значения текущей длины строки (рис. 3).
0 1
2 3 4
5
6 7
...
k k+1 ... n-1 n
xx xx xx xx xx xx xx
...
xx
занятая часть строки
... незанятая часть строки
значение k – текущая длина строки Рис. 3. Байты (символы) в строке нумеруются целыми числами, начиная с единицы. Иногда, правда, используют нулевой байт строки, содержащий текущую длину, например: L := Ord(Line[0]); хотя для этой же цели целесообразнее использовать стандартную функцию Length, которая возвращает целое значение – длину строкового параметра, т.е. L := Length(Line); Кроме операции конкатенации над значениями строкового типа выполняются операции отношения (сравнения): < меньше больше >= больше или равно
13 = равно не равно. При этом выполняются следующие правила: 1) если длины сравниваемых строк различны, то более короткая строка меньше более длинной; 2) если длины сравниваемых строк равны, то происходит посимвольное сравнение этих строк с учетом лексикографической упорядоченности значений стандартного символьного типа char. Доступ к отдельным элементам строк производится аналогично доступу к элементам одномерного символьного массива, т.е. после имени строковой переменной в квадратных скобках необходимо указать выражение целого типа, обозначающее номер элемента строки. Такая конструкция имеет тип char и является переменной, т.е. она может находиться в левой части оператора присваивания. Например: var MyLine : string; i, Count : integer; begin readln(MyLine); for i := 1 to Length(MyLine) do if ((Myline[i]>='A') and (Myline[i]='a') and (Myline[i] case MaritalStatus of married: (< Поля, присущие состоящим в браке >); single: (< Поля, присущие одиноким >); ... end; Обычно некоторое поле записи указывает, о каком варианте идет речь. Это поле, общее для всех вариантов, называется полем признака и помещается в заголовок варианта. Например, case Status : MaritalStatus of ... Прежде чем определять структуру записи с вариантной частью, полезно бывает выписать всю необходимую информацию. В случае типа Person такой информацией может являться: 1. Имя (Name) – фамилия, имя, отчество (First, Second, Last). 2. Рост (Height) – целое число. 3. Пол (Sex) – мужской, женский (male, female). 4. Дата рождения (Date of birth) – день, месяц, год (Day, Month, Year).
20 5. Число иждивенцев (Dependents) – целое число. 6. Семейное положение (Marital status): если в браке (married) или вдов (widowed): дата свадьбы (Date of marriage) – день, месяц, год (Day, Month, Year). если разведен (divorced): a) дата развода (Date of divorce) – день, месяц, год (Day, Month, Year); b) первый развод (first divorce) – нет, да (false, true). если одинокий (single): пусто. На рис.4 показаны "информационные образы" двух человек с различными атрибутами. Сидорова Тамара П етровна 169 жен 27 июнь 1970 2 разведена 17 апрель 1994 да
И ванов И горь Семенович 176 муж 12 сентябрь 1972 0 холост
Рис.4. Используя приведенную выше информацию, можно определить следующую запись Person с вариантной частью: type MaritalStatus = (Married, Widowed, Divorced, Single); Date = record Day : 1..31; Month : (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec); Year : 1900..2100 end; { Date } Natural = 0..MaxInt; Person = record Name : record First, Second, Last : string [15] end; { Name } Height : Natural; { в сантиметрах }
21 Sex : (Male, Female); Birth: Date; Depdts : Natural; case Status : MaritalStatus of Married, Widowed : (Mdate : Date); Divorced : (DDate : Date; FirstD : Boolean); Single : () end; { Person } При использовании записей с вариантами следует иметь в виду следующее: 1. Имена всех полей должны быть различными, даже если они встречаются в различных вариантах. 2. Если вариант пустой (т.е. не содержит поля), он записывается в виде "метка-варианта : ()". 3. Для того, чтобы некоторое поле в записи могло выступать в качестве поля-признака (говорят также – в качестве дискриминанта записи), его тип должен задаваться именем типа. Другими словами, можно использовать в вариантной части записи case Status : MaritalStatus of, как показано выше, но нельзя − case Status : (Married, Widowed, Divorced, Single) of . 4. Любой список полей может иметь только одну вариантную часть, которая должна следовать за фиксированной частью записи, т.е. помещаться в конце записи. 5. Каждый вариант, в свою очередь, может содержать в себе вариантную часть, т.е. допускаются вложенные варианты. 6. Область действия имен констант перечислимого типа, вводимых в записи, расширяется на все вложенные блоки. Оператор присоединения (WITH) Оператор присоединения предназначен для того, чтобы сделать запись группы операторов, манипулирующих с полями определенной записи, более наглядной и, возможно, более компактной. Фактически, оператор WITH открывает область действия, содержащую имена полей указанной записи, так что в пределах этой области имена полей могут использоваться как обычные (не составные) имена. Например, имея в виду введенное выше определение типа Person, можно записать следующее: var Student: Person; { См. выше определение типа Person } begin with Student do begin
22 with Name do begin First := 'Иванов'; Second := 'Иван'; Last := 'Иванович' end; Height := 176; Sex := Male; with Birth do begin Day := 12; Month := Sep; Year := 1978 end; Depdts := 0; Status := Single end; ... end. вместо того, чтобы использовать следующую запись: var Student: Person; begin Student.Name.First := 'Иванов'; Student.Name.Second := 'Иван'; Student.Name.Last := 'Иванович'; Student.Height := 176; Student.Sex := Male; Student.Birth.Day := 12; Student.Birth.Month := Sep; Student.Birth.Year := 1978; Student.Depdts := 0; Student.Status := Single; ... end. Операторы присоединения могут вкладываться в друг в друга (см. выше). Вложенные операторы присоединения допускают сокращенную запись, т.е. оператор вида: with R1 do with R2 do with R3 do ... with Rn do S
23 можно записать в виде: with R1, R2, R3, ..., Rn do S т.е. приведенный выше пример вложенных операторов WITH, можно переписать следующим образом: with Student, Name, Birth do begin First := 'Иванов'; Second := 'Иван'; Last := 'Иванович'; Height := 176; Sex := Male; Day := 12; Month := Sep; Year := 1978; Depdts := 0; Status := Single end; Вопросы и упражнения для самопроверки 1. Определить комбинированный тип для представления следующих понятий: − цена в рублях и копейках; − время в часах, минутах и секундах; − адрес (город, улица, дом, квартира); − бланк требования на книгу (сведения о книге: шифр, автор, название, год издания; сведения о читателе: № читательского билета, фамилия; дата заказа); − экзаменационная ведомость (название предмета, фамилия преподавателя, номер группы, дата экзамена, 25 строчек с полями: фамилия студента, № его зачетной книжки, оценка за экзамен). 2. Имеются следующие описания: type Circle = record Radius : real; Center : record x, y : real end end; var K : Circle; Требуется переменной K присвоить значение соответствующее кругу радиуса 2.5 с центром в точке (0,1.8). В каких из приведенных ниже операторов присоединения правильно решается эта задача, а в каких нет и почему? a) with K do
24 begin Radius := 2.5; x := 0; y := 1.8 end; б) with K do begin Radius := 2.5; Center.x := 0; Center.y := 1.8 end; в) with K do begin Radius := 2.5; with Center do begin x := 0; y := 1.8 end end; г) with K, Center do begin Radius := 2.5; x := 0; y := 1.8 end; д) with Center, K do begin Radius := 2.5; x := 0; y := 1.8 end; 3. Определить запись с вариантной частью Person, которая позволила бы представить следующие сведения о студентах: фамилия, имя, отчество, дата рождения, номер группы; для девушек: цвет глаз (голубые, зеленые, серые, карие, черные) и цвет волос (блондинка, шатенка, брюнетка); для юношей: рост (в см) и вес (в кг). 4. Написать программу, которая позволила бы заполнить массив из 10 записей типа Person (см. выше упражнение 3), а затем выполнить вывод части содержимого этого массива: фамилия и цвет глаз и волос (для девушек) или рост и вес (для юношей). Лекция 8. Ввод-вывод в программах на языке Паскаль. Файловый тип данных. Операции над файлами. Начальные и завершающие операции ввода-вывода. Операции ввода-вывода. Операции перемещения по файлу. Специальные операции ввода-вывода. Примеры программ, использующих операции вводавывода. Вопросы и упражнения для самопроверки. Ввод-вывод в программах на языке Паскаль Паскаль, как и большинство других развитых языков программирования, содержит средства для организации хранения информации на внешних запоминающих устройствах (ВЗУ) и доступа к этой информации. В случае ПК основными ВЗУ являются диски (главным образом, жесткие и гибкие магнитные диски, хотя в последнее время все большую популярность приобретают компактные и чрезвычайно емкие лазерные диски – CD-ROM'ы). Информация в ВЗУ хранится в виде файлов. Файл – это область на носителе информации ВЗУ, хранящая некоторую совокупность данных. Средства, обеспечиваемые языком программирования, позволяют поместить данные в файл, т.е. записать их на носитель информации ВЗУ, и прочитать их с носителя. Все эти действия по записи данных в файл и чтению их из файла имеют общее название – ввод-вывод.
25
Файловый тип данных Таким образом, ввод, вывод и хранение информации в компьютере осуществляется с помощью файлов. В Паскале файл представляется в виде переменной. Это несколько необычная переменная, поскольку она может существовать как до, так и после выполнения программы и, кроме того, она может быть намного больше, чем сама программа. В силу этих причин, действия, которые можно производить в программе на языке Паскаль над файлами, значительно ограничены. Применительно к языку Паскаль понятие “файл” обозначает информационный объект, образованный последовательностью компонент одного и того же типа. Сама последовательность устанавливает естественный порядок следования компонент, причем в каждый момент времени доступна только одна компонента. Другие компоненты файла становятся доступны по мере продвижения по файлу. Количество компонент, называемое длиной файла, в определении файлового типа не фиксируется. Эта особенность явно отличает понятие файла от понятия массива. Если файл не имеет компонент, его называют пустым. Таким образом, любой файловый тип данных представляет собой объединение последовательности компонент, имеющих один и тот же тип данных (базовый тип файла), некоторого положения в этой последовательности (позиции) и режима, указывающего, формируется (записывается) файл или просматривается (читается). Определение файлового типа данных может быть записано в общем виде следующим образом: type файловый-тип = file of базовый-тип-файла; Описание файловой переменной в этом случае может быть представлено как var имя-файловой-переменной : файловый-тип; Определение файлового типа и описание файловой переменной может быть объединено в одном описании, имеющем следующий вид: var имя-переменной : file of базовый-тип-файла; Примеры описания файловых переменных: var InFile : file of integer; Table : file of string [80]; Data : file of real; DataBase : file of Person; {Определение типа Person см. в лекции 7}
26 Операции над файлами Операции над файлами в Турбо-Паскале можно разбить на четыре основные группы: • начальные и завершающие операции ввода-вывода; • собственно операции ввода-вывода; • операции перемещения по файлу; • специальные операции ввода-вывода. Рассмотрим операции этих групп более подробно. Начальные и завершающие операции В эту группу входят четыре операции, реализованные стандартными процедурами, имеющими следующие имена: Assign, Reset, Rewrite, Flush и Close. Процедура Assign(имя-файловой-переменной, спецификация-файла) определена в модуле System и предназначена для установления связи между файловой переменной, заданной первым параметром − имя-файловой-переменной, и конкретным физическим файлом, находящимся на магнитном носителе ВЗУ и заданным вторым параметром − спецификация-файла. Например: uses System; { Можно не указывать, используется по умолчанию } var IntFile : file of integer; { Файл целых чисел } SymFile : file of char; { Символьный файл } FileName : string [12]; begin Assign(IntFile, 'd:\gr711\turbo\pas\myfile.dat'); ... write('Имя файла? (имя.расширение) '); readln(FileName); Assign(SymFile, FileName); ... end. После выполнения первого вызова процедуры Assign файловая переменная IntFile будет связана с файлом myfile.dat, находящимся на диске D: в подкаталоге \gr711\turbo\pas. После выполнения второго вызова процедуры Assign файловая переменная SymFile будет связана с файлом, имя и расширение имени которого были введены пользователем с клавиатуры в ответ на полученную подсказку. В качестве второго параметра процедуры Assign допускаются имена устройств, рассматриваемых в DOS в качестве файлов, а именно: 'CON' – консоль: экран в случае вывода информации и клавиатура в случае ввода;
27 'LPT1', 'LPT2', 'LPT3' – печатающие устройства (допускается одновременно до трех печатающих устройств). Эти устройства предназначены только для вывода информации; 'PRN' – синоним 'LPT1'; 'COM1', 'COM2' – последовательные коммуникационные порты. Смысл этих файлов определяется конкретными устройствами, подключенными к этим портам. 'AUX' – синоним 'COM1'; 'NUL' – фиктивное, несуществующее устройство. Может быть использовано для вывода информации "в никуда" в тех случаях, когда в программе по какой-либо причине нужно указать имя выходного файла, а информация, записываемая в него, не требуется. После того, как с помощью процедуры Assign установлена связь между файловой переменной, описанной в программе, и внешним файлом, находящимся на диске, необходимо подготовить файловую переменную к вводувыводу (или как говорят – открыть файл). Это выполняется с помощью процедуры Reset или Rewrite, определенных в модуле System. Процедура Reset(имяфайловой-переменной) предназначена для открытия существующего файла. Под открытием в данном случае понимается поиск файла на внешнем носителе, назначение специальных системных буферов для обмена информацией с ним и установка текущего указателя файла на начало файла. Если файл на диске не существует, то возникает ошибка. Процедура Rewrite(имя-файловой-переменной) допускает, что открываемый файл может не существовать; в этом случае она создает его. Если же файл существует, то процедура Rewrite очищает его. В обоих случаях текущий указатель файла устанавливается на начало файла. Примеры использования процедур Reset и Rewrite: Assign(IntFile, 'd:\gr711\turbo\pas\myfile.dat'); Reset(IntFile); ... Assign(SymFile, FileName); Rewrite(SymFile); ... Примечание. В начале выполнения программы всегда автоматически открываются стандартные текстовые файлы Input (ввод) и Output (вывод). Input – это доступный только для ввода (чтения) файл, связанный с клавиатурой, а Output – это доступный для вывода (записи) файл, связанный с экраном. О текстовых файлах (тип данных text) см. ниже Лекцию 8. Процедура Flush(имя-файловой-переменной) определена в модуле System и предназначена для завершения обмена информацией с файлом без его закрытия. Обмен с файлом всегда реализуется через некоторый буфер в оперативной памяти, поэтому в процессе записи в файл последние записываемые данные мо-
28 гут еще находиться в этом буфере. Процедура Flush вызывает принудительный "сброс" этих данных в файл. Процедура Close(имя-файловой-переменной) определена в модуле System и предназначена для завершения действий с файлом (говорят – для закрытия файла). При этом освобождаются внутренние буферы, закрепленные за файлом при его открытии. После этого файловую переменную можно связать с какимнибудь другим файлом на диске, используя процедуру Assign. Следует отметить, что при окончании работы программы происходит автоматическое закрытие всех файлов, открытых в программе. Тем не менее, хорошим правилом считается закрытие файлов с помощью процедуры Close после окончания работы с ними. Например: ... Close(SymFile); ... Close(IntFile); ... Операции ввода-вывода В эту группу входят две операции, выполняющие чтение информации из файла и запись информации в файл и реализованные процедурами Read и Write соответственно. Процедура Read(имя-файловой-переменной, имя-переменной-1 [, имяпеременной-2, ..., имя-переменной-N]) определена в модуле System и предназначена для чтения значений из файла в переменные программы. Первым параметром процедуры Read должно быть имя файловой переменной, к которой предварительно была применена одна из операций открытия файла (Reset или Rewrite). Далее должны быть указаны переменные, в которые будут помещаться значения, считываемые из файла. Тип этих переменных должен совпадать с базовым типом файловой переменной, заданной в качестве первого параметра. Выполнение процедуры Read происходит следующим образом. Начиная с текущей позиции указателя файла, будут последовательно читаться значения, содержащиеся в файле. Каждое прочитанное значение будет присваиваться очередной переменной из тех, которые указаны в вызове процедуры. После считывания каждого значения указатель файла будет автоматически перемещаться на следующую позицию в файле. Примечание. Выше в записи формата вызова процедуры Read использованы квадратные скобки, идентифицирующие необязательные параметры, т.е. параметры, которые могут быть опущены при вызове процедуры (сами квадратные скобки, естественно, в операторе вызова процедуры не указываются).
29 При движении вдоль файла указатель текущей позиции в файле может достигнуть конца файла. Эту ситуацию можно проконтролировать с помощью предопределенной (стандартной, встроенной) логической функции EoF(имяфайловой-переменной), которая вырабатывает значение True при достижении указателем конца файла (End Of File) и False – во всех других случаях (см. ниже). Пример использования вызова процедуры Read: ... while not eof(IntFile) do { Пока не конец файла } begin read(IntFile,IntNum); { Прочитать целое число из файла } writeln(IntNum) { Вывести это число на экран } end; ... Процедура Write(имя-файловой-переменной, выражение-1 [, выражение2, ..., выражение-N]) определена в модуле System и предназначена для записи информации из программы в файл, т.е. по смыслу она противоположна процедуре Read. Первым параметром процедуры Write должно быть имя файловой переменной, предварительно открытой с помощью процедуры Rewrite. Далее должен следовать список выражений, тип которых совпадает с базовым типом файловой переменной, указанной в качестве первого параметра. Выполнение процедуры Write происходит следующим образом. Вычисляется значение очередного выражения и помещается в файл на место, указанное текущим указателем. После этого указатель автоматически сдвигается к следующей позиции в файле и действия повторятся для следующего выражения из тех, что указаны в списке вызова процедуры Write. Пример использования вызова процедуры Write: ... for i := 1 to 100 do { В цикле выполнить 100 раз } begin read(Symbol); { Прочитать символ с клавиатуры } write(SymFile, Symbol) { Записать этот символ в файл } end; ... Операции перемещения по файлу Данная группа операций предоставляет дополнительные возможности, позволяющие изменять последовательный порядок выполнения операций чтения и записи. Эта группа объединяет две процедуры − Seek и Truncate − и три функции − FileSize, FilePos и упоминавшуюся ранее EoF. Все эти процедуры и функции определены в модуле System и оперируют непосредственно с текущим указателем файла. Процедура Seek(имя-файловой-
30 переменной, номер-позиции-в-файле) перемещает указатель в файле, заданном первым параметром, на позицию с номером, указанным вторым параметром. После выполнения процедуры Seek дальнейшие операции чтения или записи в файле будут производиться, начиная с установленной позиции указателя. Процедура Truncate(имя-файловой-переменной) определена в модуле System и предназначена для отсечения хвостовой части в файле, заданном в качестве параметра этой процедуры. Усечение файла начинается с позиции, указанной указателем файла. Функции FileSize(имя-файловой-переменной) и FilePos(имя-файловойпеременной), возвращающие длинное целое, определены в модуле System и позволяют получить дополнительную информацию о файле, указанном в качестве их единственного параметра. Функция FileSize возвращает общее количество элементов в файле, т.е. длину файла, а функция FilePos – номер элемента, на который установлен текущий указатель, т.е. текущую позицию файла. Используя эти функции, можно организовать достаточно сложные алгоритмы работы с файлами. Здесь мы приведем лишь несколько простых примеров: Seek(IntFile,FilePos(IntFile)+1) { Пропуск одного числа в файле } Seek(SymFile,0) { Установка указателя на начало файла } Seek(SymFile,FileSize(SymFile)) { Установка указателя непосред-ственно за последним символом файла (возмо-жно для добавления символов в "хвост" файла) } Булевская функция EoF(имя-файловой-переменной) также определена в модуле System; она возвращает логическое (булево) значение True, если в ходе перемещения по файлу был достигнут конец файла, и False – в противном случае. Так, например, если файл использовался для чтения, то возникновение ситуации "конец файла" (и, соответственно значение True, возвращаемое функцией EoF) означает, что все элементы файла прочитаны. При записи в файл истинность функции EoF будет означать, что очередная операция записи поместит новый элемент в конец данного файла. Специальные операции ввода-вывода Данная группа операций предназначена для действий с элементами файловой системы MS-DOS – каталогами и именами файлов, позволяя создавать и удалять каталоги, удалять и переименовывать файлы и т.д. Приведем краткое описание процедур, реализующих эти операции (все они определены в модуле System). Процедура Erase(имя-файловой-переменной) удаляет указанный в качестве параметра файл с диска.
31 Процедура Rename(имя-файловой-переменной, новое-имя-файла) переименовывает файл, указанный в качестве первого параметра; новое имя файла задается строкой, указанной в качестве второго параметра. Процедура ChDir(имя-нового-каталога) позволяет сменить текущий каталог; новым текущим (рабочим) каталогом будет каталог, имя которого задано строкой, указанной в качестве параметра. Процедура MkDir(имя-нового-каталога) позволяет создать новый каталог; в качестве имени нового каталога будет использована строка, заданная параметром. Процедура RmDir(имя-каталога) позволяет удалить пустой каталог; удаляемый каталог задается строкой, указанной в качестве параметра. Примеры программ, использующих операции ввода-вывода Ниже приведен примеры двух программ, использующих файловый тип данных и операции ввода-вывода. Программа CreateFile создает небольшой “банк данных” − файл, хранящий о каждом студенте следующую информацию: фамилия, имя, отчество, номер группы и пять экзаменационных оценок (по 4-бальной шкале), полученных во время зимней сессии. Ввод данных о студентах выполняется с клавиатуры в интерактивном режиме, т.е. в режиме диалога “компьютер-пользователь”. program CreateFile; type FullName = record FirstName, SecondName, LastName : string [20] end; Person = record Name : FullName; { Ф.И.О. } Group : 100..999; { Номер группы } Balls : array [1..5] of 2..5 { Оценки } end; var DataBase : file of Person; Student : Person; FileName : string [12]; { Имя файла банка данных } i : 1..5; Answer : char; begin write('Файл? (Имя.Расширение) '); readln(FileName); { Ввести имя файла } assign(DataBase,FileName);
32 rewrite(DataBase); { Открыть файл для записи } repeat with Student, Name do begin write('Фамилия? '); readln(FirstName); write('Имя? '); readln(SecondName); write('Отчество? '); readln(LastName); write('N группы? '); readln(Group); for i := 1 to 5 do { Цикл ввода оценок } begin write(i, '-ая оценка? '); readln(Balls[i]) end end; write(DataBase, Student); write('Еще? (Д/Н) '); readln(Answer) until Answer = 'Н' or Answer = 'н'; close (DataBase) end. Программа ScanFile запрашивает у пользователя ввод фамилии студента, последовательно считывает записи из файла и при обнаружении записи, относящейся к указанному студенту, подсчитывает средний балл, полученный во время экзаменационной сессии, и выводит его на экран. Если запись о студенте отсутствует в файле, на экран выводится соответствующее сообщение. program ScanFile; type FullName = record FirstName, SecondName, LastName : string [20] end; Person = record Name : FullName; { Ф.И.О. } Group : 100..999; { Номер группы } Balls : array [1..5] of 2..5 { Оценки } end; var DataBase : file of Person; Student : Person; FileName : string [12]; { Имя файла банка данных } StudName : string[20]; { Фамилия студента } i : 1..5; Summa : byte; { Сумма баллов }
33 Flag : boolean; { Флажок “Есть запись о студенте?” } begin write('Файл? (Имя.Расширение) '); readln(FileName); { Ввести имя файла } assign(DataBase,FileName); reset(DataBase); { Открыть файл для чтения } write('Фамилия? '); readln(StudName); { Ввести фамилию студента } Flag := false; Summa := 0; while not EoF(DataBase) do { Цикл чтения-обработки записей } begin read(DataBase, Student); with Student, Name do if FirstName = StudName then begin Flag := true; { Запись о студенте найдена } for i := 1 to 5 do Summa := Summa + Balls[i]; writeln('Средний балл = ', Summa/5) end end; close(DataBase); if not Flag then writeln('Запись о студенте с фамилией ', StudName, ' в файле ', FileName, ' не найдена.') end. Вопросы и упражнения для самопроверки 1. Что такое базовый тип файла? Какие типы данных в Турбо-Паскале могут быть использованы в качестве базовых типов файлов? 2. На какие группы разделены все операции ввода-вывода в ТурбоПаскале? Какие процедуры обеспечивают выполнение следующих основных операций ввода-вывода: a) открытие файла для чтения или записи; б) чтение информации из файла или запись информации в файл; в) закрытие файла? 3. Что представляет собой понятие “текущий указатель файла”? Какие манипуляции допускаются с текущим указателем файла в языке ТурбоПаскаль? С помощью каких средств? 4. Что представляет собой ситуация “конец файла” и как ее обнаружить средствами языка Паскаль?
34 5. Привести список команд MS-DOS, выполняющих манипуляции с файлами и каталогами, аналогичные действиям следующих процедур: Erase, Rename, ChDir, MkDir и RmDir. 6. Что выведет на экран программа ScanFile при наличии в файле информации об однофамильцах? Как модифицировать эту программу, чтобы на экран выводился средний балл только для конкретного человека? 7. Разработать две программы для работы с файлами вещественных чисел. Первая программа должна позволить пользователю ввести произвольное количество вещественных чисел с клавиатуры и записать их в файл. Вторая программа должна выполнить чтение чисел из файла, определить у каждого из них знак числа и записать положительные числа в один файл, а отрицательные − в другой. 8. Разработать две программы для работы с файлами целых чисел. Первая программа должна позволить пользователю ввести произвольное количество целых чисел с клавиатуры и записать их в файл. Вторая программа должна выполнить чтение чисел из файла, проанализировать каждое из них на чётностьнечётность и записать чётные числа в один файл, а нечётные − в другой. 9. Разработать две программы для работы с файлами символов. Первая программа должна позволить пользователю ввести произвольное количество символов с клавиатуры и записать их в файл. Вторая программа должна выполнить чтение символов из файла, их анализ и запись латинских строчных и прописных букв в один файл, всех остальных символов − в другой. 10. Разработать две программы для работы с файлами символов. Первая программа должна позволить пользователю ввести произвольное количество символов с клавиатуры и записать их в файл. Вторая программа должна выполнить чтение символов из файла, их анализ и запись цифр в один файл, латинских строчных и прописных букв − в другой, все прочие символы игнорировать. 11. Разработать две программы для работы с файлами записей, каждая из которых имеет следующие 2 поля: фамилия и номер группы. Первая программа должна позволить пользователю ввод с клавиатуры произвольного количества записей в файл. Вторая программа должна запросить у пользователя ввод номера группы, а затем выполнить чтение файла и вывод записей, относящихся к указанной группе, в отдельный файл. 12. Разработать две программы для работы с файлами записей, каждая из которых имеет следующие 2 поля: фамилия и имя студента. Первая программа должна позволить пользователю ввод с клавиатуры произвольного количества записей в файл. Вторая программа должна запросить у пользователя ввод имени студента, а затем выполнить чтение записей файла и вывод на экран фамилий студентов, имеющих заданное имя. 13. Разработать две программы для работы с файлами вещественных чисел. Первая программа должна позволить пользователю ввод с клавиатуры произвольного количества вещественных чисел в файл. Вторая программа должна
35 выполнить чтение чисел файла и подсчет для них суммы отрицательных и суммы положительных вещественных чисел. 14. Разработать две программы для работы с файлами целых чисел. Первая программа должна позволить пользователю ввод с клавиатуры произвольного количества целых чисел в файл. Вторая программа должна выполнить чтение целых чисел из файла и поиск среди них максимального числа. 15. Разработать две программы для работы с файлами целых чисел. Первая программа должна позволить пользователю ввод с клавиатуры произвольного количества целых чисел в файл. Вторая программа должна выполнить чтение целых чисел из файла и поиск среди них минимального числа. 16. Разработать две программы для работы с файлами целых чисел. Первая программа должна позволить пользователю ввод с клавиатуры произвольного количества целых чисел в файл. Вторая программа должна выполнить чтение целых чисел из файла и вычисление их среднего арифметического. Лекция 9. Текстовые файлы. Процедуры и функции для работы с текстовыми файлами. Примеры программ, использующих текстовые файлы. Обработка ошибок ввода-вывода в программах на Турбо-Паскале. Вопросы и упражнения для самопроверки. Текстовые файлы В Паскале существует предопределенный файловый тип text, называемый текстовым, который предназначен для описания файлов, компонентами которого являются символьные строки переменной длины. Символьные строки, образующие текстовый файл, отличаются от рассмотренных ранее символьных строк (тип string) тем, что не имеют начального байта длины (0-й байт), а заканчиваются признаком (маркером) конца строки − парой символов CR (Carriage Return – возврат каретки, ВК) и LF (Line Feed – перевод строки, ПС), имеющих десятичные коды 13 и 10 соответственно. Текстовые файлы заканчиваются специальным признаком (маркером) конца файла (символ Ctrl-Z, имеющий код 2610). Описание файловой текстовой переменной имеет следующий вид: var имя-файловой-переменной : text; где text – предопределенный тип, предназначенный для описания текстовых файлов. Процедуры и функции для работы с текстовыми файлами
36 Для работы с текстовыми файлами используются следующие процедуры и функции. Процедура Assign (имя-файловой-переменной,имя-текстового-файла) связывает файловую текстовую переменную, имя которой указано в качестве первого параметра, с файлом на диске, имя которого указано в качестве второго параметра. В дальнейшем все обращения к файлу выполняются через файловую текстовую переменную. Процедура Rewrite (имя-файловой-переменной) открывает новый текстовый файл для записи (указатель файла при этом устанавливается на начало файла). Если файл с таким именем существует на диске в указанном каталоге, то он будет удален. Например: var MyFile : text; begin assign (MyFile, 'd:\gr711\turbo\pas\proba.pas'); rewrite ( MyFile ); ... end. Процедура Reset (имя-файловой-переменной) открывает существующий файл для чтения (указатель файла при этом устанавливается на начало файла, т.е. на первый элемент первой строки файла). Если файл на диске отсутствует, то возникает ошибка ввода-вывода. Процедура Append (имя-файловой-переменной) открывает существующий файл для добавления в него символьных строк (указатель файла при этом устанавливается на конец файла). Если файл на диске отсутствует, то возникает ошибка ввода-вывода. Примечание. После открытия текстового файла одним из указанных выше способов, можно либо только производить запись в него, либо только выполнять чтение из него. Если необходимо изменить характер работы с текстовым файлом, его приходится закрывать, а затем заново открывать, используя соответствующую процедуру. Процедура Read ([имя-файловой-переменной,] имя-переменной-1 [, имяпеременной-2, ..., имя-переменной-N]) считывает информацию из текстового файла, связанного с файловой текстовой переменной, заданной первым параметром − имя-файловой-переменной. Поскольку текстовый файл по определению содержит символьную информацию, при чтении файла очередная часть текущей строки будет пониматься как символьное представление значения, тип которого определяется типом очередной переменной из списка переменных процедуры Read. В данном случае разделителями символьных представлений значений служат символы “пробел” и/или “табуляция”. Процедура Write ([имя-файловой-переменной,] выражение-1 [, выражение-2, ..., выражение-N]) записывает информацию в текстовый файл, связанный
37 с файловой текстовой переменной, заданной первым параметром − имяфайловой-переменной. Каждое выражение, указанное в с списке параметров, вычисляется и записывается в текстовый файл. Если тип вычисленного значения отличается от символьного, то выражение преобразуется в символьное представление, а затем записывается в очередную строку текстового файла. Выше в записи форматов вызовов процедуры Read и Write использованы квадратные скобки, идентифицирующие необязательные параметры, т.е. параметры, которые могут быть опущены при вызове процедуры (сами квадратные скобки, естественно, в операторе вызова процедуры не указываются). Таким образом, простейшая форма оператора вызова − Read(имя-переменной) − фактически означает чтение строки текста (не string) из стандартного входного текстового файла (Input), т.е. с клавиатуры, и преобразование этого текста в тип данных переменной, указанной в качестве параметра процедуры Read. Простейшая форма оператора Write(выражение) означает вычисление значения указанного выражения, преобразование его в символьный вид, т.е. в последовательность символов, и вывод этой последовательности символов на экран. Подобная простая форма процедур Read и Write неоднократно использовалась нами ранее для ввода в программу данных с клавиатуры и вывода результатов работы программы на экран. Имеются две модификации процедур Read и Write – процедуры ReadLn и WriteLn. Эти процедуры выполняют те же действия, что и соответствующие процедуры Read и Write, но после операций чтения и записи производят переход к следующей строке текстового файла. Следует отметить, что операция Read автоматически переходит к следующей строке только в случае исчерпания текущей строки; процедура ReadLn позволяет совершить этот переход, не дожидаясь окончания строки. Процедура Close (имя-файловой-переменной) закрывает открытый ранее текстовый файл. При чтении информации из текстового файла возникает необходимость в контроле за достижением конца строки и конца файла. В первом случае для этой цели используется булевская функция EoLn [(имя-файловой-переменной)], а во втором – уже знакомая нам булевская функция EoF [(имя-файловойпеременной)]. Функция EoLn возвращаeт значение True (истина), если достигнут конец строки, и False (ложь) – в противном случае. Аналогично функция EoF возвращает значение True (истина), если достигнут конец файла, и False (ложь) – в противном случае. Примечание. В форматах обращений к функциям EoF и EoLn, приведенных выше, использованы квадратные скобки, означающие, что имя файловой переменной, заключенное в круглые скобки, может быть опущено. Если это имеет место, то в качестве обрабатываемого тестового файла предполагается стандартный файл Input.
38 Нужно иметь в виду, что для текстовых файлов неприменима операция поиска записи с нужным номером (процедура Seek), определенная для обычных файлов (см. предыдущую лекцию), поскольку строки текстового файла имеют произвольную длину. Некоторым подобием процедуры Seek для текстовых файлов являются булевские функции SeekEoLn и SeekEoF. Функция SeekEoLn[(имя-файловой-переменной)] производит поиск конца текущей строки. Она пропускает все символы-разделители (пробелы и табуляции) в строке, устанавливает текущий указатель файла на конце строки и возвращает значение True (истина). Если конец строки не найден, то SeekEoLn устанавливает указатель на первом значащем символе строки (т.е. на символе, отличном от пробела или табуляции) и возвращает к значение False (ложь). Функция SeekEoF[(имя-файловой-переменной)] осуществляет поиск конца файла и действует аналогично функции SeekEoLn, пропуская символыразделители; однако, кроме пробелов и табуляций она пропускает также символы конца строки (т.е. переходит со строки на строку) в поисках конца файла. Если конец файла найден, то функция SeekEoF возвращает значение True; в противном случае – она возвращает значение и устанавливает указатель на первом значащем символе в файле. Примеры программ, использующих текстовые файлы Ниже рассмотрены три задачи, связанные с обработкой текстовых файлов, и даны их возможные решения − в виде текстов программ на языке Паскаль. Задача 1. Написать программу, выполняющую копирование одного текстового файла (входной файл X) в другой текстовый файл (выходной файл Y) с сохранением построчной структуры исходного файла. program CopyText; var X, Y : text; { Файловые текстовые переменные } C : char; { Буферная символьная переменная } Name_X : string[80]; Name_Y : string[80]; begin write('Имя входного файла? '); readln(Name_X); assign(X, Name_X); reset (X); { Открыть входной файл } write('Имя выходного файла? '); readln(Name_Y); assign(Y, Name_Y); rewrite (Y); { Открыть выходной файл }
39 { Цикл посимвольного копирования одного текстового (входного) файла в другой (выходной) файл } while not eof(X) do { Пока не конец входного файла, выполнять...} begin while not eoln(X) do { Пока не конец строки во входном } begin { файле, выполнять... } read (X,C); { Считать символ из входного файла } write (Y,C) { Записать символ в выходной файл } end; readLn (X); { Перейти к новой строке во входном файле } writeLn (Y) {Записать признак конца строки в выходной файл} end; close(X); { Закрыть входной файл } close(Y) { Закрыть выходной файл } end. Задача 2. Написать программу, выполняющую подсчет количества латинских прописных и строчных латинских букв и количества арабских цифр, обнаруженных в текстовом файле. program CountLetterAndDigit; var F : text; { Файловая текстовая переменная } C : char; { Буферная символьная переменная } LetterCount, DigitCount : word; { Счетчики букв и цифр } FileName : string[80]; { Для хранения имени файла } begin write('Имя файла? '); readln(FileName); assign(F, FileName); reset (F); { Открыть текстовый файл } { Инициализация счетчиков } LetterCount := 0; DigitCount := 0; { Цикл чтения текстового файла и анализ каждого считанного символа } while not eof(F) do {Пока не конец текстового файла, выполнять...} begin read(F,C); {Считать символ из файла в буферную переменную} if ((C >= 'A') and (C = 'A') and (C = '0') and (C 0 then ProcessPos(Val) else ProcessNeg(Val) until eof(InFile) end; { Process } procedure Finish; { Завершающие действия } begin writeln('Сумма ',CountPos,' положительных чисел = ',SumPos); writeln('Сумма ',CountNeg,' отрицательных чисел = ',SumNeg); close(InFile) end; begin { Основная программа } Start; Process; Finish end. { Конец программы } Приведенная выше программа содержит три процедуры, каждая из которых решает одну частную подзадачу: • открытие входного файла и инициализация глобальных переменных; • собственно вычисление сумм и подсчет количества чисел; • вывод результатов и закрытие файла. При этом центральная процедура, имеющая имя Process, содержит две вложенные процедуры − ProcessPos и ProcessNeg, каждая из которых выполняет конкретные вычисления, соответственно, для положительных и отрицательных чисел. Сама центральная процедура работает как управляющая по отношению к вложенным процедурам, в цикле передавая управление той или иной процедуре. Аналогичный характер имеет алгоритм основной программы, кото-
49 рый представлен тремя “макрооперациями”, обозначающими последовательные этапы работы. Функции Смысл функции заключается в задании вычисления некоторого значения и организации возврата (передачи) этого значения в точку вызова функции. Возврат вычисленного значения организуется следующим образом. В теле функции должен присутствовать оператор присваивания, в левой части которого должен присутствовать идентификатор, совпадающий с именем функции, а в правой части – выражение, вычисляющее возвращаемое значение. Тип выражения в правой части должен быть совместим с типом функции (т.е. с типом, указанным в ее заголовке после списка параметров). Следует иметь в виду, что функция может возвращать в качестве результата значение простого и ссылочного типа. Ниже приведен пример описания функции целого типа, возвращающей максимальное значение из двух целых значений, переданных ей в качестве параметров. function MaxAB(A,B: integer): integer; begin if A > B then MaxAB := A then MaxAB := B end; Обращение к функции MaxAB может быть использовано в выражении, например: M := MaxAB(X-Y, X+Y)+2*MaxAB(X*Y, X mod Y); На месте обращений к функции MaxAB будут использованы значения, возвращаемые этой функцией. Механизмы передачи параметров в подпрограммы В заголовке процедуры или функции может быть задан список формальных параметров. Этот список заключается в круглые скобки. Описание формальных параметров весьма сходно с описанием переменных в блоке. Каждый параметр, заданный в заголовке подпрограммы, считается локальным в данной подпрограмме так же, как и переменные, описанные в блоке этой подпрограммы. Имена формальных параметров представляют собой условные обозначения в теле подпрограммы тех фактических параметров, которые будут переданы в подпрограмму при ее вызове. Если в подпрограмме необходимо использовать
50 формальный параметр с типом, что следует иметь в виду, что этот тип должен быть задан именем (идентификатором) типа. Например, следующий заголовок процедуры является недопустимым: procedure InCorrect (var A: array [1..10] of byte); а заголовок type MyArray=array [1..10] of byte; ... procedure Correct (var A: MyArray); ... вполне корректен. Существуют три способа задания формальных параметров в списке формальных параметров в заголовке подпрограммы: 1. Параметры, перед которыми отсутствует служебное слово var и за которыми следует имя типа. 2. Параметры, перед которыми указано служебное слово var и за которыми следует имя типа. 3. Параметры, перед которыми указано служебное слово var, а имя типа отсутствует. Эти три способа задания формальных параметров отражают три различных механизма (способа) передачи параметров в подпрограмму: • передача параметров по значению; • передача параметров по ссылке; • передача нетипизированных параметров по ссылке. Одна подпрограмма может получать различные параметры всеми тремя способами одновременно. Мы рассмотрим два первых способа передачи параметров (называемых параметрами-значениями и параметрами-переменными соответственно), которые имеют место в стандартном Паскале. Третий способ передачи параметров (называемых нетипизированными параметрамипеременными) имеется только в Турбо-Паскале и представляет некоторое отступление от принципов строгой типизации, характерных для языка Паскаль. Передача параметров по значению Этот способ является наиболее распространенным и самым простым способом передачи параметров. В данном случае параметр считается обычной локальной переменной в пределах подпрограммы; особенность этой переменной заключается в том, что при вызове подпрограммы начальное значение параметра автоматически устанавливается равным значению соответствующего фактического параметра, заданного при вызове подпрограммы. Этот фактический параметр может быть произвольным выражением того же типа, что и формальный параметр.
51 В теле подпрограммы возможны произвольные действия с данным формальным параметром, но любые изменения его значения никак не отражаются на значениях переменных вне данной подпрограммы. Ниже приводится пример процедуры, использующей механизм передачи параметров по значению. procedure SumSquare (X, Y : real); begin X := X * X; Y := Y * Y; writeln('Сумма квадратов = ', X+Y) end; Вызов этой процедуры может выглядеть следующим образом: var A, B : real; begin A := 1.75; B := 8.25; SumSquare(A,B); ... end. При вызове процедуры SumSquare с фактическими параметрами A и B значения этих параметров (один раз) копируются в соответствующие формальные параметры X и Y, и дальнейшие манипуляции с формальными параметрами в теле этой процедуры никак не влияют на значения переменных A и B. Передача параметров по ссылке Этот способ передачи параметров используется в тех случаях, когда необходимо передать некоторое значение в точку вызова подпрограммы. Поясним это на следующем примере. Пусть необходимо вычислить сумму и разность квадратов и использовать вычисленные значения в вызывающей программе. Для этого можно определить процедуру SumSubSquare следующим образом: procedure SumSubSquare(X, Y : real; var Sum, Sub : real); begin Sum := X*Х+Y*Y; Sub := X*X-Y*Y end; В этом случае формальные параметры Sum и Sub считаются синонимами соответствующих фактических параметров в пределах процедуры SumSubSquare. При этом фактические параметры должны быть переменными (не выражениями) того же типа, что и формальные параметры. Если вызов процедуры SumSubSquare будет выглядеть следующим образом:
52 var A, B: real; SumAB, SubAB: real; begin A := 1.75; B := 8.25; SumABSquare(A, B, SumAB, SubAB); ... end; то присваивание параметрам Sum и Sub внутри тела процедуры будут означать соответствующие присваивания переменным SumAB и SubAB, переданным процедуре по ссылке. Ниже приведен еще один пример процедуры, использующей механизм передачи параметров по ссылке. Эта процедура производит обмен значений между двумя вещественными переменными. procedure Swap(var X, Y: real); var T : real; begin T := X; X := Y; Y := T end; Обмен значений двух переменных, переданных в процедуру Swap по ссылке, выполняется с помощью промежуточной переменной T. Вызов процедуры Swap может быть выполнен следующим образом: var A, B : real; begin A := 1.75; B := 8.25; Swap(A,B); ... end. Примечание. Следует иметь в виду, что переменные файловых типов могут передаваться в подпрограммы только как параметры-переменные. Завершение подпрограмм Работа процедуры или функции завершается после выполнения последнего оператора ее тела. В Паскале отсутствуют специальный оператор возврата из подпрограммы (подобно оператору RETURN в языке ПЛ/1 или ФОРТРАН). Однако в языке Турбо-Паскаль имеется дополнительное средство прерывания выполнения программы в произвольной точке – системная процедура exit, не
53 имеющая параметров, которая немедленно завершает выполнение подпрограммы и возвращает управление в точку вызова. Необходимо помнить, что перед вызовом процедуры exit в теле функции должен обязательно выполниться оператор присваивания с именем функции в левой части. Следующий пример показывает использование системной процедуры exit для возврата управления во внешний – по отношению к приведенной процедуре P – блок. procedure P(X,Y: real; var Res : real); begin if X-Y < 0.0001 then exit; Res := (X*X+Y*Y)/(X*X-Y*Y) end; Если вызов процедуры exit был произведен из блока, соответствующего программе в целом (т.е. из самого внешнего блока), то будет немедленно завершено выполнение всей программы. Предварительное объявление подпрограмм Как правило, телом процедуры является блок. Однако, имеется несколько исключений из этого правила. Мы рассмотрим одно из них, суть которого состоит в следующем. Пусть имеются две процедуры с именами Proc1 и Proc2, которые вызывают друг друга, причем это процедуры одного уровня вложенности: procedure Proc1(x,y: real); begin ... Proc2(1, 2); ... end; procedure Proc2(a,b: integer); begin ... Proc1(0.5, 0.6); ... end; При трансляции процедуры Proc1 компилятор не может правильно обработать вызов процедуры Proc2, так как эта процедура описывается ниже по тексту программы и информация о ней еще неизвестна. Если произвести обмен местами этих двух процедур в программе, то аналогичная проблема возникнет с трансляцией вызова процедуры Proc1 в процедуре Proc2. Чтобы решить подобную проблему (разорвать порочный круг) в Паскале введен механизм предварительного (опережающего) описания подпрограмм.
54 Предварительное описание содержит заголовок подпрограммы, а вместо тела записывается служебное слово forward, которое указывает, что полное описание подпрограммы располагается в тексте далее. В этом случае заголовок полного (определяющего) описания может быть записан в сокращенном виде, без списка параметров и (для функций) без результата. Для приведенного выше примера предварительные и полные описания будут выглядеть следующим образом: procedure Proc1(x, y: real); forward; procedure Proc2(a, b: integer); forward; procedure Proc1; begin ... Proc2(1, 2); ... end; procedure Proc2; begin ... Proc1(0.5, 0.6); ... end; В данном случае при обработке вызова процедуры Proc2(1, 2) в процедуре Proc1 компилятор использует информацию о процедуре Proc2 из заголовка ее предварительного описания. Следует отметить, что в случае предварительного описания подпрограммы далее в тексте должно обязательно содержаться ее определяющее описание, даже если в программе не встречается вызов этой подпрограммы. Блочная структура программ на Паскале Как отмечалось выше, телом подпрограммы является блок, содержащий описания объектов и группу операторов, работающих с этими объектами. Имена объектов, описанных в блоке подпрограммы (это относится и к процедурам, и к функциям), считаются известными только в пределах данного блока. Поскольку среди описаний блока могут содержаться описания процедур и/или функций, допускается наличие в подпрограмме вложенных подпрограмм, которые, в свою очередь, могут содержать свои вложенные подпрограммы. Таким образом, программы на Паскале имеют блочную структуру. Рассмотрим блочную структуру программы Example, рассмотренной выше. Структура этой программы схематично изображена на рис. 5.
55 Ex ample S tart Process ProcessPos ProcessN eg
Finish Рис.5. На приведенной выше схеме блок основной программы (Example) является самым внешним блоком. Блоки подпрограмм Start, Process и Finish описаны в этом внешнем блоке. Блок подпрограммы Process в свою очередь содержит описания двух подпрограмм – ProcessPos и ProcessNeg, т.е. соответствующие им блоки являются вложенными в блок Process. Такая блочная структура программ традиционна для алголоподобных языков (а Паскаль "вырос" из Алгола-60) и требует определенной дисциплины для доступа к объектам, описанным в различных блоках. Эта дисциплина может быть кратко сформулирована в виде следующих правил: 1. Имена объектов, описанных в некотором блоке, считаются известными в пределах данного блока, включая и все вложенные блоки. 2. Имена объектов, описанных в блоке, должны быть уникальными в пределах данного блока и могут совпадать с именами объектов из других блоков. 3. Если в некотором блоке описан объект, имя которого совпадает с именем объекта в объемлющем блоке, то это последнее имя становится недоступным в данном блоке (говорят, что имя, описанное в блоке, экранирует или закрывает одноименные объекты из внешних – по отношению к данному – блоков). Возвращаясь к приведенной выше схеме, можно сказать, что объекты, описанные в блоке основной программы (Example), известны (говорят также – видны), кроме самого блока Example, еще и в блоках Start, Process, ProcessPos, ProcessNeg и Finish. Имена из блоков Start, ProcessPos, ProcessNeg и Finish известны только в пределах соответствующих блоков. Таким образом, для приведенной выше схемы можно составить табл.7, показывающую области видимости (или области действия) имен объектов: Таблица 7. Блок Example
Можно обращаться к объектам Start, Process, Finish, InFile, CountPos, CountNeg, SumPos, SumNeg
56 Start Process ProcessPos ProcessNeg Finish
Process, Finish, InFile, CountPos, CountNeg, SumPos, SumNeg ProcessPos, ProcessNeg, Val, InFile, CountPos, CountNeg, SumPos, SumNeg, Start, Finish Val, InFile, CountPos, CountNeg, SumPos, SumNeg, Process, ProcessNeg, Start, Finish Val, InFile, CountPos, CountNeg, SumPos, SumNeg, Process, ProcessPos, Start, Finish InFile, CountPos, CountNeg, SumPos, SumNeg, Start, Process Вопросы и упражнения для самопроверки
1. Указать ошибки в описании каждой из следующих функций: а) function F(a : ‘A’..’Z’) : integer; begin F := Ord(a)-Ord(‘P’); if F < 0 then F := -F end; б) function G(k : integer) : 0..MaxInt; var i, s : 0..MaxInt; begin s := 0; for i := 1 to k do s := s + sqr(i) end; в) function H(x : integer) : integer; begin H(x) := (sqr(x) + x) / 2 end; 2. Даны следующие описания: var c, d : integer; procedure P(x, y : integer); begin y := x + 1 end; procedure Q(x : integer; var y : integer); begin y := x + 1 end; procedure R(var x, y : integer); begin y := x + 1 end; a) Для каждой из этих процедур указать, какие из ее параметров являются параметрами-значениями, а какие − параметрами-переменными. б) Определить, что будет выведено на экран: c := 2; d := 0; P(sqr(c) + c, d); writeln(d); c := 2; d := 0; Q(sqr(c) + c, d); writeln(d);
57 Почему при изменении в процедуре параметра-значения соответствующий фактический параметр не меняет своего значения? Что сделать, чтобы он менял значение? 3. Пусть процедура MaxMin(x,y) присваивает параметру x большее из вещественных чисел x и y, а параметру y − меньшее. a) Какие из параметров этой процедуры обозначают исходные данные для нее, а какие − результаты? Какие параметры следует объявить параметрамизначениями, а какие − параметрами-переменными? б) Допустимы ли обращения MaxMin(5.75, sin(z)) и MaxMin(z, k), где z − вещественная переменная, а k − целая? в) Описать данную процедуру и использовать ее для перераспределения значений вещественных переменных a, b и c так, чтобы стало a ≥ b ≥ c. Лекция 11. Модульная организация программ на Турбо-Паскале. Понятие модуля. Общая структура модуля. Использование подпрограмм в модулях (пример модуля). Компиляция и использование модулей. Стандартные модули языка Турбо-Паскаль версии 6.0. Вопросы и упражнения для самопроверки. Модульная организация программ на Турбо-Паскале В Турбо-Паскале версии 4.0 введено понятие модуля. Этот решающий шаг превратил язык Турбо-Паскаль в язык профессионального программирования, пригодный для крупных разработок производственного и коммерческого назначения на современном уровне технологии программирования. Никлаус Вирт создал язык Паскаль как средство обучения программированию. Он прекрасно осознавал, что отсутствие модульности в программах на Паскале крупный недостаток. Поэтому в следующем своем языке, названном Модула (от слова Module − модуль), понятие модуля становится одним из базовых понятий языка. Модуль − это независимо разрабатываемая и хранимая, независимо компилируемая и тестируемая программная единица со строго определенным программным интерфейсом. Модуль содержит в себе программные объекты − константы, типы, переменные и подпрограммы, которые используются другими модулями и программами. Важно понимать, что модуль сам по себе не является выполняемой программой − его программные объекты используются другими модулями и программами. Общая структура модуля Все программные объекты модуля можно разделить на две части: объекты, прямо предназначенные для использования другими программами и моду-
58 лями, и объекты, рабочего (внутреннего) характера, играющие вспомогательную роль в самом модуле. В соответствии с этим модуль имеет две основные части, называемые интерфейсом (interface) и реализацией (implementation). Кроме того, модуль может содержать так называемый раздел инициализации, предназначенный для установки начальных значений переменных модуля перед их использованием. Таким образом, модуль имеет следующую структуру: unit Имя_модуля; { Заголовок модуля } interface { Интерфейс } ... implementation { Реализация } ... end; begin { Раздел инициализации } ... end. В интерфейсной части модуля сосредоточены описания программных объектов, доступных из других программных единиц. В части реализации помещаются рабочие объекты, называемые также невидимыми или скрытыми. Использование подпрограмм в модулях (пример модуля) Процедуры и функции могут использоваться в модулях наравне с другими программными объектами. Однако для них имеются особенности, обусловленные их структурой. Поскольку заголовок подпрограммы содержит всю информацию, необходимую для ее вызова (имя, количество и типы параметров, тип результата − для функции), то он должен быть представлен в интерфейсной части модуля. Тело подпрограммы раскрывает ее алгоритм, поэтому его помещают в раздел инициализации и снабжают сокращенным заголовком. В качестве примера рассмотрим небольшой модуль, содержащий средства для работы с комплексными числами. unit CmplVals; interface type Complex = record Re, Im : real end; procedure InitC(C: Complex; R, I: real); procedure AddC(C1, C2: Complex; var R: Complex); procedure MultC(C1, C2: Complex; var R: Complex); procedure DivC(C1, C2: Complex; var R: Complex); procedure WriteC(C: Complex); implementation procedure InitC; { Инициализация комплексного числа }
59 begin with C do begin Re := R; Im := I end end; procedure AddC; { Сложение двух комплексных чисел } begin with R do begin Re := C1.Re + C2.Re; Im := C1.Im + C2.Im end end; procedure MultC; { Умножение двух комплексных чисел } begin with R do begin Re := C1.Re * C2.Re + C1.Im * C2.Im; Im := C1.Im * C2.Re + C1.Re * C2.Im end end; procedure DivC; { Деление двух комплексных чисел } var Tmp : real; begin with C2 do Tmp := Re * Re + Im * Im; with R do begin Re := (C1.Re * C2.Re + C1.Im * C2.Im) / Tmp; Im := (C2.Re * C1.Im + C1.Re * C2.Im) / Tmp end end; procedure WriteC; { Вывод комплексного числа } begin with C do begin write(Re); if Im = 0 then exit; if Im > 0 then write(‘+’); write(Im);
60 write(‘i’) end end; end. Таким образом, механизм модулей позволяет скрыть детали реализации тех или иных программных подсистем, предоставив в распоряжение использующих модуль программ строго определенную совокупность интерфейсных объектов. Пример программы, использующей модуль CmplVals: program Proba; uses CmplVals; var C1, C2, C3 : Complex; begin Init(1,2,C1); Init(3,4,C2); Mult(C1,C2,C3); write(C3); DivC(C1,C2,C3); write(C3) end. Необходимо отметить следующие важные моменты, связанные с использованием модулей: 1. Если имена интерфейсной части модуля частично пересекаются с именами использующей модуль программы, то действует следующее правило видимости имен: интерфейсные имена модуля, указанного в списке uses первым образуют самый внешний блок программы; интерфейсные имена второго модуля образуют блок, вложенный в первый блок, и т.д. Таким образом, если во втором модуле находятся имена, совпадающие с именами первого модуля, то они будут “экранировать” имена первого модуля. Чтобы обеспечить к ним доступ необходимо использовать формат, похожий на формат селектора записи: имя_модуля.имя_программного_объекта 2. Возможны случаи косвенного использования имен. Например, имеются два модуля: unit A; unit B; interface interface ... uses A; end. ... end. Если некоторая программа использует модуль B, то в соответствующей строке uses должна быть указана вся цепочка используемых модулей, даже если программа не использует непосредственно объекты модуля A. 3. Схема использования модулей может образовывать древовидную структуру любой сложности, но при этом недопустимо явное или косвенное об-
61 ращение модуля к самому себе. Так, например, следующие отношения являются ошибочными: unit A; unit B; interface interface uses B; uses A; ... ... end. end. Однако допускается взаимное использование модулей, позволяющее ослабить указанное ограничение. В этом случае список модулей в uses может указываться в разделе реализации. 4. Если в модуле имеется раздел инициализации, то операторы из этого раздела будут выполнены перед началом выполнения программы, в которой используется данный модуль. Если программа использует несколько модулей, то их разделы инициализации будут выполнены в том же порядке, в котором эти модули перечислены в списке uses. Компиляция и использование модулей Заголовок модуля, как правило, совпадает с именем файла, содержащего исходный текст модуля (расширение файла несущественно, но по умолчанию предполагается .PAS). Компилятор помещает код модуля, полученный в результате компиляции, в файл с таким же именем и расширением .TPU. При компиляции программы, использующей этот модуль, компилятор ищет TPU-файл с именем, заданным в спецификации uses, и связывает его с кодом программы. Таким образом, в спецификации uses фактически задаются не имена модулей, а имена файлов, их содержащих. Например, если в программе имеется строка uses MyUnit; компилятор перед компиляцией самой программы должен найти на диске файл с именем MYUNIT.TPU; в этом файле должен находиться код модуля с заголовком unit MyUnit; Если по какой-либо причине необходимо хранить код модуля в файле с другим именем, то можно использовать директиву $U для переопределения имени файла. Эта директива, имеющая параметр, рассматриваемый как “настоящее”имя файла с данным модулем, помещается непосредственно перед именем модуля в спецификации uses. Например, следующая строка: uses {$U MYFILE} MyUnit; приведет к тому, что компилятор будет искать код модуля MyUnit в файле MYFILE.TPU. При компиляции программы или модуля, использующего другие модули, компилятор последовательно отыскивает файлы, содержащие коды используе-
62 мых модулей с тем, чтобы подключить их к компилируемой программе. При этом компилятор работает по следующей схеме: 1. Компилятор просматривает содержимое системного библиотечного файла TURBO.TPL (Turbo Pascal Library). Этот файл библиотеки модулей имеет специальную структуру и предназначен для компактного хранения и быстрого доступа к коду следующих системных модулей: System, Dos, Crt, Printer и Overlay. С помощью утилиты TPUMOVER.EXE можно помещать код необходимых модулей в файл TURBO.TPL и удалять код неиспользуемых модулей. 2. Если искомый модуль в файле TURBO.TPL, то компилятор осуществляет поиск соответствующего TPU-файла в текущем каталоге. 3. Если в текущем каталоге нужный файл не найден, то поиск продолжается в каталогах, заданных в опциях {Options | Directories | Unit Directories} интегрированной среды разработки (ИСР) языка Турбо-Паскаль версии 6.0. 4. Если на предыдущих шагах файл не найден, то компилятор выдает диагностическое сообщение и прекращает работу. 5. Если компилятор активизирован командами {Compile | Make} или {Compile | Build}, то описанные выше шаги проводятся в поисках исходных текстов используемых модулей, которые будут откомпилированы перед компиляцией самой программы. При этом подразумевается, что имя файла с текстом модуля совпадает с именем модуля и имеет расширение .PAS.
Стандартные модули языка Турбо-Паскаль версии 6.0 Язык Турбо-Паскаль версии 6.0 имеет восемь стандартных модулей: SYSTEM, DOS, CRT, PRINTER, OVERLAY, GRAPH, TURBO3, GRAPH3. Каждый стандартный модуль содержит логически связанную совокупность типов, констант, переменных и подпрограмм, относящихся к определенной области применений, и хранится в одноименном TPU-файле в системном каталоге системы Турбо-Паскаль. Модуль System содержит все процедуры и функции стандартного языка Паскаль, а также множество дополнительных подпрограмм общего характера, в частности, ориентированных на конкретную операционную среду. Модуль Dos содержит средства для доступа к возможностям (службам) операционной системы MS-DOS. Модуль Crt содержит средства для управления выводом на экран монитора в текстовом режиме и чтением информации с клавиатуры, а также простейшие средства для управления звуком встроенного динамика компьютера. Модуль Printer содержит единственный интерфейсный элемент − переменную Lst стандартного типа text, системно связанную с логическим устройством PRN (т.е. с печатающим устройством, если оно имеется в системе). Исполь-
63 зование этой переменной в стандартных процедурах Write и WriteLn приводит к выводу информации на печать. Модуль Overlay содержит средства для создания так называемых оверлейных программ (или программ с перекрытиями). Эти средства позволяют создавать большие программные системы, размер которых превышает объем доступной оперативной памяти. Модуль Graph содержит средства для работы в графических режимах, обеспечиваемых наиболее распространенными типами видеоадаптеров: CGA, EGA, VGA и т. п. Модули Turbo3 и Graph3 обеспечивают совместимость программ, разработанных в системе Турбо-Паскаль версии 3.0, с данной версией языка ТурбоПаскаль. Вопросы и упражнения для самопроверки 1. Указать место программного модуля в блочно-иерархической структуре программной системы, написанной на языке Турбо-Паскаль версии 6.0 по отношению: а) к основной программе; б) к подпрограммам, входящим в состав программы и программных модулей; в) к описаниям и операторам, составляющим подпрограммы, модули и программы? 2. Назвать структурные части модуля и указать их назначение и порядок следования в модуле.
3. Какие требования предъявляются к заголовку модуля? Может ли заголовок модуля содержать более восьми символов? Почему? 4. Каким образом указать компилятору на необходимость подключения кода некоторого модуля к коду программы? 5. Какие правила “экранирования” действуют при подключению к программе нескольких модулей? Как предотвратить экранирование программных объектов, описанных в различных модулях? 6. Перечислить стандартные модули системы Турбо-Паскаль версии 6.0 и кратко указать функциональное назначение каждого из них. 7. Что представляет собой файл TURBO.TPL? Какие стандартные модули представлены в этом файле? Какая утилита используется для добавления модулей в файл TURBO.TPL? Удаления модулей из файла TURBO.TPL? 8. Создать свой собственный модуль, содержащий средства для решения задач из некоторой проблемной области (например, процедуры, реализующие численные методы решения уравнений).
64
Содержание Лекция 6. Составные типы данных языка Паскаль. Регулярные типы (массивы). Примеры программ, использующих регулярные типы данных. Строковый тип данных (строки символов). Пример программы, использующей строковый тип данных. Лекция 7. Комбинированный тип данных (записи). Фиксированные записи. Записи с вариантами. Примеры программ, использующих записи. Оператор присоединения (WITH). Пример программы, использующей оператор WITH. Вопросы и упражнения для самопроверки. Лекция 8. Ввод-вывод в программах на языке Паскаль. Файловый тип данных. Операции над файлами. Начальные и завершающие операции ввода-вывода. Операции ввода-вывода. Операции перемещения по файлу. Специальные операции ввода-вывода. Примеры программ, использующих операции ввода-вывода. Вопросы и упражнения для самопроверки. Лекция 9. Текстовые файлы. Процедуры и функции для работы с текстовыми файлами. Примеры программ, использующих текстовые файлы. Обработка ошибок ввода-вывода в программах на ТурбоПаскале. Вопросы и упражнения для самопроверки. Лекция 10. Понятие подпрограммы. Подпрограммы в языке Паскаль: процедуры и функции. Примеры программ, использующих процедуры и функции. Механизмы передачи параметров в подпрограммы. Передача параметров по значению. Передача параметров по ссылке. Завершение подпрограмм. Предварительное объявление подпрограмм. Блочная структура программ на языке Паскаль. Вопросы и упражнения для самопроверки. Лекция 11. Модульная организация программ на Турбо-Паскале. Понятие модуля. Общая структура модуля. Использование подпрограмм в модулях (пример модуля). Компиляция и использование модулей. Стандартные модули языка Турбо-Паскаль версии 6.0. Вопросы и упражнения для самопроверки.
3
14
24
35
45
57
65
Стариков Александр Вениаминович
ИНФОРМАТИКА Основы программирования на языке Паскаль Тексты лекций в 3-х частях Часть 2
Редактор С.О. Петровская
Подписано в печать 29.04.99. Форм. бум. 297×420 1/16. Бум. ZOOM. Усл. п. л. − 3,72. Уч.-изд. л. − 4,58. Тираж 75. Заказ № РИО ВГЛТА. УОП ВГЛТА. 394613, г. Воронеж, ул. Тимирязева, 8.