Цель работы– умение использовать многомерные массивы, а также вводить информацию из файла и выводить в файл.
Теоретическая часть.
Одномерные массивы удобны для описания таких математических объектов как, например, вектора. Однако для описания матриц, с каждым элементом которых связано два индекса, одномерные массивы неудобны. Поэтому введение понятия многомерного массива вполне естественно. Все сказанное ранее относительно объявления одномерных массивов можно распространить и на многомерные массивы, учтя при этом изменения в количестве индексов.
Описание массива может быть дано разными способами. В первом способе это делается в разделе описания переменных. При этом необходимо указать: имя массива, границы изменения индексов, тип элементов массива. Таким образом, вся информация о массиве приводится сразу.
var a: array[1…2,1…3] of real;
Здесь объявляется двумерный массив с именем a, состоящий из шести элементов вещественного типа. В квадратных скобках указываются границы изменения каждого индекса, разделенные запятой. В данном случае первый индекс принимает значения 1 или 2, а второй изменяется от 1 до 3.
|
|
В памяти компьютера элементы многомерного массива располагаются последовательно таким образом, что при переходе от одного элемента к другому, в первую очередь возрастает самый правый индекс. В рассмотренном примере двумерного массива это соответствует расположению элементов в памяти «по строкам», то есть в следующем порядке:
a[1,1], a[1,2], a[1,3], a[2,1], a[2,2], a[2,3].
При втором способе описания определяется новый тип данных, представляющих собой многомерный массив с заданными границами изменения индексов, а затем описываются переменные этого типа. Например,
type matrix = array [1.. 5, 1.. 5] of integer;
var a,b:matrix;
В этом примере определяются данные типа matrix (это массивы, состоящие из пяти строк и пяти столбцов целых чисел) и вводятся две переменные a и b этого типа. Такой способ описания позволяет дать используемым типам данных содержательные имена и тем самым сделать программу более наглядной.
Для многомерных массивов справедливо то, что было сказано в лабораторной работе №6 об использовании диапазонного типа данных при описании границ изменения индексов, например
type ind1=1..3; ind2=1..6;
matr= array[ind1, ind2] of real;
var a: matr;
Граничные значения индексов удобно объявлять как константы. При этом значения констант задаются в разделе описания констант:
const nmin1=1; nmax1=5; nmin2=1; nmax2=5;
type ind1=nmin1..nmax1;
ind2=nmin2.. nmax2;
matr =array[ind1,ind2];
var a,b:matr;
При таком способе объявления легко можно изменять граничные значения индексов, изменяя величину соответствующей константы.
|
|
Обращение к конкретному элементу массива осуществляется путем указания имени массива, за которым в квадратных скобках через запятую перечисляются индексы элемента. Причем в качестве индекса может выступать арифметическое выражение, дающее значение целого типа.
Для работы с многомерными массивами требуется организовать перебор индексов. При этом возникает ситуация, когда при изменении одного индекса другие тоже должны пробегать ряд значений. Это ведет к тому, что в теле одного оператора цикла помещается другой оператор цикла, в теле которого в свою очередь может содержаться оператор цикла и т.д. В итоге возникают вложенные циклы. Основное правило, которое при этом необходимо соблюдать заключается в том, что внутренний цикл должен целиком содержаться в теле внешнего. Вход в цикл разрешен только через его заголовок. Однако переход из внутреннего цикла в тело внешнего возможен, так как внутренний цикл является его частью.
Рассмотрим пример организации вложенных циклов с помощью оператора цикла с параметром. Эту конструкцию удобно использовать в тех случаях, когда количество повторений цикла заранее определено.Пусть требуется ввести двумерный массив
b [1.. 4,1.. 5]. Соответствующий фрагмент программы имеет вид:
for i:=1 to 4 do {цикл перебора строк}
begin
for j:=1 to 5 do {цикл перебора столбцов}
read (b[i,j]); {ввод элементов i-ой строки}
readln; {переход к новой строке ввода}
end;
В данном примере тело внешнего оператора цикла, ограниченное операторными скобками begin…end, содержит внутренний оператор цикла и оператор readln. Ввод элементов массива осуществляется по строкам. При этом на клавиатуре набираются через пробел элементы очередной строки и нажимается клавиша ввода.
Рассмотрим еще один пример. Пусть требуется вычислить суммы элементов каждого из столбцов матрицы x[1..5,1..5] и записать их в массив y[1..5]. Соответствующий фрагмент программы:
for j:=1 to 5 do { цикл перебора столбцов }
begin
y[j]:=0; {начальное значение суммы для каждого столбца}
for i:=1 to 5 do { цикл перебора строк }
y[j]:= y[j] + x[i,j] {подсчет суммы}
end;
Все действия, связанные с внутренним оператором цикла, в том числе и подготовка к нему, заключающаяся в занесении начальных значений искомых сумм y[j]:= 0, производится, в то время как второй y[j]:= y[j] + x[x, j] – двадцать пять раз.
Для работы этой программы необходимо ввести двадцать пять чисел. в процессе отладки программы, как правило, приходится несколько раз повторять ввод исходных данных. Даже в случае такого сравнительно небольшого массива на ввод с клавиатуры будет уходить много времени. Эту ситуацию можно исправить, если применить ввод данных из файла. При этом исходные данные записываются в файл один раз с помощью текстового редактора и хранятся на диске, как программа, а в нужный момент просто считываются из файла в соответствующем месте программы. Длина файла данных практически ограничена только емкостью свободной оперативной памяти. Результаты расчетов также можно поместить в файл, который позднее можно отредактировать (например, ввести комментарии), напечатать, сохранить.
Для того, чтобы программа, написанная на Турбо Паскале, могла работать с файлом данных, в ней должна быть введена специальная файловая переменная, имеющая тип text. Ее описание имеет стандартный вид
var имя переменной: text;
Переменная типа text – это так называемый текстовый файл. Он представляет собой последовательность символов, разбитую на строки произвольной длины.
Использование файловых переменных освобождает программиста от необходимости знания технических подробностей организации обмена данными между различными устройствами ЭВМ, так как программа работает не с конкретными физическими устройствами, а с их образами в виде файловых переменных. Для этого каждой файловой переменной ставится в соответствие физический файл – именованная область памяти на каком-либо внешнем носителе(обычно на диске). Организация подобной связи не регламентируется стандартом языка, а зависит от конкретной его реализации.
|
|
Пред обращением к файлу необходимо его открыть. для этого предусмотрены следующие две процедуры:
reset (имя файловой переменной); – открывает для чтения существующий файл,
rewrite (имя файловой переменной); – открывает файл для записи.
Если файл, открываемой для записи, к этому моменту уже существовал, то все данные из него будут стерты.
Чтение и записи данных в файл становятся возможными только после его открытия. Чтение данных из файла производится с помощью операторов:
read (имя файловой переменной, список ввода);
readln (имя файловой переменной, список ввода);
readln (имя файловой переменной);
Запись в файл производится с помощью операторов:
write (имя файловой переменной, список ввода);
writeln (имя файловой переменной, список ввода);
writeln (имя файловой переменной);
Имена файловых переменных должны быть указаны как параметры в заголовке программы.
Обратим внимание на то, что в использовавшихся до сих пор операторах ввода-вывода имена файловых переменных отсутствовали. Дело в том, что в них подразумевались стандартные имена, которые можно не указывать: input — для операторов ввода и output —для операторов вывода (однако, их необходимо указывать в заголовках программ). Вэтих случаях считается также, что в начале программы автоматически выполнены соответствующие операторы открытия файлов
reset (input); rewrite (output);
При этом заранее определено, с какими физическими устройствами связаны стандартные файловые переменные (обычно: input — с клавиатурой, output с экраном дисплея).
Все сказанное ранее о работе операторов ввода-вывода остается справедливым и в рассматриваемом случае. Разница лишь в том, что теперь мы явным образом указываем, на каком устройстве создавать тот или иной набор данных. Организация связи с конкретным устройством, как уже отмечалось, зависит от используемой системы программирования.
|
|
Например, в системе Турбо-Паскаль имеется оператор
assign (имя файловой переменной, 'имя файла');
Здесь имя файла — имя файла на диске, которое может включать не только имя и его расширение, но и описание пути к файлу в соответствии с правилами, принятыми в операционной системе. Если путь не указан, то подразумевается, что файл находится в текущем каталоге (из которого произошло обращение к системе Турбо-Паскаль). Оператор assign должен предшествовать операторам открытия файлов.
К моменту обращения к оператору reset соответствующий файл уже должен существовать на диске. Оператор rewrite создает новый файл, если файл с указанным именем не был создан ранее.
После завершения работы с файлами они должны быть закрыты с помощью оператора
close (имя файловой переменной);
Для иллюстрации введенных понятий рассмотрим полностью программу подсчета сумм элементов столбцов матрицы х, фрагмент которой был приведен ранее, написанную с учетом особенностей системы Турбо-Паскаль.
program example;
var i,i: integer; {объявление простых переменных}
у: array [1.. 5] of real; {объявление}
х: array [1.. 5,1.. 5] of real; {массивов}
f1,f2: text; {объявление файловых переменных}
begin
{связывание файловых переменных с конкретными файлами}
assign {fl,'Е: fLPRfinit.dat1);
assign (f2f rez.doc');
{открытие файла исходных данных для чтения}
reset(fl);
{открытие файла для записи результатов}
rewrite(f 2);
{чтение из файла исходных данных массива х по строкам}
for i:= 1 to 5 do
begin
for j:= 1 to 5 do
read (fl,x[i,j]);
readln (fl);
end;
{вычисление сумм}
for j:= 1 to 5 do
begin
y[j]:= 0;
for i:= 1 to 5 do
y[j]:= y[j] +x[i,j];
{запись элементов массива у в файл 'rez.doc’}
writeln (f2, y[j]);
{вывод элементов массива у на экран}
writeln (y[j]);
end;
{закрытие файлов}
close (f1);
close(f2);
end.
Исходные данные для программы должны быть помещены в файл init.dat, находящийся в каталоге LPR на диске Е, в следующем виде:
-1 | ||||
-2 | -4 | |||
-7 | -4 | -8 | -2 | |
Результаты записываются в файл rez.doc, созданный в процессе работы программы в текущем каталоге, и одновременно выводятся на экран дисплея. Отметим, что в заголовке программы вместо параметра input, относящегося к вводу с клавиатуры, указано имя файловой переменной f1, связанной с используемым здесь файлом исходных данных
Контрольные вопросы.
1. Дайте определение массиву.
2.Что такое размерность массива?
3.Какие способы описания массивов вы знаете?
4.Можно ли к массивам применять операции отношения?
5.Как располагаются в памяти элементы многомерного массива?
6.Можно ли один массив присвоить другому массиву того же типа?
7.Что называется файлом?
8.Как описывается файловая переменная текстового файла?
9.Как открыть файл для чтения?
10.Как открыть файл для записи?
11.Как закрыть файл?
Задания
При выполнении следующих заданий организовать ввод исходных данных из файла, находящегося на диске в текущем каталоге.
1. Дана квадратная матрица порядка N. Вычислить среднее арифметическое положительных элементов матрицы, стоящих выше главной диагонали.
2. Дана матрица размерности N на M. Найти строку, в которой максимальный элемент минимален в соответствующем столбце.
3. Дана матрица размерности N на M. Найти столбец, в которой максимальный элемент минимален в соответствующей строке.
4. Дана матрица размерности N на M. Найти в матрице первую по порядку строку с наибольшей суммой элементов. Вывести ее номер.
5. Дана квадратная матрица порядка N. В матрице вычислить среднее арифметическое положительных элементов, стоящих на главной диагонали.
6. Дана квадратная матрица порядка N. Вывести строку матрицы, в которой элемент, стоящий на главной диагонали, максимален среди элементов главной диагонали.
7. Дана матрица размерности N на M. Положительные элементы матрицы переписать подряд в одномерный массив В.
8. Дана матрица размерности N на M. Вычислить количество строк матрицы, в которых есть хоть один отрицательный элемент.
9. В квадратной матрице найти сумму элементов побочной диагонали и разделить на полученную сумму все элементы последнего столбца.
10. Дана матрица размерности N на M. Найти максимальный элемент и строку, содержащую этот элемент, поменять с первой строкой. Полученную матрицу вывести построчно.
11. Дана матрица размерности N на M. Вывести количество строк матрицы, в которых число положительных элементов больше числа отрицательных элементов.
12. Дана квадратная матрица порядка N. Найти произведение элементов побочной диагонали квадратной матрицы.
13. Дана матрица размерности N на M. Вывести номера всех столбцов матрицы, не содержащих отрицательных элементов.
14. Дана матрица размерности N на M. В матрице найти первый по порядку столбец с максимальной суммой элементов. Вывести его номер.
15. Дана матрица размерности N на M. Вычислить количество строк матрицы, в которых нет ни одного отрицательного элемента.
Лабораторные работы № 8, 9
ПОДПРОГРАММЫ: ФУНКЦИИ И ПРОЦЕДУРЫ
Цель работы — умение составлять и использовать функции и процедуры.
Теоретическая часть
При составлении сложных программ с разветвленной логикой естественно разбить программу на отдельные смысловые части. Тогда она легче воспринимается, поскольку детали низшего уровня будут спрятаны внутри сравнительно крупных структур. В качестве таковых могут рассматриваться подпрограммы. Подпрограммой будем называть именованную, логически законченную часть программы, которую можно вызывать для выполнения из разных мест основной программы. Использование подпрограмм увеличивает наглядность и, следовательно, понимание программ, а также является эффективным средством их разработки.
В языке Турбо Паскаль есть два вида подпрограмм: процедуры и функции. Они, в свою очередь, также подразделяются на две группы: предописанные (стандартные) и определенные пользователем. Предописанные процедуры и функции могут вызываться по имени без предварительного их определения в программе. Таковы, например, процедуры read, write; функции sin, ln и др. Вводимые пользователем процедуры и функции определяются самим программистом в соответствии с правилами языка.
Прежде чем воспользоваться подпрограммой, требуется составить ее описание. Оно помещается в разделе описаний основной программы после описания переменных. Описание подпрограммы выглядит как программа, то есть сначала записывается заголовок подпрограммы, за ним следует раздел описаний и раздел операторов. Последний ограничен операторными скобками begin... end.
Заголовок функции имеет следующий вид
function имя функции (список формальных параметров): имя типа;
Здесь function — служебное (зарезервированное) слово языка; имя функции — имя, выбираемое программистом; список формальных параметров — аргументы функции с указанием их типа; имя типа — тип значения, вырабатываемого функцией.
Список формальных параметров состоит из секций, отделяемых друг от друга точкой с запятой. Каждая секция включает в себя параметры одного типа и имеет формат
список имен переменных: имя типа;
где список имен переменных — имена формальных параметров, разделенных запятой; имя типа — общий тип этих параметров.
Пусть, например, в некоторой программе надо выполнять возведение в степень с произвольным целым показателем степени. Фактически необходима функция, которая позволяет вычислять выражения, подобные таким как: 3.143, 1.414-4, 17°.
Определим функцию, которую назовем power. Эта функция должна иметь два аргумента: вещественное число в качестве основания степени и целочисленное значение для показателя. С использованием этой функции приведенные выше выражения можно записать в следующем виде:
роwer(3.14,3), power(1.414, —4), power(17.0,0).
Сам процесс возведения в степень опишем в виде следующей подпрограммы-функции:
function power (num: real; expon: integer): real;
{ эта функция возводит вещественное число пит }
{ в целочисленную степень ехроn }
var count: integer; res: real;
begin
if expon = 0 then power:= 1 else
begin
res:= num;
for count:= 2 to abs(expon) do res:= res * num;
if expon < 0 then power:= 1 /res else power:= res
end
end;
В теле функции power выполняется проверка переменной ехроп на равенство нулю. Если равенство выполняется, то результат будет равен 1, поскольку любое число в нулевой степени дает 1. Если переменная ехроп не равна нулю, то возведение в степень осуществляется последовательным умножением с использованием переменной res. Результат вычисления значения функции присваивается имени функции — power.
Само по себе описание функции никаких вычислений не производит, а формальные параметры не имеют значений. Для того чтобы вычислить функцию, к ней следует обратиться, задав конкретные значения ее аргументов. Обращение к функции имеет вид
имя функции (список фактических параметров);
Фактические параметры в списке отделяются друг от друга запятыми. В качестве фактических параметров могут выступать константы, переменные, арифметические выражения. Их значения заменяют соответствующие формальные параметры в описании функции. Соответствие определяется на основе положения, занимаемого параметрами в списке. Поэтому списки должны быть согласованы друг с другом по числу параметров, порядку их следования и типу принимаемых значений. Обращение к функции может использоваться в программе так же как обращения к предописанным функциям sin. ln и т. п. Приведем пример обращения к функции power при вычислении арифметического выражения
hypotenuse:= sqrt (power(а, 2) + power(b, 2));
Рассмотрим программу, использующую функцию power.
program CallFunc;
var
a, b, hypotenuse: real;
{описание функции}
function power (num: real; expon: integer): real;
var
count: integer;
res: real;
begin
... { операторы функции power }
end; {power}
{конец всех описаний}
begin {начало раздела операторов программы CallFunc }
writeln ('Введите катеты треугольника');
readln(a, b);
hypotenuse:= sqrt(power(a, 2) + power(b, 2));
writeln('a =',a, ‘b =', b,’ с =', hypotenuse)
end.
Раздел описаний вместе с разделом операторов представляют собой блок. Таким образом каждая программа или подпрограмма состоит из заголовка и блока. Если в программе присутствуют подпрограммы, то возникают вложенные друг в друга блоки. Все описания констант, типов, переменных и подпрограмм являются локальными по отношению к тому блоку, в котором они выполнены. Это означает, что соответствующие имена имеют смысл (могут употребляться) только в той части текста программы, которая представляет собой данный блок. Такой фрагмент текста называется областью действия этих имен.
Таким образом, при введении в программу функций и процедур возникает разделение данных на глобальные и локальные. Глобальные переменные — это переменные, которые описаны в программе вне процедур и функций. Они могут использоваться в любом месте программы. Наоборот, локальные переменные описываются и существуют только внутри процедур или функций. Поскольку подпрограммы должны быть, по возможности, независимы от основной программы, то описывать алгоритм подпрограммы следует с помощью локальных переменных и формальных параметров.
Дополнительные действия функций и процедур, связанные с изменением глобальных параметров, называются побочными эффектами подпрограмм, их рекомендуется всячески избегать. Так. существует неписанное правило: если подпрограмма содержит в себе циклы for, то управляющие параметры циклов должны быть описаны как локальные переменные. Это предотвратит неразбериху при циклическом вызове процедур.
Описание процедур подобно описанию функций. Оно включает в себя заголовок процедуры, раздел описаний, раздел операторов:
procedure имя процедуры (список формальных параметров);
раздел описаний begin
раздел операторов end;
Обратим внимание на то, что, в отличие от заголовка функции, в заголовке процедуры не указывается тип результата. Эта разница возникает из-за того, что с именем процедуры не связывается какое- либо вычисляемое значение. Поэтому в разделе операторов проце- дуры отсутствует также и оператор, присваивающий значение имени процедуры.
Обращение к процедуре осуществляется с помощью оператора процедуры, который имеет вид
имя процедуры (список фактических параметров);
Как и в случае функции списки формальных и фактических параметров должны быть согласованы между собой.
В качестве примера использования процедуры приведем программу, в которой описана процедура, выводящая на экран строку из N черточек.
program Print Symbols;
var length: integer;
procedure PrintBars (N: integer);
var count: integer;
begin
for count:= 1 to N do write(‘_’)
end; {PrintBars}
begin
for length:= 1 to 5 do
begin
Print Bars(length);
writeln
end;
end.
Эта программа выводит на экран пять строк черточек, первая из которых содержит одну черточку, вторая — две и т. д.
Создадим процедуру, которая получает два числа х и у, выявляет из них большее и меньшее и помещает их в переменные max и min:
procedure MaxMin (х,у: integer; var max,min: integer);
begin
if x > у
then begin max:= x; min:= у end
else begin max:= y; min:= x end
end;
Приведем фрагмент программы, использующей данную процедуру:
var a, b,c,d: integer;
…………..
а:= 10; b:= 2;MaxMin(a + b, 9, с, d);
writeln(: большее=', с,’ меньшее=', d);
Перед выполнением процедуры переменные а и b равны соответственно 10 и 2, а с и d не имеют никаких начальных значений. Внутри процедуры переменная max получает значение 12, a min — 9. По возвращении в основную программу значением переменной с станет число 12, a d — число 9. В результате программа отпечатает: “большее=12, меньшее= 9”.
Поясним, почему перед параметрами max и min в заголовке процедуры появилось служебное слово var. Все параметры процедуры можно разделить на два класса: параметры, передающие значения в процедуру — это параметры-значения (входные параметры) и параметры, получающие значения в итоге работы процедуры — параметры-переменные (выходные параметры). Служебное слово var и указывает на то, что формальные параметры данной секции являются параметрами-переменными. В рассмотренном примере х, у — параметры-значения, max, min — параметры-переменные. При выполнении процедуры для параметров х и у отводится память, в которую помещаются значения соответствующих фактических параметров, то есть происходит присваивание х:= а + b, у:= 9. Для переменных max и min память не выделяется, а процедура работает с областями памяти, выделенными под соответствующие фактические параметры, то есть с и d. Другими словами можно сказать, что при выполнении процедуры вместо имен переменных max и min используются соответственно имена с и d.
Таким образом передать параметры подпрограмме можно двумя способами: как параметры-значения или как параметры-переменные. По первому способу перед началом выполнения процедуры вычисляется конкретное значение фактического параметра, которое присваивается переменной, обозначаемой соответствующим формальным параметром. Далее подпрограмма работает с этой переменной, значение которой не определено после выхода из подпрограммы. Следовательно, параметр-значение нельзя использовать для представления результатов работы подпрограммы.
При передаче параметра-переменной в подпрограмму пересылается не значение фактического параметра, а его имя, точнее адрес в памяти компьютера. Поэтому такой способ передачи называют передачей по имени или по адресу. В этом случае имена фактического и формального параметров можно считать синонимами, поскольку они занимают одну и ту же область памяти. Любые действия с таким формальным параметром, определенные в подпрограмме, будут выполняться непосредственно над соответствующим фактическим параметром. Параметры-переменные получают значения внутри процедуры, поэтому соответствующими фактическими параметрами не могут быть выражения или константы, а только переменные. Результаты работы процедуры следует определять с помощью параметров- переменных.
При рассмотрении функций параметры-переменные не использовались, так как ни один из параметров не должен получать новое значение при вычислении функции. Само значение функции присваивается имени функции.
Еще одна особенность описания подпрограмм связана с передачей в качестве параметра массива значений. Так как для параметров-значений отводится дополнительная память, то при передаче массива таким способом в подпрограмме создается его копия, для чего в случае больших массивов может потребоваться много памяти. Поэтому массивы обычно передают как параметры-переменные.
Кроме того, так как фактический и формальный параметры должны быть одного типа, то возникает вопрос о совместимости типов массивов. Частично он уже затрагивался ранее в работе 6 в связи с выполнением оператора присваивания для переменных-массивов.
Рассмотрим следующие описания:
type
Т1 = array [1..10] of real;
Т2 = array [1..10] of real;
var v1: Tl; v2: Tl; v3: T2;
Зададимся вопросом, принадлежат ли массивы vl, v2, v3 одному типу? Ответ зависит от того, как в конкретном языке трактуется эквивалентность типов. Выделяют два вида эквивалентности: структурную и именную. При структурной эквивалентности все три массива vl, v2, v3 будут принадлежать одному типу, поскольку их структуры (число компонент и их типы) абсолютно идентичны.
При именной эквивалентности массивы v1 и v2 принадлежат одному типу, а массив v3 — другому. Дело в том, что при описании массивов v\ и v2 использовано имя типа Т1, а при описании массива v3 — имя типа Т2. Имена типов различны, поэтому отличаются и переменные, описанные с их использованием. На выбор того или иного вида эквивалентности типов решающее влияние оказывают вопросы эффективности реализации. Как правило, именная эквивалентность типов реализуется проще, чем структурная, поскольку необходимо лишь проверить соответствие имен, а не анализировать структуру типа. Поэтому в большинстве языков программирования, в том числе и в Паскале реализована именная эквивалентность.
Таким образом, переменные считаются одного и того же типа, если при их описании использовано одно и то же имя типа. Поэтому, если массив является параметром подпрограммы, то надо ввести отдельное имя типа для параметров-массивов, описав его в разделе определения типов.
В качестве примера составим процедуру, которая в массиве из N заданных значений температур находит максимальную и определяет ее порядковый номер. Входными параметрами здесь являются массив значений температур Т и количество его компонент Num; выходными — максимальная компонента Мах и ее номер Imax.
Введем в основной программе тип row
type row = array [1..100] of real; а затем применим его для описания фактического массива Tempr:
var Tempr: row; и формального массива Т в списке параметров процедуры:
procedure FindMax(T: row;...);
Здесь многоточием обозначена остальная часть заголовка процедуры, относящаяся к другим параметрам.
Заметим, что было бы ошибкой использовать следующие описания
var Temp: array[1…100] of real;
procedure FindMax (T: array[1…100] of real;…);
Рассмотрим кратко алгоритм поиска максимума. Вначале переменной Max присваивается значение первого элемента массива, а переменной Imax – соответственно его номер, то есть 1. Затем в цикле каждая компонента массива T сравнивается со значением переменной Max, чтобы определить, не содержит ли она большее число. Если найдена компонента, большая чем Max, то значениеMax заменяется на новое, при этом изменяется и номер в Imax. По завершении цикла переменная max будет содержать наибольшее значение среди всех компонент массива T, а Imax - номер максимальной компоненты. Приведем полностью описание процедуры:
procedure FindMax (T: row; Num: integer;
var Max: real; var Imax: integer);
var i: integer;
begin
Max:=T[1]; Imax:= 1;
for i:=2 to Num do
if T[i]>Max then
begin Max:= T[i]; Imax:=i end
end;
Приведем программу, в которой используется разработанная процедура.
program MaxTemp;
type row=array [1…100] of real;
var Num,i,NumbMax: integer;
MaxTemp: real;
Temp: row;
procedure FindMax (T: row; Num: integer;
var Max: real; var Imax: integer);
………………….{ блок процедуры FindMax }
end; { конец описания FindMax}
begin
writeln (‘ введите число элементов’);
readln (Num);
writeln (‘ введите массив значений’);
for i:= 1 to Num do read (Tempr[i]);
{обращаемся к процедуре}
FindMax (Temp, Num, MaxTemp, NumbMax);
writeln(‘ Максимум=’, MaxTemp,’Номер=’, NumbMax)
end.
Процедуры и функции могут быть вложенными друг в друга. Число уровней вложения может быть достаточно большим, но на практике обычно не превышает двух. Вложенная процедура или функция относится к охватывающей ее подпрограмме точно так же, как сама подпрограмма относится к основной программе. Вложенные процедуры или функции могут вызываться только внутри охватывающей подпрограммы.
Контрольные вопросы.
1. Что такое процедура?
2.Чем отличается процедура от функции?
3.Что значит описать подпрограмму?
4. Что такое формальные и фактические параметры?
5. Что такое глобальное и локальные переменные?
6.Какими способами можно передать параметры подпрограмме?
7.Что такое параметр- значение?
8.Что такое параметр-переменная?
Задания
Для предложенных структур данных выполнить действия согласно индивидуальному варианту задания. При выполнении вариантов заданий использовать процедуры и функции с параметрами.
Произвести построчную распечатку значений элементов массива(-ов) при исходных данных и после их модификации. Примечание. При выводе данных на экран обязательно наличие комментариев, позволяющих понять принадлежность этих данных тому или иному массиву.
1. Дан одномерный массив размерности N. С помощью функций выполнить:
─ сортировку элементов массива по убыванию;
─ обнуление элементов массива, значение которых меньше заданного.
2. Дан одномерный массив размерности N. С помощью функций выполнить:
─ кольцевой сдвиг справа налево таким образом, чтобы в самой первой позиции оказался наибольший элемент;
─ линейный сдвиг слева направо таким образом, чтобы в последней позиции оказался наименьший элемент.
3. Дан одномерный массив А размерности N. С помощью функций выполнить:
─ заполнить массив В числами из массива А, значения которых меньше по модулю среднего арифметического элементов массива А;
─ заполнить массив С числами из массива А, значения которых больше половинного значения максимального элемента массива А.
4. Даны одномерные массивы А и В размерности N. С помощью функций выполнить:
─ заполнить массив С числами, получающимися попарным перемножением соответствующих элементов массивов А и В;
─ найти минимальное значение для массивов А и В, вывести название массива и номер позиции для найденного элемента.
5. Даны одномерные массивы А и В размерности N. С помощью функций выполнить:
─ поменять местами минимальный элемент массива А и максимальный элемент массива В;
─ определить, что больше: среднее арифметическое элементов массива А или среднее арифметическое модулей массива В.
6. Задана квадратная матрица размерности N х N. С помощью функций выполнить:
─ вычислить разность между суммой элементов, расположенных по периметру матрицы, и суммой элементов, расположенных на побочной диагонали;
─ обнулить все элементы матрицы, расположенные ниже ее главной диагонали.
7. Задана квадратная матрица размерности N х N. С помощью функций выполнить:
─ перестановку столбцов матрицы таким образом, чтобы они располагались в порядке убывания сумм их элементов слева направо;
─ поменять местами элементы главной и побочной диагоналей матрицы.
8. Задана квадратная матрица А размерности N х N. С помощью функций выполнить:
─ заполнить массив В размерности N элементами, полученными сортировкой по возрастанию побочной диагонали матрицы А;
─ удвоить содержимое элементов массива В и переписать его в главную диагональ матрицы А.
9. Задана квадратная матрица размерности N х N. С помощью функций выполнить:
─ выполнить прокрутку элементов матрицы, расположенных по периметру, на 1 позицию по часовой стрелке;
─ подсчитать количество положительных, отрицательных и нулевых элементов, расположенных по периметру матрицы.
10. Задана квадратная матрица размерности N х N. С помощью функций выполнить:
─ если какой-либо столбец матрицы содержит все “1”, поменять элементы этого столбца с симметричными им элементами относительно главной диагонали;
─ подсчитать количество элементов, квадраты которых лежат в пределах заданного пользователем диапазона.
11. Задана квадратная матрица размерности N х N. С помощью функций выполнить:
─ найти количество четных элементов матрицы, расположенных ниже побочной диагонали;
─ заменить все отрицательные элементы главной диагонали на их модули.
12. Задана квадратная матрица размерности N х N. С помощью функций выполнить:
─ упорядочить строки матрицы по возрастанию характеристик (считать характеристикой строки элемент, расположенный на главной диагонали);
─ поменять местами строки с наименьшим и наибольшим элементами.
13. Задана матрица размерности N х M. С помощью функций выполнить:
─ упорядочить столбцы матрицы по убыванию характеристик (считать характеристикой столбца его максимальный элемент);
─ подсчитать количество положительных элементов в нечетных строках матрицы.
14. Задана матрица размерности N х M. С помощью функций выполнить:
─ найти элемент, значение которого является ближайшим к значению, заданному пользователем;
─ подсчитать количество элементов, для которых значение совпадает с номером строки.
15. Задана квадратная матрица А размерности N х N. С помощью функций выполнить:
─ заполнить массив В элементами, получаемыми из минимальных элементов всех диагоналей матрицы А, параллельных главной (включая главную);
Лабораторная работа №10
МОДУЛИ
Цель работы — ознакомиться с имеющимися в языке Турбо- Паскаль для персональных ЭВМ дополнительными возможностями разработки больших программ.
Теоретическая часть
В стандарте языка Паскаль нет возможности раздельно компилировать части программы, а затем "собирать" их для выполнения. Поэтому в предыдущих работах описания всех используемых в программе типов данных, констант, переменных, процедур и функций включались в единый программный блок, что очень неудобно при разработке больших программ. Кроме того, в практике программирования часто необходимо использовать написанные ранее подпрограммы при создании новых программ. Например, подпрограмма решения системы линейных алгебраических уравнений может быть использована непосредственно для решения системы, при численном решении дифференциальных уравнений и ряда других задач. Поэтому в язык Турбо-Паскаль включено важное средство для независимой разработки отдельных частей программы и последующего их связывания в одну программу — модули.
Модуль - это отдельно создаваемая и компилируемая программная единица, имеющая собственное имя, которая может включать описания типов данных, переменных, констант, процедур и функций. Ссылаясь в программе с помощью специального объявления на имя модуля, можно использовать в ней все описанные в модуле средства. Модуль имеет следующую структуру:
unit имя модуля; interface
интерфейсная часть (раздел описаний) implementation
исполняемая часть (раздел реализации) begin
инициирующая часть end.
Модуль состоит из трех частей — интерфейсной, исполняемой и инициирующей. Рассмотрим подробно правила написания каждой части.
Первая строка текста модуля — это его заголовок. Он состоит из зарезервированного слова unit (модуль) и следующего за ним идентификатора — имени модуля. Это имя должно быть уникальным и совпадать с именем дискового файла, в который будет помещен исходный текст модуля. Имя служит для связи модуля с другими модулями и основной программой.
Раздел описаний начинается с зарезервированного слова inter face (интерфейс) и заканчивается перед зарезервированным словом implementation (выполнение). В этом разделе описываются константы, типы данных, переменные, процедуры и функции. Ко всем этим описаниям основная программа может обращаться так, как будто они выполнены непосредственно в ней. Поэтому интерфейсную часть называют еще "видимой" частью модуля, так как она определяет, что именно "видно" программе, использующей данный модуль. Константы, типы и переменные описываются в интерфейсной части обычным образом, а для процедур и функций здесь помещаются только заголовки (т.е. указания, как к ним правильно обращаться).
Следующий раздел — раздел реализации — начинается зарезервированным словом implementation. Все, что описано в интерфейсной части модуля (константы, типы, переменные, процедуры и функции), можно использовать и в разделе реализации. Кроме того, здесь помещаются описания объектов, которые являются локальными для модуля, то есть недоступными вызывающим модуль программам ("не видимыми" извне). Они используются процедурами и функциями, имена которых указаны в интерфейсной части. Описания этих процедур и функций также помещаются в разделе реализации. Здесь их заголовки могут быть записаны в краткой форме, т.е. без списков формальных параметров в виде
procedure имя;
function имя;
За заголовком следует блок подпрограммы.
Для локальных процедур и функций заголовки в разделе реализации записываются в полной форме, как обычно. Подчеркнем, что они могут использоваться только внутри модуля и не могут быть вызваны из другой программы. Последним разделом модуля, который может, в частности, и отсутствовать, является раздел инициализации. Если он отсутствует, то за последней строкой раздела реализации просто ставится слово end с обязательной после него точкой. Если же раздел инициализации имеется, то он состоит из последовательности операторов, ограниченной операторными скобками begin и end. В разделе инициализации могут присваиваться начальные значения переменным, открываться файлы данных, выдаваться различные диагностические сообщения. Операторы раздела инициализации выполняются только один раз при запуске программы, а не при каждом обращении к модулю.
Связь основной программы с модулями указывается с помощью объявления
uses список модулей;
где uses (использует) — зарезервированное слово языка; список модулей — разделенные запятыми имена модулей, к которым обращается программа. Данное объявление должно открывать раздел описаний основной программы.
Рассмотрим пример написания и использования модулей. Сформируем модуль с именем DMod, в который поместим функцию от вещественного f и целого п, вычисляющую через умножение величину . Опишем в модуле константу Author, которой присвоим в качестве значения фамилию автора модуля, например, 'Иванов'. В инициирующую часть поместим вывод сообщения: 'Работает модуль DMod, автор: Иванов'.
Заметим, что в предыдущей работе данная функция была описана внутри программы, в которой она использовалась, и обращаться к ней можно было только в этой программе. Оформленная в виде модуля она может использоваться разными программами. Приведем текст данного модуля.
unit DMod;
interface
const Author = 'Иванов';
function St (f: real; n: integer): real;
implementation
function St;
var i: integer;
b: real;
begin
if n =0 then St:= 1
else
begin
b:= f;
for i:= 2 to Abs(n) do b:= b* f;
if n < 0 then St:= 1/b else St:= b
end
end;
begin {инициирующая часть}
writeln (Работает модуль DMod, автор: ', Author)
end.
Наберем в редакторе этот модуль и сохраним его текст в файле DMod.pas. Имя файла, содержащего модуль, должно совпадать с именем модуля. Напишем теперь программу, вычисляющую значение выражения
S = а5 + (а + I)"7
для значения а, вводимого с клавиатуры, и обращающуюся к модулю DMod.
program ModTest;
uses DMod;
var a, rsum: real;
begin
writeln ('введите значение о');
readln(a);
rsum:= St(a, 5) + St(a + 1, -7);
writeln('Результат: ', rsum)
end.
Рассмотрим компиляцию программы, содержащей обращение к модулям. Один из способов ее выполнения — войти в пункт Compile главного меню и затем выполнить пункт Make в открывшемся подменю (тот же эффект без использования меню достигается нажатием клавиши [F9]). Обнаружив в программе объявление об использовании модуля, компилятор транслирует его и создает файл с именем модуля и расширением tpu (Turbo Pascal Unit). Этот файл будет помещен на диск в текущий каталог. Далее продолжается трансляция основной программы. После завершения компиляции выполнение программы осуществляется обращением к пунктам Run основного меню и подменю (или одновременным нажатием клавиш [Ctrl] и [F9],
Последнюю команду можно использовать и для компиляции, которая в этом случае также осуществляется в режиме Compile/Make. Если компиляция заканчивается успешно, происходит автоматический переход к выполнению программы.
Можно компилировать модули отдельно от основной программы. Для этого следует обратиться к пунктам Compile основного меню и подменю.
Контрольные вопросы.
1.Что такое модуль?
2.Из каких частей состоит модуль?
3.Как осуществляется связь основной программы с модулями?
Задания
Написать модуль, содержащий подпрограмму, а затем программу, использующую этот модуль.
1. В модуль включить подпрограмму вычисления площади треугольника по формуле Герона , где a,b,c – длины сторон, p – полупериметр.В основной программе ввести длины трех отрезков, проверить, можно ли построить треугольник для заданных длин сторон, и вычислить его площадь.
2. В модуль включить подпрограмму определения числа положительных элементов последовательности n вещественных чисел. В основной программе ввести две последовательности чисел и и определить, в какой из них больше положительных элементов.
3.
Приложение 1
СРЕДА ТУРБО-ПАСКАЛЯ
В настоящем приложении рассматривается обработка программ в широко распространенной системе программирования Турбо-Паскаль (версия 7.0). При этом даются лишь минимальные сведения, знание которых в первую очередь необходимо для успешной работы с системой.
Совокупность программ, обеспечивающих взаимодействие программиста с системой Турбо-Паскаль, составляет интегрированную среду Турбо-Паскаля (ИС). Чтобы попасть в ИС, следует, находясь в исходном каталоге, обратиться к программе, содержащейся в файле turbo.exe. Если каталог с Турбо-Паскалем (стандартное имя TP) находится, например, на диске D, а нужный нам файл в подкаталоге BIN, то соответствующая команда имеет вид
D: \ТР \ BIN \ turbo иф
где иф — имя файла (необязательный параметр).
После входа в ИС мы попадаем в программу, называемую редактором (в среду редактора). Он предназначен для набора и корректировки текстов, вводимых в компьютер, в частности, программ. На экране "распахивается" окно редактора. Если при входе в ИС указано имя существующего файла, то в окно редактора загружается текст, содержащийся в этом файле. Если файл под указанным именем не существует, то окно будет пустым. В верхней части окна расположены указатели меню, с помощью которого программист взаимодействует с ИС, в нижней части приведены назначения некоторых функциональных клавиш.
Редактирование. Позиция на экране, обрабатываемая редактором по текущей команде, отмечается курсором. Перемещение курсора по экрану осуществляется с помощью следующих клавиш:
[ ], [ ], [ ], [ ] — соответственно вправо или влево на одну позицию, вверх или вниз на одну строку;
[Home], [End] — в начало или конец строки;
[PgUp], [PgDn] -—• на одну страницу вверх или вниз по экрану (под страницей подразумевается часть текста, занимающая в данный момент весь экран);
[Ctrl]+[PgUp] — к самому первому символу текста;
[Ctrl]+[PgDn] — к последнему символу текста (при этом нажимаем клавишу [Ctrl], а затем, не отпуская ее, клавишу [PgUp] или [PgDn]).
Поместим курсор в левый верхний угол экрана и, последовательно нажимая соответствующие символьные клавиши, наберем первую строку вводимого текста (программы). Для перехода на следующую строку необходимо нажать клавишу [Enter], при этом в конце строки ставится символ-разделитель, который на экране не высвечивается.
Ошибки, возникающие в процессе набора, могут быть исправлены с помощью редактора. Для удаления ненужного символа следует подвести к нему курсор и нажать клавишу [Delete]. Другая возможность — поместить курсор непосредственно справа от ошибочного символа и нажать клавишу [Backspace].
Чтобы вставить в набранный текст новый символ, следует подвести курсор к той позиции, в которую необходимо поместить этот символ, и нажать соответствующую клавишу. При этом текст сдвигается вправо, и новый символ вставляется на указанное курсором место. Такая работа редактора соответствует режиму вставки. Он может быть отменен нажатием клавиши [Insert], При этом курсор принимает форму мигающего прямоугольника, сигнализируя о смене режима. В изменившемся режиме новые символы забивают старые. Возврат к режиму вставки осуществляется повторным нажатием клавиши [Insert].
В случае необходимости можно удалить всю строку, если поместить в нее курсор и нажать клавиши [Ctrl]+[Y], Если необходимо вставить новую строку в набранный текст, то курсор следует поместить в конец той строки, после которой необходимо сделать вставку и нажать [Enter] (при этом редактор должен работать в режиме вставки). Если возникает необходимость объединить две строки в одну, следует удалить символ-разделитель между ними, используя клавишу [Delete].
Для конкретизации дальнейшего изложения наберем, используя описанные средства, следующую простую программу.
program Sample;
var a, b,c,t: real;
begin
writeln('введите a,b ');
read(a, b); t:= a + b;
с:= t* ln(t);
writeln(c)
end.
Слово Sample в первой строке — это заголовок программы, поэтому далее будем ссылаться на нее как на программу Sample.
Набранную программу следует записать в память компьютера. Для этого нужно войти в главное меню (верхняя строка экрана), нажав клавишу [F10]. С помощью клавиш перемещения курсора по горизонтали высветим пункт меню с именем File. Нажав клавишу [Enter], увидим дополнительное меню, в котором следует обратиться к пункту Save as. Выделим этот пункт указателем с помощью клавиш управления курсором по вертикали и нажмем [Enter], Если имя файла было указано при входе в ИС, то запись будет произведена в файл под данным именем. В противном случае на экране появится так называемое диалоговое окно, где в поле Save file as следует набрать имя файла и нажать [Enter]. При этом к набранному имени будет автоматически добавлено расширение pas.
Обратим внимание на то, что в строке пункта меню Save указана клавиша [F2], Это означает, что соответствующее данной команде действие может быть сразу выполнено из среды редактора (то есть, не входя в меню) нажатием данной клавиши. Аналогичная возможность предусмотрена и для других часто используемых команд ИС. Далее для краткости будем указывать команды следующим образом: пункт главного меню/пункт дополнительного меню, альтернативный вариант с помощью клавиш. В такой записи команда сохранения будет иметь вид File/Save, [F2],
Итак, запишем нашу программу в файл, например, с именем prsample.pas. Чтобы убедиться в том, что запись действительно произведена, выйдем из ИС. Для этого выполним команду File/Exit, [Alt]+[X], Выйдя из среды, мы возвращаемся в тот каталог, из которого входили в нее. Убедившись в том, что, действительно, появился файл с именем prsample.pas и в нем содержится программа Sample, вернемся снова в ИС. Для этого дадим команду D: \ТР \ BIN \ turbo prsample, по которой мы попадем в среду редактора, причем в его окно будет загружена программа Sample. Если в команде не указывать параметр prsample, то окно редактора окажется пустым. Тогда нужную программу в него можно загрузить с помощью команды File/Open, [F3].
Компиляция. Программа Sample, в том виде как она набрана, еще не может быть выполнена компьютером. Следующий шаг — это перевод программы с языка Паскаль во внутреннее представление, обрабатываемое аппаратурой компьютера. Для этого служит специальная программа, называемая компилятором. Обратиться к ней можно по команде Compile/Make,[F9].
Одна из функций компилятора — это проверка обрабатываемого им текста программы на соответствие правилам языка Паскаль. При наличии ошибки компьютер помещает курсор около того места, где обнаружена ошибка, а в верхней части экрана появляется сообщение о характере ошибки. Часто этой информации бывает достаточно для того чтобы понять, в чем состоит ошибка. Если нажать клавишу [F1], то на экране появляется окно справочной службы с более подробной информацией об ошибке.
Допустим, набирая программу, мы пропустили точку с запятой в шестой строке. Анализируя это место, компилятор помещает курсор под символом с в следующей строке и выдает сообщение
Error 85: ";"expected (";"ожидается)
После нажатия любой клавиши информация об ошибке исчезает и восстанавливается режим редактирования.
Исправим допущенную ошибку, вставив пропущенный символ так, как это было описано выше. После исправления текста его новый вариант надо записать в память на место старого по команде File/Save, [F2], Далее будем снова обращаться к компилятору до тех пор пока все ошибки не будут исправлены. В таком случае компилятор выдает сообщение о том, что компиляция закончилась успешно. При этом компьютер создает новый файл с именем prsample.exe, который помещается в текущий каталог.
Теперь образ нашей программы, содержащийся в последнем файле, уже может быть выполнен компьютером. Для этого нужно дать команду Run/Run, [Ctrl]+[F9]. Заметим, что эта же команда может использоваться и для компиляции, причем, если последняя завершилась успешно, то происходит автоматический переход к выполнению. Программу можно выполнить и из текущего каталога, обратившись непосредственно к файлу prsample.exe.
Во время работы программы открывается окно выполнения, в которое помещаются результаты. После завершения программы восстанавливается окно редактора. Просмотреть окно выполнения можно, дав команду Debug/User screen, [Alt] -f-[F5].
Отладка. He следует считать, что если программа успешно прошла контроль компилятора и выдала определенный результат, то она обязательно правильная. Например, если в шестой строке вместо знака сложения ошибочно набран знак деления, то при успешном завершении программы будет получен некоторый результат, но он не является правильным.
Для выявления такого рода ошибок необходимо иметь контрольные примеры, результаты которых подсчитаны каким-либо другим способом. Решение таких примеров с помощью проверяемой программы называется тестированием. Каждая программа обязательно должна быть протестирована!
Другой вид ошибок в успешно откомпилированной программе — это так называемые ошибки периода выполнения. Например если в программе Sample входные данные о и 6 таковы, что а + b < О, то при попытке вычисления логарифма в седьмой строке программы компьютер прекратит вычисления и выдаст в окне редактора сообщение
Error 207: Invalid floating point operation
(Недействительная операция над числом с плавающей точкой).
Процесс поиска и исправления ошибок в программе, успешно прошедшей компиляцию, называется ее отладкой. Для облегчения отладки в ИС имеется отладчик, некоторые возможности которого мы сейчас рассмотрим.
Установим курсор в той строке программы, с которой начинается участок, работу которого мы хотим проверить, и выполним команду RunfGo to cursor, [F4], Программа начнет работать обычным образом, но остановится перед выполнением операций из той строки, на которую указывает курсор. Причем сама эта строка выделяется цветной полосой. Далее можно поступить двояко. Одна возможность — перевести курсор в новую строку и аналогично предыдущему выполнить программу до этой новой строки и т. д. Другая — использовать команду RunfTrace into, [F7]. По этой команде выполняются действия, запрограммированные в выделенной строке, после чего выполнение прекращается, а цветовой указатель смещается на следующую строку и т.д. В момент прерывания выполнения (в процессе отладки) можно просмотреть значения отдельных переменных. По команде Debug/Evaluate modify, [Ctrl]+[F4] на экране распахивается диалоговое окно. В содержащемся в нем поле Expression следует набрать имя переменной, значение которой мы хотим узнать. После нажатия клавиши [Enter] соответствующая величина появится в поле Result. Окно удаляется с экрана клавишей [Esc].
За изменением значений отдельных переменных или выражений можно следить в окне отладчика, которое открывается по команде Debug/Watch. Для того,чтобы поместить в него нужное имя переменной или выражение, его следует набрать сначала в диалоговом окне, вызываемом командой Debug/Add watch, [Ctrl]+[F7]. После нажатия клавиши [Enter] набранный текст появляется в окне отладчика. Чтобы продолжить выполнение программы, следует перейти в окно редактора (сделать его активным), нажав клавишу [F6], При этом окно отладчика удаляется с экрана. Повторное нажатие клавиши [F6] снова возвращает его на экран. Для удаления ненужной переменной или выражения из окна отладчика, следует сделать его активным, выделить цветом соответствующую строку (с помощью клавиш управления курсором по вертикали) и нажать клавиши [Ctrl] +[Y].
Закрыть (удалить из среды) активное окно можно с помощью команды Window/Close, [Alt]+[F3]. Выход из режима отладки осуществляется командой Run/Program reset, [Ctrl]+[F2].
Список использованной литературы