Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Glava_11.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
1.11 Mб
Скачать

305

11. Записи

Задание 11.1.

Постановка задачи

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

Выбор метода решения и проектирование

Правила приема известны всем, поэтому на них останавливаться не будем1. Тем не менее, договоримся о некоторых аспектах, которые могут оказаться существенными для нашей задачи. Во-первых, договоримся, что знания абитуриентов по предметам оцениваются целыми натуральными числами. Шкала (пятибальная или «богзнаетсколькибальная») в данном случае значения не имеет, т.к. оцениваются абсолютные значения сумм. Во-вторых, претендентов сотни, следовательно, данные о них и результаты расчетов должны храниться в файле. В исходном файле должна содержаться следующая информация:

  • ФИО абитуриента (string);

  • Оценка по физике (Integer);

  • Оценка по математике (Integer);

  • Оценка по информатике (Integer);

  • Оценка по русскому языку (Integer).

Основная проблема, возникающая в этой задаче – как хранить исходные данные. В данных явно присутствуют элементы, по крайней мере, двух разных типов: string и Integer и поэтому для работы с ними нельзя использовать ни один из рассмотренных нами выше типизированных нетекстовых файлов. С другой стороны, данные об абитуриентах, все-таки, имеет четко выраженную повторяющуюся структуру, а для таких файлов, как известно, рекомендуется использовать именно типизированные файлы. Получается замкнутый круг, дилемма: либо рекомендация не верна, либо мы не все знаем о файловых типах. В предположении, что не верна, все-таки, рекомендация попробуем определить некий гибрид: «текстовый файл с четко выраженной повторяющейся структурой», а именно, договоримся, что он будет иметь следующий формат:

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

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

Текст программы:

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

FIO – строковый массив, предназначенный для хранения паспортных данных об абитуриенте;

Phis – целочисленный массив, предназначенный для хранения оценок, полученных абитуриентами за экзамен по физике;

Inf – целочисленный массив, предназначенный для хранения оценок, полученных абитуриентами за экзамен по информатике;

Mat – целочисленный массив, предназначенный для хранения оценок, полученных абитуриентами за экзамен по математике;

Rus – целочисленный массив, предназначенный для хранения оценок, полученных абитуриентами за экзамен по русскому языку;

Res – целочисленный массив, предназначенный для хранения суммарной оценки, полученных абитуриентами по результатам вступительных экзаменов.

program AbituraV1;

var

I,J,N,Max:Integer;

Phis,Inf,Mat,Rus,Res:array[1..200] of Integer;

FIO:array[1..200] of string;

C0:Integer;

F0:string;

F,T:Text;

begin

Assign(F,'abitur.txt');

ReSet(F);

N:=0;

while not EOF(F) do

begin

N:=N+1;

ReadLn(F,FIO[N]);

ReadLn(F,Phis[N],Inf[N],Mat[N],Rus[N]);

Res[N]:=Phis[N]+Inf[N]+Mat[N]+Rus[N];

end;

Close(F);

for I:=1 to N-1 do

begin

Max:=I;

for J:=I+1 to N do

if Res[J]>Res[Max]

then Max:=J;

C0:=Res[I];

Res[I]:=Res[Max];

Res[Max]:=C0;

F0:=FIO[I];

FIO[I]:=FIO[Max];

FIO[Max]:=F0;

C0:=Phis[I];

Phis[I]:=Phis[Max];

Phis[Max]:=C0;

C0:=Inf[I];

Inf[I]:=Inf[Max];

Inf[Max]:=C0;

C0:=Mat[I];

Mat[I]:=Mat[Max];

Mat[Max]:=C0;

C0:=Rus[I];

Rus[I]:=Rus[Max];

Rus[Max]:=C0;

end;

Assign(T,'stud.txt');

ReWrite(T);

for I:=1 to N do

begin

WriteLn(FIO[I]);

WriteLn(T,FIO[I]);

WriteLn(T,Phis[I]:3,Inf[I]:3,Mat[I]:3,Rus[I]:3);

end;

Close(T);

WriteLn(‘Расчет окончен. Нажмите клавишу Еnter’);

ReadLn;

end.

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

Обращение к отдельным полям осуществляется с помощью уточненных идентификаторов (см. раздел «Лексемы»).

В качестве базового типа может использоваться любой предопределенный или объявленный в разделе TYPE тип данных (в том числе и запись).

Рисунок 11.1 - Размещение записей в ОЗУ

а)

2

5.2

б)

1

2.5

запись А'

1

2.5

запись В'

в)

5.2

4

6.2

3

1.2

2

9.5

2

5.1

г)

5

7.1

запись в записи

№ п.п.

Фрагмент программы

Комментарии

1.

var

A:record

X:Integer;

Y:Real;

end;

Во фрагменте приведено описание переменной A типа запись, которая включает в себя два поля: A.X – целое и A.Y –действительное (рисунок 11.1а).

2.

var

A,B:record

X:Integer;

Y:Real;

Z:string;

end;

begin

A.X:=1;

A.Y:=2.5;

B:=A;

А.Z:=’запись A’;

B.Z:=’запись B’;

end.

Во фрагменте приведено описание двух переменных A и B типа запись, каждая из которых включает в себя три поля: X – целое, Y –действительное и Z – строковое (рисунок 11.1б).

3.

type

TMyRec= record

X:Integer;

Y:Real;

end;

var

A:TMyRec;

F:file of TMyRec;

begin

ReadLn(A.X,A.Y);

Write(F,A);

end.

Во фрагменте приведено описание простой переменной A типа запись и файла F, каждый элемент которого является записью. Обратите внимание: прочитать целиком (например, с помощью оператора ReadLn(A)) такую запись с клавиатуры не представляется возможным, т.к. клавиатуре поставлен в соответствие текстовый буфер. Возникает ошибка несоответствия типов: буфер текстовый, а мы пытаемся прочитать информацию типа запись. Поэтому ввод значений записи с клавиатуры (или вывод их на экран дисплея) осуществляется по полям.

4.

type

TMyRec= record

X:Integer;

Y:Real;

end;

var

C:array[1..5] of TmyRec;

Во фрагменте приведено описание массива C, каждый элемент которого представляет собой запись, состоящую из двух полей: X – целое, и Y - действительное (рисунок 11.1в).

5.

type

TMyRec= record

X:Integer;

Y:Real;

end;

var

A: record

S:string;

L:TMyRec;

end;

begin

A.S=:’запись в записи’;

A.L.X:=5;

A.L.Y:=7.1;

end.

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

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

program AbituraV2;

type

TAbit= record

FIO:string;

Phis,Inf,Mat,Rus,Sum,Res:Integer;

end;

var

I,J,N,Max:Integer;

Abit:array[1..200] of TAbit;

C0:TAbit;

F:Text;

T:file of TAbit;

begin

Assign(F,'abitur.txt');

ReSet(F);

N:=0;

while not EOF(F) do

begin

N:=N+1;

ReadLn(F,Abit[N].FIO);

ReadLn(F,Abit[N].Phis,Abit[N].Inf,Abit[N].Mat,

Abit[N].Rus);

Abit[N].Res:=Abit[N].Phis+Abit[N].Inf+

Abit[N].Mat+Abit[N].Rus;

end;

Close(F);

for I:=1 to N-1 do

begin

Max:=I;

for J:=I+1 to N do

if Abit[J].Res>Abit[Max].Res

then Max:=J;

C0:=Abit[I];

Abit[I]:=Abit[Max];

Abit[Max]:=C0;

end;

Assign(T,'stud.dat');

ReWrite(T);

for I:=1 to N do

begin

WriteLn(Abit[I].FIO);

Write(T,Abit[I]);

end;

Close(T);

WriteLn(‘Расчет окончен. Нажмите клавишу Еnter’);

ReadLn;

end.

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

Работа оператора присоединения продемонстрирована в программе AbituraV3, которая с точностью до протоколов прохождения контрольных расчетов идентична программе AbituraV2:

program AbituraV3;

type

TAbit= record

FIO:string;

Phis,Inf,Mat,Rus,Res:Integer;

end;

var

I,J,N,Max:Integer;

Abit:array[1..200] of TAbit;

C0:TAbit;

F:Text;

T:file of TAbit;

begin

Assign(F,'abitur.txt');

ReSet(F);

N:=0;

while not EOF(F) do

begin

N:=N+1;

with Abit[N] do

begin

ReadLn(F,FIO);

ReadLn(F,Phis,Inf,

Mat,Rus,Rus);

Res:=Phis+Inf+Mat+Rus;

end;

end;

Close(F);

for I:=1 to N-1 do

begin

Max:=I;

for J:=I+1 to N do

if Abit[J].Res>Abit[Max].Res

then Max:=J;

C0:=Abit[I];

Abit[I]:=Abit[Max];

Abit[Max]:=C0;

end;

Assign(T,'stud.dat');

ReWrite(T);

for I:=1 to N do

begin

WriteLn(Abit[I].FIO);

Write(T,Abit[I]);

end;

ReadLn;

Close(T);

end.

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

program Example1;

var

X1:record

A,B:Integer;

end;

X2:record

A,X:Real;

end;

begin

X1.A:=1; X1.B:=2;

X2.A:=0; X2.B:=0;

with X1,X2 do

begin

A:=A+1;

X:=B+1;

end;

WriteLn(X1.A,X1.B);

WriteLn(X2.A,X2.X);

ReadLn;

e nd.

program Example2;

var

X1:record

A,B:Integer;

end;

X2:record

A,X:Real;

end;

begin

X1.A:=1; X1.B:=2;

X2.A:=0; X2.B:=0;

with X2,X1 do

begin

A:=A+1;

X:=B+1;

end;

WriteLn(X1.A,X1.B);

WriteLn(X2.A,X2.X);

ReadLn;

end.

Не смотря на кажущуюся идентичность программ, результаты их работы существенно отличаются1, причем, разные версии одного и того же языка могут выдавать различные результаты. Подобные ситуации принято называть конструкциями с неочевидной семантикой. При профессиональном подходе к программированию ситуаций с неочевидной семантикой следует избегать всеми доступными средствами2. Подробнее об этом см. главу 13 «Разработка больших программных комплексов».

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]