Министерство общего и профессионального образования Российской Федерации РОСТОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ КАФЕДРА ...
7 downloads
245 Views
413KB 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
Министерство общего и профессионального образования Российской Федерации РОСТОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ КАФЕДРА ТЕОРЕТИЧЕСКОЙ И ВЫЧИСЛИТЕЛЬНОЙ ФИЗИКИ _______________________________________________________________
Т. П. Шестакова МЕТОДИЧЕСКИЕ УКАЗАНИЯ по курсу "Программирование и вычислительная физика" Часть II Процедуры и функции в языке Turbo Pascal. Перечисляемые типы данных, тип-диапазон и оператор case. Массивы
г. Ростов-на-Дону 2003 г.
2
Печатается по решению учебно-методической комиссии физического факультета. Протокол N от Шестакова Т. П. Методические указания по курсу "Программирование и вычислительная физика". Часть II. Процедуры и функции в языке Turbo Pascal. Перечисляемые типы данных, тип-диапазон и оператор case. Массивы. Заказ N
3
ВВЕДЕНИЕ Во второй части методических указаний продолжается знакомство с основными конструкциями языка Turbo Pascal и типы данных. Рассматривается использование определяемых программистом процедур и функций, которые играют роль подпрограмм в языке Turbo Pascal, в частности, рекурсивный вызов процедур и процедурные типы данных. Также обсуждаются перечисляемые типы данных, тип-диапазон и оператор выбора case, одномерные и двумерные массивы. ПРОЦЕДУРЫ И ФУНКЦИИ. Процедуры и функции представляют собой самостоятельные фрагменты программы, связанные с остальной программой с помощью нескольких параметров. Использование процедур и функций позволяет выделять логически связанные части программы, а также писать и выполнять их отладку отдельно от основной программы. Программы, в которых используются процедуры и функции, являются структурированными. Особое значение использование процедур и функций приобретает при написании больших и сложных программ. С другой стороны, их использование позволяет экономить память, поскольку к одной и той же процедуре, как правило, программа обращается многократно. Процедуры и функции описываются в разделе описаний и имеют собственные имена. Обращение к процедуре или функции по ее имени называется вызовом процедуры или функции. Сразу после вызова процедуры или функции начинают выполняться входящие в нее операторы; после выполнения последнего из них управление передается обратно в основную программу. Внутри процедуры или функции, однако, может содержаться обращение к другим процедурам или функциям или к самой себе (рекурсивный вызов). Некоторые переменные, используемые в программе, являются вспомогательными и могут быть локализованы внутри процедур. Каждая процедура, как и сама программа, состоит из раздела описаний и раздела операторов. В разделе описаний описываются используемые в процедуре константы, переменные, типы, вложенные процедуры низшего уровня и т. д. Однако, все описанные внутри процедуры константы, переменные и т. д. недоступны основной программе. Подобным образом, если процедура более высокого уровня содержит описание процедур и функции низшего уровня, константы и переменные, описанные в процедуре низшего уровня, недоступны процедуре более высокого уровня. Таким образом, осуществляется принцип локализации имен. Все объекты, описанные внутри процедуры или функции, являются локальными. В то же
4
время любая процедура может использовать константы, переменные и т. д., которые опиcаны в процедурах более высокого уровня и в основной программе. Такие объекты являются глобальными по отношению к данной процедуре. При этом описание этих объектов должно предшествовать описанию процедуры. Отметим, что имя локальной переменной может, в принципе, совпадать с именем глобальной переменной. При этом, однако, нужно иметь в виду следующее правило: внутри процедуры локальная переменная (описанная внутри этой процедуры) "закрывает" глобальную (описанную в основной программе или в процедуре более высокого уровня), т. е. внутри процедуры с этим именем связывается локальная переменная, глобальная же переменная с тем же именем становится недоступной. Описание процедуры: ЗАГОЛОВОК ПРОЦЕДУРЫ: Procedure <имя процедуры> (<описание формальных параметров>); <директивы>; РАЗДЕЛ ОПИСАНИЙ (описание локальных констант, переменных, типов, процедур и функций низшего уровня) РАЗДЕЛ ОПЕРАТОРОВ begin ... end; <имя процедуры> − идентификатор языка Turbo Pascal. При вызове процедуры формальные параметры заменяется фактическими параметрами. Формальные параметры резервируют место для фактических параметров. Через эти параметры происходит обмен данными процедуры с основной программой. Имена формальных параметров выбираются произвольно (в частности, они могут совпадать с именами фактических параметров). При обращении к процедуре из основной программы количество и порядок следования фактических параметров должен в точности соответствовать количеству и порядку следования формальных параметров в заголовке процедуры. В противном случае обращение к процедуре может привести к ошибке. В частности, ошибка может возникнуть из-за несовпадения типов формального и фактического параметра. Существует несколько способов описания формальных параметров. 1. Параметр-значение. Описание: <имя параметра>: <тип параметра>
5
Процедуре передается копия фактического параметра, и в ходе выполнения процедуры изменяется только эта копия. Ее изменение никак не влияет на значение фактического параметра. Это называется передачей параметра по значению. Параметру-значению при вызове процедуры должен соответствовать фактический параметр − выражение указанного типа. 2. Параметр-переменная. Для того, чтобы значение фактического параметра могло изменяться в ходе выполнения процедуры, его описанию должно предшествовать служебное слово var. Такой параметр рассматривается как параметр-переменная: Описание: var <имя параметра>: <тип параметра> Процедуре передается адрес фактического параметра, поэтому изменение соответствующего формального параметра внутри процедуры изменяют и фактический параметр. Это называется передачей параметра по ссылке (имеется в виду ссылка на адрес фактического параметра). Параметру-переменной при вызове процедуры должен соответствовать фактический параметр − переменная указанного типа. 3. Параметр-константа. Описание: const <имя параметра>: <тип параметра> Процедуре передается адрес фактического параметра, однако такому параметру невозможно присвоить новое значение в ходе выполнения процедуры (это блокируется компилятором). Параметру-константе при вызове процедуры должен соответствовать фактический параметр − выражение указанного типа. Все выходные параметры процедуры, через которые основной программе передаются результаты работы процедуры, должны быть описаны как параметры-переменные. С другой стороны, использование параметров-значений или констант позволяет работать с фактическими параметрами − выражениями, а также гарантирует невозможность случайно изменить значение фактического параметра при работе процедуры, что повышает надежность программы. Необходимо также учитывать, что использование параметров-значений требует дополнительных затрат памяти (так как для копии каждого параметра-значения отводится дополнительное место в памяти). Параметры одного типа и назначения можно перечислить через запятую. Описание параметров разных типов и разного назначения отделяются точкой с запятой, например: Procedure P(const c123: integer; i, j: integer; a, b, c: real; var f1: real);
6
За заголовком процедуры могут следовать директивы. Стандартными директивами языка Turbo Pascal являются директивы assembler, external, far, near, forward, inline, interrupt. Например, использование директивы assembler указывает, что процедура представляет собой последовательность команд встроенного в компилятор языка Turbo Pascal ассемблера. Директива far определяет так называемую дальнюю модель памяти, т. е. обеспечивает возможность обратиться к процедурам и функциям, сопровождаемым этой директивой, из любого участка программы. Указание этой директивы необходимо, например, при использовании процедурных типов (см. ниже). Напротив, директива near определяет ближнюю модель памяти. Директива forward используется при опережающем описании процедуры, − так бывает, например, когда одна из процедур содержит обращение ко второй процедуре, а вторая, в свою очередь, к первой. Поскольку каждая процедура должна быть описана до обращения к ней, заголовок второй процедуры нужно объявить перед описанием первой процедуры, снабдив ее директивой forward. Директива forward, таким образом, указывает, что описание процедуры помещается далее в тексте программы, но обращаться к ней можно уже сейчас (из первой процедуры, описание которой предшествует описанию второй процедуры). Функция отличается от процедуры тем, что результатом ее работы является единственное значение, поэтому обращение к функции можно использовать в выражениях наряду с переменными и константами. Описание функции: ЗАГОЛОВОК ФУНКЦИИ: Function <имя функции> (<описание формальных параметров>): <тип результата>; РАЗДЕЛ ОПИСАНИЙ (описание локальных констант, переменных, типов, процедур и функций низшего уровня) РАЗДЕЛ ОПЕРАТОРОВ begin ... end. Раздел операторов обязательно должен включать оператор, в котором функции присваивается вычисленное значение, т. е. <имя функции> := <вычисленное значение>;
7
В предыдущем примере находилось значение функции sin ( x ) с заданной точностью с помощью суммирования ряда. Результат сравнивался со значением, полученным с помощью стандартной функции языка Turbo Pascal. Проиллюстрируем на том же примере использование процедур и функций. Идея заключается в том, чтобы фрагмент программы, в котором выполняется последовательное суммирование членов ряда, выделить в отдельную процедуру. В нашем примере достаточно описать в основной программе переменные: epsilon − заданная точность вычисления; x − аргумент функции; sinus − результат вычисления функции с помощью ряда. Эти переменные будут глобальными по отношению к процедуре вычисления функции sinus. Вспомогательные переменные будут локальными (локализованными внутри процедуры). В процедуре суммирования ряда sin1 используются следующие формальные параметры: e1 − заданная точность вычисления; x1 − аргумент функции. Эти параметры не изменяются при выполнении процедуры и поэтому передаются по значению (как параметры-значения). s1 − результат вычисления; он должен быть передан в основную программу и поэтому описывается как параметр-переменная. В принципе, можно было использовать для формальных параметров имена epsilon, x, sinus, поскольку в данном случае это не должно привести к недоразумению. Результатом работы процедуры является единственное значение s1. Поэтому в данном случае можно было использовать функцию. Такая функция описана далее в программе под именем sin2. Используются формальные параметры: e2 − заданная точность вычисления; x2 − аргумент функции. Значение функции имеет тип extended. Program Procedures_and_functions_1; {Описание глобальных переменных} var epsilon, x, sinus: extended; Procedure sin1 (e1, x1: extended; var s1: extended); {Описание вспомогательных (локальных) переменных} var s, sqrx: extended; n: byte; {Раздел операторов процедуры sin1} begin {sin1} s := x1; {Первый член ряда} n := 1; {Степень члена ряда} s1 := 0; {Сумма ряда}
8
sqrx := sqr(x1); while abs(s) > e1 do begin s1 := s1 + s; n := n + 2; s := - s * sqrx / ((n - 1) * n) end end; {sin1} Function sin2 (e2, x2: extended): extended; {Описание вспомогательных (локальных) переменных} var s, sqrx, s2: extended; n: byte; {Раздел операторов функции sin2} begin {sin2} s := x2; {Первый член ряда} n := 1; {Степень члена ряда} s2 := 0; {Сумма ряда} sqrx := sqr(x2) while abs(s) > e2 do begin s2 := s2 + s; n := n + 2; s := - s * sqrx / ((n - 1) * n) end; {Присваивание вычисленного значения s2 функции sin2} sin2 := s2 end; {sin2} {Основной текст - исполняемая часть программы} begin {Program} writeln('Вычисление функции SIN(X):'); write('Введите заданную точность: '); readln(epsilon); write('Введите аргумент функции: '); readln(x); {Вызов процедуры sin1} sin1(epsilon, x, sinus); {При обращении к процедуре sin1 формальные параметры e1, x1, s1 заменяются фактическими
9
epsilon, x, sinus} write('Значение, вычисленное с помощью '); writeln('суммирования ряда: '); write('Результат выполнения '); write('процедуры sin1 '); writeln(sinus:20:18); write('Результат использования '); write('функции sin2: '); writeln(sin2(epsilon, x):20:18); {Здесь обращение к функции происходит внутри оператора writeln, что допустимо, поскольку аргументом этого оператора является выражение} write('Значение, вычисленное с помощью '); write('стандартной функции: '); writeln(sin(x):20:18); writeln; end. {Program}
Заметим, что операторы write, writeln, read, readln на самом деле осуществляют вызов стандартных процедур ввода-вывода языка Turbo Pascal. Так же, как и стандартные функции, эти процедуры не нуждаются в предварительном описании, в отличие от создаваемых программистом процедур и функций. При обсуждении целочисленных типов данных (см. часть I) мы рассматривали пример вычисления суммы цифр числа. Покажем теперь, как можно вычислить эту сумму с помощью рекурсивного обращения процедуры самой к себе. Program Procedures_and_functions_2; {Описание глобальных переменных n, summa} var n: longint; summa: byte; Procedure Sum (n: longint; var ss: byte); {Имя формального параметра n совпадает с именем глобальной переменной n} {Раздел операторов процедуры Sum} begin {Sum} if n <> 0 then begin
10
ss := ss + abs(n mod 10); Sum(n div 10, ss) {Рекурсивный вызов процедуры Sum} end end; {Sum} {Основной текст - исполняемая часть программы} begin {Program} writeln('Вычисление суммы цифр:'); write('Введите любое целое число, которое '); write('по модулю не превосходит '); writeln('2 147 483 647'); readln(n); summa := 0; Sum(n, summa); writeln('Сумма цифр ', n:9, ' равна ', summa:3) end. {Program}
В части IV методических указаний в разделе, посвященном динамической памяти и использованию указателей, будет приведен еще один пример рекурсивного вызова процедуры для вывода на экран списочной структуры данных. В языке Turbo Pascal допускается использование процедурных типов. Процедурные типы относятся к типам, определяемым программистом. Они описываются в разделе описаний программы. Переменная процедурного типа имеет своим значением имя процедуры или функции. Использование процедурных типов дает программисту возможность передачи процедур и функций в качестве фактических параметров при обращении к другим процедурам и функциям. Описание процедурного типа: type <имя типа> = <заголовок процедуры или функции без указания имени процедуры или функции>; Рассмотрим, например, фрагмент программы, в котором нам необходимо вывести на экран таблицы значений нескольких функций. Пусть это будут функции f1, f2. Процедуры и функции, имена которых будут передаваться другим процедурам и функциям в качестве фактических параметров, должны сопровождаться директивой far. Еще раз напомним, что эта директива определяет дальнюю модель памяти, т. е. обеспечивает возможность обратиться к этим процедурам и функциям из любой части программы.
11
функции одного Обе функции f1, f2 − действительные действительного переменного. Введем процедурный тип func. Фрагмент программы, в котором значения функций f1 и f2 выводятся на экран, оформим в виде процедуры PrintFunction. Program Procedures_and_functions_3; type func = function (x: real): real; Function f1 (x: real): real; far; begin f1 := (sin(x) + 1) * exp(-x) end; Function f2 (x: real): real; far; begin f2 := (cos(x) + 1) * exp(-x) end; Procedure PrintFunction (f: func); {Формальный параметр f имеет процедурный тип func} {Описание локальных переменных} var a, b, delta: extended; begin {PrintFunction} write('Задайте отрезок, на котором '); writeln('будут вычисляться значения функции:'); write('Начало отрезка: '); readln(a); write('Конец отрезка: '); readln(b); write('Задайте шаг вычисления '); write('значений функции: '); readln(delta); b := b + 0.0001; while a < b do begin write(' x = ', a:5:3); writeln(' f = ', f(a):5:3); {Обращение к функции f в операторе writeln} a := a + delta end; writeln end; {PrintFunction}
12
{Основной текст - исполняемая часть программы} begin {Program} writeln('Таблица функции (SIN(X)+1)*EXP(-X):'); {Обращение к процедуре PrintFunction с фактическим параметром-функцией f1} PrintFunction(f1); writeln; writeln('Таблица функции (COS(X)+1)*EXP(-X):'); {Обращение к процедуре PrintFunction с фактическим параметром-функцией f2} PrintFunction(f2); writeln end. {Program} ПЕРЕЧИСЛЯЕМЫЙ ТИП, ТИП-ДИАПАЗОН И ОПЕРАТОР CASE
Язык Turbo Pascal позволяет программисту определять свои собственные типы данных. Здесь будет рассмотрен перечисляемый тип данных, который задается перечислением тех значений, которые может принимать переменная этого типа. Типы данных, определяемые программистом, должны быть описаны в разделе описаний программы. Описание типов должно предшествовать описанию переменных. Описание перечисляемого типа: type <имя типа> = (<список значений через запятую>); Пример: пусть нам необходима переменная, которая должна принимать значения, соответствующие названиям месяцев. Определим тип month (месяц) и переменную m1 этого типа. type Month = (January, February, March, April, May, June, July, August, September, October, November, December); var m1: Month; Здесь каждое значение переменной m1 является строковой константой. К сожалению, русские слова употреблять нельзя!
13
Альтернативно, переменную перечисляемого типа можно объявлять в программе без предварительного описания самого типа. var <имя переменной>: (<список принимаемых значений через запятую>); var m2: (January, February, March, April, May, June, July, August, September, October, November, December); Перечислимый тип относится к порядковым типам данных. Данные любого порядкового типа имеют конечное множество возможных значений, которые можно определенным образом упорядочить. Каждому значению порядкового типа можно сопоставить порядковый номер значения. Первое значение в списке, определяющем перечисляемый тип, получает порядковый номер 0, следующие значения − 1, 2 и т. д. Максимальное число возможных значений − 65536. Ко всем порядковым типам применимы следующие функции: ord(x) возвращает порядковый номер значения выражения x; pred(x) возвращает предыдущее значение порядкового типа, соответствующее порядковому номеру ord(x)-1; succ(x) возвращает следующее значение порядкового типа, соответствующее порядковому номеру ord(x)+1. К порядковым типам относятся также все целые типы, логический (boolean) и символьный (char), а также тип-диапазон. Тип-диапазон − это подмножество элементов другого порядкового типа, задаваемое своим первым и последним значениями. Описание типа-диапазона: type <имя типа> = <минимальное значение> .. <максимальное значение>; Пример: определим тип MonthNumber (номер месяца) как интервал целых значений от 1 до 12. type MonthNumber = 1..12; var n1: MonthNumber;
14
Альтернативно, переменную типа-диапазона можно объявлять в программе без предварительного описания самого типа. var <имя переменной> : <минимальное значение> .. <максимальное значение>; var n2: 1..12; К переменным типа-диапазона применимы следующие функции: high(x) возвращает максимальное значение типа-диапазона, к которому принадлежит переменная x; low(x) возвращает минимальное значение типа-диапазона, к которому принадлежит переменная x. Использование перечисляемых типов и типов-диапазонов повышает надежность программы благодаря возможности контролировать множество значений, которые могут принимать переменные перечисляемых типов. Для контроля возможного выхода значений переменных за границы допустимого диапазона служит опция RANGE CHECKING (меню OPTIONS − COMPILER − RUNTIME ERRORS − RANGE CHECKING). В активном состоянии она позволяет контролировать выход значений переменных за границы диапазона. Существуют также директивы компилятора, которые позволяют включать (директива {$R+}) и отключать (директива {$R-}) контроль границ диапазона. Однако, значение переменной перечисляемого типа нельзя ввести с клавиатуры с помощью оператора read или вывести на экран с помощью оператора write, т. е. такие операторы, как readln(m1); write(m2); недопустимы. Для ввода-вывода данных перечисляемого типа необходимо писать специальные процедуры (см. ниже). В этих процедурах удобно использовать оператор выбора case − оператор, который позволяет выполнить то или иное действие в зависимости от значения специального параметра, называемого ключом выбора. Этот параметр может принимать значения любого порядкового типа. Оператор выбора: case <ключ выбора> of <значение ключа выбора>: <оператор 1>; <значение ключа выбора>: <оператор 2>; ... <значение ключа выбора>: <оператор N>; else <оператор N+1> end;
15
Оператор выбора работает следующим образом: сначала вычисляется значение параметра − ключа выбора, затем выполняется оператор, соответствующий вычисленному значению (он может быть составным). Если в списке выбора не будет найдена константа, соответствующая вычисленному значению, будут выполнен оператор, стоящий за служебным словом else. Ветвь else можно опускать. Если ветвь else опушена, а константа, соответствующая вычисленному значению ключа выбора не найдена, оператор case не выполнит никаких действий. Далее следует пример написания специальных процедур для того, чтобы пользователь мог задать значение переменной перечислимого типа и вывести на экран результат. Используется оператор case. Процедура ReadMonthNumber присваивает значение переменной m1, когда пользователь вводит номер месяца. В программе процедура заменяет оператор read. Процедуру присваивания значения переменной перечисляемого типа можно организовать и иначе. В следующей процедуре используется вспомогательная строковая переменная name (типа string, причем количество символов в строковой переменной не превышает 8). Строковая переменная не является переменной перечисляемого типа, поэтому в данном случае невозможно использовать оператор case. Процедура WriteMonth выводит на экран название месяца, соответствующее значению переменной m1. В программе процедура заменяет оператор write. Текст основной программы становится компактным благодаря использованию процедур. Program Case_operator; Procedure ReadMonthNumber (var m1: Month); var n1: MonthNomber; begin {ReadMonthNumber} write('Введите номер месяца: '); readln(n1); case n1 of 1: m1 := January; 2: m1 := February; 3: m1 := March; 4: m1 := April; 5: m1 := May; 6: m1 := June;
16
7: 8: 9: 10: 11: 12:
m1 m1 m1 m1 m1 m1
:= := := := := :=
July; August; September; October; November; December
end end; {ReadMonthNumber} Procedure ReadMonthName (var m1: Month); var name: string[8]; begin {ReadMonthName} write ('Введите название месяца: '); readln(name); if (name = 'январь') or (name = 'Январь') or (name = 'ЯНВАРЬ') then m1 := January; if (name = 'февраль') or (name = 'Февраль') or (name = 'ФЕВРАЛЬ') then m1 := February; if (name = 'март') or (name = 'Март') or (name = 'МАРТ') then m1 := March; if (name = 'апрель') or (name = 'Апрель') or (name = 'АПРЕЛЬ') then m1 := April; if (name = 'май') or (name = 'Май') or (name = 'МАЙ') then m1 := May; if (name = 'июнь') or (name = 'Июнь') or (name = 'ИЮНЬ') then m1 := June; if (name = 'июль') or (name = 'Июль') or (name = 'ИЮЛЬ') then m1 := July; if (name = 'август') or (name = 'Август') or (name = 'АВГУСТ') then m1 := August; if (name = 'сентябрь') or (name = 'Сентябрь') or (name = 'СЕНТЯБРЬ')
17
then m1 := September; if (name = 'октябрь') or (name = 'Октябрь') or (name = 'ОКТЯБРЬ') then m1 := October; if (name = 'ноябрь') or (name = 'Ноябрь') or (name = 'НОЯБРЬ') then m1 := November; if (name = 'декабрь') or (name = 'Декабрь') or (name = 'ДЕКАБРЬ') then m1 := December end; {ReadMonthName} Procedure WriteMonth (m1: Month); begin {WriteMonth} case m1 of January: writeln('январь.'); February: writeln('февраль.'); March: writeln('март.'); April: writeln('апрель.'); May: writeln('май.'); June: writeln('июнь.'); July: writeln('июль.'); August: writeln('август.'); September: writeln('сентябрь.'); October: writeln('октябрь.'); November: writeln('ноябрь.'); December: writeln('декабрь.') end end; {WriteMonth} {Основной текст - исполняемая часть программы} begin {Program} {Задается номер месяца} ReadMonthNumber(m1); {На экран выводится название месяца} write('Этот месяц - '); WriteMonth(m1); {Задается название месяца} ReadMonthName(m1); {На экран выводится количество дней в этом месяце}
18
write('В этом месяце '); case m1 of February: writeln('28 или 29 дней.'); April, June, September, November: writeln('30 дней.'); else writeln('31 день.') end; writeln end. {Program} ОДНОМЕРНЫЕ И МНОГОМЕРНЫЕ МАССИВЫ
Массивы относятся к структурированным типам данных. Такие типы данных состоят из многих элементов. Каждый из элементов, или компонентов, в свою очередь, принадлежит к определенному типу данных. Массив − это совокупность фиксированного числа компонентов одного и того же типа. К каждому из компонентов массива можно обращаться, указав его индекс (порядковый номер). Для того, чтобы описать массив, нужно указать тип его компонентов и тип индексов. Очевидно, что индексы массива должны иметь порядковый тип. Особенно часто используется тип-диапазон, поскольку он позволяет задать пределы изменения индексов, но ничто не запрещает использование любого другого порядкового типа. Исключение составляет целый тип longint и те типы-диапазоны, для которых тип longint является базовым. Описание одномерного массива: type <имя типа> = array [<тип индекса>] of <тип компонентов>; Примеры: type symbolic = array [byte] of char; {Массив символов, индексы принимают значения типа byte} vector = array [0..15] of real; {Массив действительных чисел; индексы принимают целочисленные значения от 0 до 15; использован тип-диапазон}
19
var s: symbolic; a, b: vector;
Альтернативно, переменную типа array можно объявлять в программе без предварительного описания самого типа. var <имя переменной>: array [<тип индекса>] of <тип компонентов>; var x: array [10..30] of byte; Подобным образом, однако, нельзя описывать переменные в заголовке процедуры или функции. Например, объявление процедуры Procedure P (y: array [0..15] of real); является неправильным и вызовет ошибку, поскольку в списке формальных параметров процедуры или функции могут быть только параметры, которые имеют стандартный или заранее объявленный тип. В данном случае в заголовке процедуры фактически объявляется тип-диапазон, указывающий границы индексов массива, что недопустимо. Правильным является следующее объявление процедуры: Procedure P (y: vector); причем тип vector описан перед описанием процедуры P. Тип string[n] (строка) в языке Turbo Pascal во многом аналогичен типу array [0..n] of char, т. е. одномерному массиву символов (n − максимальное количество символов в строке, некоторая константа). В отличие от обычного массива, однако, количество символов в строке может меняться от 0 до n. Максимальное количество символов в строке можно не указывать, при этом длина строки принимает максимально возможное значение n = 255. К каждому символу в строке можно обращаться так же, как к элементу массива, например, var st: string[40]; ... if st[5] = 'a' then ... Если при обращении к некоторой процедуре или функции необходимо использовать переменную типа string, этот тип, так же, как и в случае массива, должен быть описан заранее. Иными словами, объявление процедуры Procedure P1 (z: string[10]); является недопустимым. Правильным в данном случае будет
20
type str = string[10]; ... Procedure P1 (z: str);
Многомерные массивы можно понимать как "массив массивов" и описывать следующим образом, например, type multiarray1 = array [0..5] of array [-2..2] of array [char] of integer;
Такое описание, однако, громоздко и неудобно. Существует более компактный способ. Описание многомерного массива: type <имя типа> = array [<тип индекса>, ... <тип индекса>] of <тип компонентов>; type multiarray2 = array [0..5, -2..2, char] of integer;
В языке Turbo Pascal можно с помощью одного оператора присвоить всем элементам одного массива значения, которые имеют элементы другого массива. Например, для массивов a и b, описанных выше, допустим оператор a := b; В то же время с помощью одного оператора присваивания нельзя присвоить всем элементам массива одно и то же значение, т. е. оператор типа b := 0; недопустим; необходимо использовать оператор цикла. Подобным образом, для того, чтобы сравнить элементы двух массивов, необходимо сравнивать их поэлементно, т. е. в цикле последовательно сравнивать соответствующие элементы. Использование оператора if a = b then ... в данном случае является недопустимым. Для того, чтобы ввести значения всех компонентов массива с клавиатуры или вывести значения всех компонентов массива на экран, также необходимо использовать операторы циклов. С помощью операторов read/write можно ввести/вывести только отдельные компоненты массива, иначе говоря, оператор
21
write(a[i]); является правильным, а оператор write(a); недопустим. Для ввода-вывода компонентов массива удобно писать специальные процедуры. Подчеркнем в связи с этим, что число компонентов массива всегда фиксировано. Это означает, в частности, что нельзя написать процедуру вывода на экран всех значений компонент одномерного массива, который содержит произвольное число компонентов. Недопустимо описание массива var n: byte; type c = array [1..n] of word; где n описана как переменная; если же n описана как константа, такое описание допустимо. Существуют определенные способы, которые позволяют работать с массивами с переменным числом компонентов, например, открытые массивы, когда описывается только тип компонентов массива, но не тип его индексов. В качестве примера работы с массивами рассмотрим программу, которая находит максимальные и минимальные элементы матрицы 5×5. Программу удобно разбить на несколько процедур. Program Arrays; type matrix = array [1..5, 1..5] of integer; var m: matrix; Procedure ReadMatrix (var m: matrix); {Процедура позволяет ввести с клавиатуры элементы матрицы m} var i, j: byte; begin {ReadMatrix} for i := 1 to 5 do for j := 1 to 5 do begin write('m[', i, ',', j, '] = '); read(m[i, j]) end end; {ReadMatrix}
22
Procedure WriteMatrix (m: matrix); {Процедура позволяет вывести на экран элементы матрицы m} var i, j: byte; begin {WriteMatrix} for i := 1 to 5 do begin for j := 1 to 5 do write(' m[', i, ',', j, '] =', m[i, j]:3); writeln end end; {WriteMatrix} Procedure MaxElement(m: matrix); {Процедура находит максимальные элементы матрицы m} var i, j: byte; max: integer; begin {MaxElement} max := m[1,1]; for i := 1 to 5 do for j := 1 to 5 do if m[i, j] > max then max := m[i, j]; writeln('Максимальные элементы матрицы M:'); for i := 1 to 5 do for j := 1 to 5 do if m[i, j] = max then writeln(' m[', i, ',', j, '] =', m[i, j]:3) end; {MaxElement} Procedure MinElement(m: matrix); {Процедура находит минимальные элементы матрицы m} var i, j: byte; min: integer;
23
begin {MinElement} min := m[1, 1]; for i := 1 to 5 do for j := 1 to 5 do if m[i, j] < min then min := m[i, j]; writeln('Минимальные элементы матрицы M:'); for i := 1 to 5 do for j := 1 to 5 do if m[i, j] = min then writeln(' m[', i, ',', j, '] =', m[i, j]:3) end; {MinElement} begin {Program} ReadMatrix(m); WriteMatrix(m); MaxElement(m); MinElement(m) end. {Program} ЛИТЕРАТУРА
1. В. Э. Фигурнов, IBM PC для пользователя. Краткий курс, М., 1999. 2. В. В. Фаронов, Turbo Pascal 7.0. Начальный курс, М., Изд-во "Нолидж", 1998. 3. Л. А. Бугаев, Г. М. Чечин, К. Н. Жучков, Методические указания к проведению практических занятий по курсу "Информатика". Вып. 1.1. Необходимые определения и команды для работы на компьютере. Элементы языка Pascal (часть I), Ростов-на-Дону, УПЛ РГУ, 1998. 4. Л. А. Бугаев, Г. М. Чечин, К. Н. Жучков, Методические указания к проведению практических занятий по курсу "Информатика". Вып. 1.4.Задачи для самостоятельного решения на компьютере, Ростов-на-Дону, УПЛ РГУ, 1998. 5. Шестакова Т. П. Методические указания по курсу "Программирование и вычислительная физика". Часть I. Основные конструкции языка Turbo Pascal. Простейшие типы данных, Ростов-на-Дону, УПЛ РГУ, 2003.