Назад Оглавление

Вперед

 

2.4. Комбинированный тип

     Комбинированный тип (тип запись) √ производный тип, значения которого состоят из нескольких компонент, которые могут иметь разные типы и доступ к которым осуществляется по именам. Данный тип данных содержит установленное число элементов или полей, которые могут быть различных типов. Описание типа запись указывает тип каждого поля и идентификатор, который именует поле. Форматом внутреннего представления данных комбинированного типа является непрерывная последовательность переменных. Первое поле хранится в младших адресах памяти.

      Ниже представлен синтаксис описания комбинированного типа.

<задание комбинированного типа>::= record <список полей> end

<список полей>::= <фиксированная часть> | <фиксированная часть> <вариативная часть>

<фиксированная часть>::= <список идентификаторов>: <тип> {; <список идентификаторов>: <тип>}

     Фиксированная часть типа запись содержит список фиксированных полей вместе с идентификатором и типом для каждого поля. Каждое поле содержит информацию, которая всегда отыскивается одним и тем же способом.

     Приведем пример описания комбинированного типа.

type

  DescriptionOfDay= record

    Year:  0..3000; { год }

    Month: 1..12;  { месяц }

    Day:   1..31    { день }

  end;

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

>var Date1: DescriptionOfDay;

...

Date1.Year:= 1799;

...

     Допустимо создавать на основе записей регулярные структуры √ массивы записей. Ниже приведен пример, в котором описан тип Week √ вектор записей.

type Week= array [ 1..7 ] of DescriptionOfDay; { неделя }

var Week1: Week;

     Здесь для обращения к конкретному полю необходимо спозиционироваться на элемент массива √ запись и указать имя поля:

...

Week1[ 2 ].Year:= 1899;

Week1[ 2 ].Day:= 1;

Week1[ 2 ].Month:= 10;

...

     Элементом комбинированного поля может быть запись √ таким образом получается иерархия записей. Например:

type

  DescriptionOfEvent= record

    Description: String;

    DateOfEvent: SomeDate

end

     Операция селектирования состоит в последовательном перечислении подуровней иерархии с самого верхнего (т.е. полной переменной) до нижнего (т.е. частичной переменной):

var SomeEvent: DescriptionOfEvent;

...

SomeEvent.DateOfEvent.Month:= 1;

...

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

<вариативная часть>::= case <идентификатор>: <тип признака поля> of <вариант> {; <вариант>}

<тип признака поля>::= <идентификатор порядкового типа>

<вариант>::= <константа> {, <константа>}: ( <список полей> )

align="justify"     В вариативных записях каждый вариант идентифицирован, по крайней мере, одной константой. Все константы должны быть отличными друг от друга и иметь порядковый тип, совместимый с типом поля признака. Доступ к вариантным и фиксированным полям один и тот же. В вариантной части можно указать необязательный идентифика╜тор - идентификатор признака поля. При наличии идентификатора признака поля он становится идентификатором дополнительного фиксированного поля записи - поля признака. Программа может использовать значение поля признака для указания, какой вариант является активным в настоящий момент. Ниже приведен ряд примеров описания вариативных записей.

type

DescriptionOfMan= record

  FirstName, LastName: string[40];

      BirthDate  : DescriptionOfDay;

      case Citizen  : boolean of

    True : (BirthPlace: string[40]);

    False: (Country  : string[20];

            EntryDate: DescriptionOfDay;

            ExitDate : DescriptionOfDay)

end

     Следует отметить, что вариативные записи позволяют экономить ресурсы, так как различные варианты используют одну и ту же область памяти.

     Для обработки данных с вариативным типом целесообразно использовать оператор выбора. Например:

var Man1: DescriptionOfMan;

...

case Man1.Citizen of

  True : writeln ( Man1.BirthPlace );

  False:

    begin

      writeln ( Man1.Country );

      writeln ( Man1.EntryDate.Year );

      writeln ( Man1.EntryDate.Month );

      writeln ( Man1.EntryDate.Day )

    end

end

...

     С целью упрощения позиционирования на конкретное поле записи введен специальный оператор √ оператор присоединения. Он имеет следующий синтаксис:

<оператор присоединения>::= <заголовок><оператор>

<заголовок>::= with <список переменных-записей> do

<список переменных-записей>::= <переменная-запись> {, <переменная-запись> }

     С использованием данного оператора фрагмент кода, представленный в предыдущем примере мог бы выглядеть следующим образом:

...

with Man1 do

  begin

    case Citizen of

      True : writeln ( BirthPlace );

      False:

        begin

          writeln ( Country );

          writeln ( EntryDate.Year );

          writeln ( EntryDate.Month );

          writeln ( EntryDate.Day )

        end

    end

end

...

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

1)      внутри оператора присоединения идентификатор, который может трактоваться как имя переменной или имя поля, интерпретируется как имя поля;

2)      разбор идентификаторов полей последовательности полных переменных производится справа налево.

Пример:

var

  FirstRecord: record

    A, B, C: Integer

  end;

  SecondRecord: record

    A, D: Integer

    B: record

      C, E: Integer

    end

  end;

...

with SecondRecord.B, FirstRecord do

  begin

    A:= 1; B:= 2; C:= 3; E:= 5

  end

...

{эквивалентно}

...

begin

  FirstRecord.A:=1;

  FirstRecord.B:= 2;

  FirstRecord.C:= 3;

  SecondRecord.B.E:= 5

end

...

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

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

const

  SomeDay: DescriptionOfDate= ( Year: 1960; Month: 1; Day: 2 );

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

     Разберем решение типичной задачи, связанной с обработкой записей.

Текст задания

     Существует следующее описание:

type

  Time1= record

    Hour: 0..23;

    Min, Sec: 0..59

  end;

var

  t, t1: Time1;

     Требуется описать процедуру NextSec ( t,  t1 ), присваивающую параметру t1 время, на секунду большее времени t  с учетом смены суток.

Решение

program RecordSample;

type

  Time1= record

    Hour: 0..23;

    Min, Sec: 0..59

  end;

var

  t, t1: Time1;

procedure NextSec( var t, t1: Time1);

begin

  t1:= t;

  with t1 do

    if Sec < 59 then Sec:= Sec + 1 {инкрементировать значение секунд}

      else begin {инкрементировать значение минут}

        Sec:= 0;

        if Min < 59 then Min:= Min + 1

          else begin {инкрементировать значение часов}

            Min:= 0;

            Hour:= ( Hour + 1 ) mod 24{учет суток}

          end

      end

end;

begin

  with t do begin

    repeat

      read( Hour );

      read( Min );

      read( Sec )

    until ( Hour<= 23 ) and ( Hour>= 0 )

      and ( Min<= 59 ) and ( Min>= 0 )

      and ( Sec<= 59 ) and ( Sec>= 0)

  end;

  NextSec( t, t1 );

  with t1 do begin

    write( Hour, ':' );

    write( Min, ':' );

    write( Sec )

  end

end.

Варианты заданий

1.      type field = record ord: (a,b,c,d,e,f,g,h); abc: 1..8 end; var x1, x2: field; Для вводимых x1 и x2 проверить, может ли ферзь за один ход перейти с поля x1 на шахматной доске на поле x2.

2.      const n = 300; type rec = record key: integer; body: array [1..99] of 'a'..'z' end; table = array [1..n] of rec; Упорядочить записи таблицы по возрастанию ключей.

3.      type day = 1..31; month = 1..12; year = 1..2000; mydate = record d: day; m: month; y: year end; Вычислить количество дней в месяце, которому принадлежит дата d: mydate.

4.      const n = 300; type rec = record key: integer; body: array [1..99] of 'a'..'z' end; table = array [1..n] of rec; Вывести запись с ключом k.

5.      Во входном файле записана следующая информация о каждом из 100 студентов некоторого вуза: <Фамилия>, <Имя>, <Отчество>, <Пол>, <Возраст>, <Курс>; причем, в фамилии, имени, отчестве не более 12-ти букв, пол - "М" или "Ж", возраст - целое от 16-ти до 35-ти, курс - целое от 1 до 5. Сведения о студентах отделены друг от друга точкой с запятой. Вывести фамилии трех самых взрослых студентов.

6.      type day = 1..31; month = 1..12; year = 1..2000; mydate = record d: day; m: month; y: year end; Проверить семантическую правильность введенной даты.

7.      Во входном файле записана следующая информация о каждом из 100 студентов некоторого вуза: <Фамилия>, <Имя>, <Отчество>, <Пол>, <Возраст>, <Курс>; причем, в фамилии, имени, отчестве не более 12-ти букв, пол - "М" или "Ж", возраст - целое от 16-ти до 35-ти, курс - целое от 1 до 5. Сведения о студентах отделены друг от друга точкой с запятой. Вывести номер курса, на котором самый большой процент мужчин.

8.      type day = 1..31; month = 1..12; year = 1..2000; mydate = record d: day; m: month; y: year end; Вычислить, сколько дней прошло с 1.01.01 до введенной даты.

9.      type field = record ord: (a,b,c,d,e,f,g,h); abc: 1..8 end; var x1, x2: field; Для вводимых x1 и x2 проверить, может ли ладья за один ход перейти с поля x1 на шахматной доске на поле x2.

10.  type time = record hour: 0..23; min, sec: 0..59 end; Для вводимых var t1, t2: time; вычислять функции отношения.

11.  Во входном файле записана следующая информация о каждом из 100 студентов некоторого вуза: <Фамилия>, <Имя>, <Отчество>, <Пол>, <Возраст>, <Курс>; причем, в фамилии, имени, отчестве не более 12-ти букв, пол - "М" или "Ж", возраст - целое от 16-ти до 35-ти, курс - целое от 1 до 5. Сведения о студентах отделены друг от друга точкой с запятой. Вывести в алфавитном порядке фамилии и инициалы всех студентов, возраст и отчества которых являются самыми распространенными.

12.  type time = record hour: 0..23; min, sec: 0..59 end; Для вводимых var t1, t2: time; вычислять интервал d = t2 - t1.

13.  Во входном файле записана следующая информация о каждом из 100 студентов некоторого вуза: <Фамилия>, <Имя>, <Отчество>, <Пол>, <Возраст>, <Курс>; причем, в фамилии, имени, отчестве не более 12-ти букв, пол - "М" или "Ж", возраст - целое от 16-ти до 35-ти, курс - целое от 1 до 5. Сведения о студентах отделены друг от друга точкой с запятой. Вывести самые распространенные мужские и женские имена.

14.  type field = record ord: (a,b,c,d,e,f,g,h); abc: 1..8 end; var x1, x2: field; Для вводимых x1 и x2 проверить, может ли конь за один ход перейти с поля x1 на шахматной доске на поле x2.

15.  Во входном файле записана следующая информация об итогах зимней сессии первого курса. Сведения о каждом студенте-первокурснике (всего их 400) заданы в виде следующего текста: <Фамилия>, <Номер группы>, <Оценка1>, <Оценка2>, <Оценка3>; причем, в фамилии 12-ти номер группы - целое от 101 до 116-ти, каждая оценка √ это 2, 3, 4 или 5, причем первая оценка √ за экзамен по матанализу, вторая по алгебре, третья √ по программированию. Сведения о студентах отделены друг от друга точкой с запятой. Вывести фамилии студентов, имеющих задолженность хотя бы по одному предмету.

16.  Во входном файле записана следующая информация об итогах зимней сессии первого курса. Сведения о каждом студенте-первокурснике (всего их 400) заданы в виде следующего текста: <Фамилия>, <Номер группы>, <Оценка1>, <Оценка2>, <Оценка3>; причем, в фамилии 12-ти номер группы - целое от 101 до 116-ти, каждая оценка √ это 2, 3, 4 или 5, причем первая оценка √ за экзамен по матанализу, вторая по алгебре, третья √ по программированию. Сведения о студентах отделены друг от друга точкой с запятой. ╚Качество╩ успеваемости, т.е. процент студентов, сдавших все экзамены на 4 и 5.

17.  Во входном файле записана следующая информация об итогах зимней сессии первого курса. Сведения о каждом студенте-первокурснике (всего их 400) заданы в виде следующего текста: <Фамилия>, <Номер группы>, <Оценка1>, <Оценка2>, <Оценка3>; причем, в фамилии 12-ти номер группы - целое от 101 до 116-ти, каждая оценка √ это 2, 3, 4 или 5, причем первая оценка √ за экзамен по матанализу, вторая по алгебре, третья √ по программированию. Сведения о студентах отделены друг от друга точкой с запятой. Название предмета, который был сдан лучше всего.

18.  Во входном файле записана следующая информация об итогах зимней сессии первого курса. Сведения о каждом студенте-первокурснике (всего их 400) заданы в виде следующего текста: <Фамилия>, <Номер группы>, <Оценка1>, <Оценка2>, <Оценка3>; причем, в фамилии 12-ти номер группы - целое от 101 до 116-ти, каждая оценка √ это 2, 3, 4 или 5, причем первая оценка √ за экзамен по матанализу, вторая по алгебре, третья √ по программированию. Сведения о студентах отделены друг от друга точкой с запятой. Номера групп в порядке убывания средней успеваемости студентов.

19.  const n= 300; type Col= record key: integer; body: array [1.99] of ▒a▓..▓z▓ end; table= array [1..n] of Col; Считая, что в таблице записи имеют различные ключи, описать процедуру sort( table ), упорядочивающую записи таблицы table по возрастанию их ключей.

20.  const n= 300; type Col= record key: integer; body: array [1.99] of ▒a▓..▓z▓ end; table= array [1..n] of Col; Считая, что в таблице записи имеют различные ключи, описать логическую функцию find( table, k, n ), определяющую, есть ли в таблице table (все записи которой уже упорядочены по возрастанию их ключей) запись с ключом k, и, если есть, присваивающую ее номер параметру n.

21.  type day= record date: 1..31; month: 1..12; year: 1900..1990 end; anket= record name: string; sex: (m, f); birthday: day end; group= array [1..25] of anket; Описать процедуру Oldest( gr, fam ), присваивающую строке fam фамилию самого старшего мужчины из группы gr (считать, сто такой есть и он единственный).

22.  type day= record date: 1..31; month: 1..12; year: 1900..1990 end; anket= record name: string; sex: (m, f); birthday: day end; group= array [1..25] of anket; Описать процедуру wr( gr, ch ), выводящую все фамилии людей из группы gr, начинающиеся с литеры ch, и даты рождения этих людей.

23.  type domino= record left, right: 1..6 end; vect= array [1..28] of domino; Описать логическую функцию rightvect( v ), которая проверяет, правильно ли выставлены кости домино в ряду v (равна ли правая цифра очередной кости левой цифре следующей кости).

24.  type name= (George, Alex, Piter, Ann, Paul, John, Fred, Natalie, Robert); data= record sex(m, f); hight: 140..220 end; group= array [ name ] of data; Описать функцию midhight( gr ), определяющую средний рост женщин из группы gr.

25.  type name= (George, Alex, Piter, Ann, Paul, John, Fred, Natalie, Robert); data= record sex(m, f); height: 140..220 end; group= array [ name ] of data; Описать функцию hight( gr ), для определения самого высокого мужчины из группы gr.

26.  type name= (George, Alex, Piter, Ann, Paul, John, Fred, Natalie, Robert); data= record sex(m, f); height: 140..220 end; group= array [ name ] of data; Описать логическую функцию onehight( gr ), проверяющую есть ли в группе gr хотя бы два человека одного роста.

27.  type decart= record x, y: real end; polar= record r, fi: real end; {r >=0, - < fi <=} Описать процедуру dp( d, p ), преобразующую координаты точки на плоскости из декартовых d в полярные p.

28.  type decart= record x, y: real end; polar= record r, fi: real end; {r >=0, - < fi <=} Описать процедуру pd( p, d ), преобразующую координаты точки на плоскости из полярных p в декартовые d.

29.  type name= (George, Alex, Piter, Ann, Paul, John, Fred, Natalie, Robert); data= record sex(m, f); height: 140..220 end; group= array [ name ] of data; Описать логическую функцию summhight( gr ), проверяющую больше ли средний рост мужчин, среднего роста женщин.

30.  Во входном файле записана следующая информация об итогах зимней сессии первого курса. Сведения о каждом студенте-первокурснике (всего их 400) заданы в виде следующего текста: <Фамилия>, <Номер группы>, <Оценка1>, <Оценка2>, <Оценка3>; причем, в фамилии 12-ти номер группы - целое от 101 до 116-ти, каждая оценка √ это 2, 3, 4 или 5, причем первая оценка √ за экзамен по матанализу, вторая по алгебре, третья √ по программированию. Сведения о студентах отделены друг от друга точкой с запятой. Номер группы с самым высоким процентом студентов, не имеющих одинаковых оценок по разным предметам.

Назад Оглавление

Вперед