Массив и множество объединяют в себе компоненты только одного типа. В записи можно объединить компоненты различных типов, как простых, так и составных.
Предположим, что в памяти ЭВМ необходимо сформировать архив данных по отделу кадров предприятия. Личная карточка может содержать следующие сведения:
- фамилия, имя, отчество;
- дата рождения;
- национальность;
- должность;
- зарплата по месяцам;
- номер отдела или цеха;
- дата поступления на работу;
- образование и др.
Для графы "Образование" могут указываться более подробные сведения:
- если образование неполное среднее, то никаких дополнительных сведений;
- если образование среднее, то год окончания школы;
- если образование среднее специальное или высшее, то год окончания учебного заведения, его наименование, полученная специальность.
Данные в личной карточке имеют различный тип (целые числа, вещественные числа, строки и т.п.); кроме того, в этих данных есть фиксированная часть, одна и та же во всех карточках, и переменная часть, зависящая от графы "Образование". Эти данные размещаются соответственно в фиксированной и вариантной частях записи.
Структура типа записи:
Список полей:
Список полей может быть пустым, может иметь только фиксированную или только вариантную части, а также и фиксированную, и вариантную части.
Синтаксическая диаграмма фиксированной части представлена на следующей странице.
Как видно из диаграммы, синтаксис фиксированной части записи аналогичен синтаксису раздела описания переменной.
Предположим, что нам требуется выполнить действия над комплексными числами. В Паскале нет данных комплексного типа. Такие данные программист должен сам определить в программе.
Комплексное число имеет действительную и мнимую части, например, 5,1 - 2 i ; 0 + i . Поскольку эти две части составляют единое целое, то комплексную переменную удобно представить в программе в виде записи, которая объединяет указанные два поля:
Type Complex = record
Re : real; { действительная часть }
Im : real; { мнимая часть }
end;
Поскольку типы для полей Re и Im одинаковы, то их можно объединить:
Type Complex = record
Re,Im : real;
end;
Vara,b : Complex;
Примечание. Re и Im – общепринятые обозначения в математике для действительной и мнимой частей комплексного числа (от слов Real – действительный и Implicit – неявный, мнимый).
В массиве длины всех его компонентов одинаковы. Поэтому для доступа к компоненту массива достаточно указать порядковый номер компонента, определяемый его индексом, после чего программа легко вычисляет местоположение этого компонента, т.е. его адрес в памяти.
В записи длины компонентов (полей записи) могут быть различными. Поэтому для доступа к полям записи используют составное имя, включающее в себя имя записи и имя поля, разделенные точкой. Например, a.Re, b.Im .
Структура поля a:
½¾¾ a.Re ¾¾®½¾¾ a.Im ¾¾®½
½¾¾¾¾¾¾¾ a ¾¾¾¾¾¾¾¾®½
Здесь
a - имя поля памяти длиной 12 байт с адресом a + 0;
a.Re - имя поля памяти длиной 6 байт с адресом a + 0;
a.Im - имя поля памяти длиной 6 байт с адресом a + 6.
Ввод-вывод записей из текстовых файлов, как и массивов, осуществляется поэлементно:
Read(a.Re,a.Im);
Writeln('b= ',b.Re,' ',b.Im).
Если две записи имеют одно и то же имя типа, то пересылку одной записи в другую можно выполнить одним оператором присваивания: b := a (это справедливо для любых переменных, как простых, так и составных, если типы этих переменных тождественны).
Пример 1. Для комплексных переменных вычислить .
Как известно,
;
.
ProgramComp;
Type Complex = record
Re,Im : real;
end;
Varx,y,p,q,z,r : Complex;
{ ------------------------------------ }
Procedure Sum(Var u,v,w : Complex);
Begin
w.Re:=u.Re+v.Re; w.Im:=u.Im+v.Im;
End { Sum };
{ ------------------------------------ }
Procedure Mult(Var u,v,w : Complex);
Begin
w.Re:=u.Re*v.Re-u.Im*v.Im;
w.Im:=u.Re*v.Im+v.Re*u.Im;
End { Mult };
{ ------------------------------------ }
Begin
Read(x.Re,x.Im,y.Re,y.Im,p.Re,p.Im,q.Re,q.Im);
Mult(x,y,r); Mult(p,q,z);
Sum(r,z,z);
Writeln(z.Re,’ ‘,z.Im);
End.
Пример 2. В трехмерном пространстве заданы точек своими координатами. Сгруппировать эти точки в порядке их удаления от начала координат.
Точки можно описать как четыре отдельных массива: абсцисс, ординат, апликат и расстояний от начала координат:
TypeKoor = array[1..100] of real;
Var X,Y,Z,D : Koor;
В этом случае параметры одной и той же точки будут рассредоточены в четырех различных массивах, а для присваивания одной точке значений параметров другой точки потребуется четыре оператора присваивания:
x[i]:=x[j]; y[i]:=y[j]; z[i]:=z[j]; d[i]:=d[j] .
Каждая точка - это отдельный объект, обладающий определенными характеристиками, набор которых зависит от поставленной задачи. В данном случае рассматриваются четыре характеристики. Для каждой отдельной точки эти характеристики целесообразно объединить в одну группу, назвав их одним именем. Поскольку параметры x, y, z и d имеют одинаковый тип real, то это можно сделать с помощью массива или записи.
При использовании массива получим:
Type PointType = array[1..4] of real;
Var Point1,Point2 : PointType;
Так как Point1 и Point2 - это переменные одного типа, то для присваивания одной точке параметров другой точки достаточно одного оператора присваивания: Point2:=Point1.
Характеристики точки в описании ее типа PointType кодируются индексами массива. При разработке программы требуется постоянно помнить, что индекс 1 - это абсцисса, индекс 2 - ордината и т.д. Более удобной для описания отдельного объекта и его свойств является запись, позволяющая ставить в соответствие каждой характеристике объекта вполне определенное имя:
Type PointType = record
x,y,z,d : real
end;
VarPoint1,Point2 : PointType;
Для присваивания точке значений параметров другой точки здесь также требуется лишь один оператор: Point2:=Point1.
Кроме вещественных характеристик, точка может иметь также характеристики другого типа. Например, в графических задачах в набор характеристик точки можно добавить признак видимости, принимающий значения true или false. В этом случае для объединения всех характеристик в одну группу массив вообще не может быть использован.
В программе примера 2 для описания точек используется массив записей, группировка точек производится методом "пузырька". При вводе точек в записях заполняются компоненты Point.x, Point.y, Point.z; компонент Point.d вычисляется в программе.
Пример 3. Для каждого из студентов группы указаны следующие сведения: фамилия и инициалы, дата рождения, национальность, год окончания школы, пол. Отпечатать список юношей старше 18 лет.
ProgramStudGroup;
Const Nmax = 40;
TypeDate = record{ тип даты }
Day : 1..31; { день }
Month : 1..12; { месяц }
Year : 1970 .. 2005; { год }
end;
StudType = record{ тип личной карточки }
Fam : string[20]; { фамилия и инициалы }
BirthDay : Date; { дата рождения }
Nac : string[15]; { национальность }
SchoolYear : 1980..2005;{ год окончания школы }
Sex : char { пол }
end;
Nummer = 0..Nmax;
StudAr = array[Nummer] of StudType;
Vari,k,n : Nummer;
ch : char;
Today : Date; { текущая дата }
Student : StudType; { личная карточка }
Group : StudAr; { массив карточек }
FileStud : text; { исходный файл }
Begin
Assign(FileStud,'Stud.dat');
Reset(FileStud);
n:=0;
While noteof(FileStud) do
Begin
Inc(n);
Read(FileStud,Student.Fam);
Read(FileStud,Student.BirthDay.Day,
Student.BirthDay.Month,Student.BirthDay.Year);
Repeat
Read(FileStud,ch);
Until ch<>' ';
Read(FileStud,Student.Nac);
Insert(ch,Student.Nac,1);
Read(FileStud,Student.SchoolYear);
Repeat
Read(FileStud,ch);
Until ch<>' ';
Student.Sex:=ch;
Readln(FileStud);
Group[n]:=Student;
End;
Close(FileStud);
Writeln('Введите текущую дату: день месяц год ');
Readln(Today.Day,Today.Month,Today.Year);
Writeln(' СПИСОК ЮНОШЕЙ СТАРШЕ 18 ЛЕТ');
k:=0;
Fori:=1 ton do
If (Group[i].Sex='м') and(Today.Year-
Group[i].BirthDay.Year>18) then
Begin
Inc(k);
Writeln(k:2,' ',Group[i].Fam);
End;
End.
Комментарии к программе StudGroup:
1. В программе предполагается, что переменная Student.Fam записана с первой позиции строки текстового файла.
2. В одной строке входного файла одновременно размещены разнотипные элементы: числа и строки. Эти элементы отделены друг от друга одним или несколькими пробелами. Поскольку при вводе переменной типа stringпробел воспринимается как обычный символ, то для индикации начала текстовой строки в цикле Repeatперебираются позиции строки файла до тех пор, пока в символьную переменную ch не будет прочитан символ, отличный от пробела, после чего читается строковая переменная Student.Nac. Поскольку первый символ графы "Национальность" был прочитан в переменную ch, то его добавление в переменную Student.Nac производится процедурой Insert.
3. Ввод строки из текстового файла осуществляется по объявленной в программе длине этой строки. Поэтому при формировании с клавиатуры текстового файла Stud.dat необходимо учитывать, что элемент Student.Fam должен занимать в строке файла не менее 20 позиций, а элемент Student.Nac - не менее 15 позиций.
4. В программе возраст вычисляется как разность между текущим годом и годом рождения. Более точно возраст нужно вычислять с учетом дня и месяца.
Программу StudGroup можно рассматривать как фрагмент программы учетно-административной задачи. Одним из элементов такой задачи является ввод из текстового файла с целью формирования архива соответствующих сведений (в данном случае архива личных карточек студентов).
Учетно-административные программы, как и другие промышленные программные системы, ориентированы на пользователей, которые не являются программистами. Одним из требований, предъявляемым к таким программам, является обеспечение максимально удобного сервиса.
С точки зрения пользователя, программа StudGroup имеет серьезный недостаток: при подготовке текстового файла необходимо постоянно следить, чтобы значение переменной Student.Fam занимало в строке файла не менее 20 позиций, а переменной Student.Nac - не менее 15 позиций, вне зависимости от текущей длины этих переменных. Для повышения сервиса пользователя более удобно было бы оставить лишь одно ограничение: элементы строки файла должны разделяться одним или несколькими пробелами. Пример реализации ввода записей Student из текстового файла с учетом указанного ограничения приведен ниже в программе ReadRecord.
ProgramReadRecord;
Const Nmax = 40;
Type Date = record{ тип даты }
Day : 1..31; { день }
Month : 1..12; { месяц }
Year : 1970 .. 2005; { год }
end;
StudType = record{ тип личной карточки }
Fam : string[20]; { фамилия и инициалы }
BirthDay : Date; { дата рождения }
Nac : string[15]; { национальность }
SchoolYear : 1980..2005;{ год окончания школы }
Sex : char { пол }
end;
Nummer = 0..Nmax;
StudAr = array[Nummer] of StudType;
Var i,k,k1,k2,n : byte;
Today : Date; { текущая дата }
Student : StudType; { личная карточка }
Group : StudAr; { массив карточек }
S1,S2 : string[80];
Code : integer; { код преобразования }
Cond : boolean;
FileStud : text; { исходный файл }
{ Текст функций Space и NotSpace }
Begin
Assign(FileStud,'Stud.dat');
Reset(FileStud);
n:=0;
While not eof(FileStud) do
Begin
Inc(n);
Readln(FileStud,S1);
k:=0; k2:=0; Cond:=true;
While Cond do
Begin
k1:=NotSpace(S1,k2+1);
If k1=0 then
Cond:=false
Else
Begin
k2:=Space(S1,k1+1);
Ifk2=0 then
Begin
k2:=length(S1)+1; Cond:=false;
End;
Inc(k);
S2:=Copy(S1,k1,k2-k1);
Case k of
1 : Student.Fam:=S2;
2 : Student.Fam:=Student.Fam+' '+S2;
3 : Val(S2,Student.BirthDay.Day,Code);
4 : Val(S2,Student.BirthDay.Month,Code);
5 : Val(S2,Student.BirthDay.Year,Code);
6 : Student.Nac:=S2;
7 : Val(S2,Student.ScoolYear,Code);
8 : Student.Sex:=S2[1];
end;
End;
End;
Group[n]:=Student;
End;
Close(FileStud);
Обработка массива записей Group и формирование
выходных документов
End.
В программе ReadRecord очередная строка текстового файла вводится в строковую переменную S1. После этого с помощью функций Space и NotSpace выделяются отдельные слова строки S1, формирование компонент записи Student производится в операторе Case в соответствии с порядковым номером k выделенного слова. При этом учтено, что между фамилией и инициалами студента в строке исходного файла имеется по крайней мере один пробел.
Примечание. Оператор
Student.Sex:=S2
был бы неверным, так как в левой части оператора записан символ (переменная типа char), а в правой части – строка (переменная типа string). В этом случае при трансляции было бы выдано сообщение «Type mismatch» (несоответствие типов).