Обращение к конкретным полям записи связано с некоторыми неудобствами. Составные идентификаторы получаются длинными. При совместной обработке ряда полей одной записи длину идентификаторов можно сократить.
Чтобы упростить доступ к полям записи, используется оператор присоединения With:
with <переменная> do <оператор>
Здесь with, do – ключевые слова (с, делать); <переменная> – имя переменной типа запись, за которым, возможно, следует список вложенных полей; <оператор> – любой оператор Турбо Паскаля.
Оператор присоединения with... do открывает запись. В поле его действия имена полей записи сокращаются. Например:
with c.bd do month:= 9;
Это эквивалентно
with c do with bd do month:= 9;
или
c.bd.month:= 9.
В такой записи не чувствуется преимущество использования оператора with. Когда обрабатываются все поля записи, оператор with... do дает существенную экономию в размере программы.
Задача 18. Составить программу формирования архива данных по изданным книгам. Данные включают: автора, название, год издания, город, где издана, и цену книги.
Предположим, что в архив необходимо занести данные о 50 книгах. Данные будут представлять собой массив записей. Каждая запись состоит из:
1) фамилии И. О. автора (author);
2) названия книги (title);
3) названия города, в котором книга выпущена, (city);
3) года издания (year);
4) цены (cost).
Данные author, title, city – символьного типа. Year – может быть переменной интервального типа. Цена (cost) должна быть переменной вещественного типа. Объявление нового типа данных entry как записного будет выглядеть следующим образом:
entry = record
author, title, city: string;
year: 1..9999;
cost: real;
end;
Далее следует объявить массив из 50 элементов, имеющих тип entry. Ввод элементов записи (рис. 16) организуем в цикле, причем окончание цикла зафиксируем вспомогательной переменной ch. Перед вводом содержимого очередной записи сделаем запрос на продолжение пополнения архива. Для ввода компонентов записи используем оператор readln. Компоненты записи в программе имеют составные имена: m[i].author, m[i].title, m[i].city, m[i].year, m[i].cost.
Программа:
program archives;
uses crt;
label 10;
type
entry = record {Выходные данные по книге}
author, title, city: string;
year: 1..9999;
cost: real;
end;
var
m: array[1..50] of entry;
{Архив не превышает 50 книг}
{Массив, элементами которого являются записи}
i: integer;
ch: char;
begin
i:= 0;
{Блок формирования архива данных по книгам}
writeln (‘Пополняешь архив? – Да – [Y]’);
ch:= readkey;
if upcase(ch) = ‘Y’ then
repeat
i:= i+1;
writeln(‘Автор книги ‘); readln(m[i].author);
writeln(‘Название книги ‘); readln(m[i].title);
writeln(‘Город издания ‘); readln(m[i].city);
writeln(‘Год издания ‘); readln(m[i].year);
writeln(‘Цена книги ‘); readln(m[i].cost);
writeln (‘Пополняешь архив? – Да – [Y]’);
ch:= readkey
until upcase(ch) <> ‘Y’
else
begin
write (‘Вы передумали? До свидания!’);
goto 10
end;
repeat until keypressed;
10:end.
Задача 19. Усложним предыдущую задачу. Составить программу подборки книг по указанному автору из сформированного массива записей.
Для формирования архива используем предыдущую программу. Введем переменную символьного типа ss – фамилию автора, список книг которого необходимо вывести (рис. 17). Размер архива запомним в переменной k. Для более компактного представления используемых переменных, воспользуемся оператором присоединения with... do. При этом имена полей записи могут фигурировать как имена обычных переменных. Для возможности вывода нескольких списков организуем внешний цикл с признаком окончания ch = ‘Y’.
Программа:
program archives2;
uses crt;
label 10;
type
entry = record {Выходные данные по книге}
author, title, city: string;
year: 1..9999;
cost: real;
end;
var
m: array[1..50] of entry;
{Архив не превышает 50 книг}
{Массив, элементами которого
являются записи}
i, j, k: integer;
ss: string; {Фамилия для поиска}
ch: char;
begin
i:= 0;
{Блок формирования архива
данных по книгам}
writeln (‘Пополняешь архив? – Да – [Y]’);
ch:= readkey;
if upcase(ch) = ‘Y’ then
repeat
i:= i+1;
with m[i] do
begin
writeln(‘Автор книги ‘); readln(author);
writeln(‘Название книги ‘); readln(title);
writeln(‘Город издания ‘); readln(city);
writeln(‘Год издания ‘); readln(year);
writeln(‘Цена книги ‘); readln(cost);
end;
writeln (‘Пополняешь архив? – Да – [Y]’)
ch:= readkey
until upcase(ch) <> ‘Y’
else
begin
write (‘Вы передумали? До свидания!’);
goto 10
end;
k:= i;
{Блок подборки всех книг по указанному автору}
repeat
writeln(‘Укажите фамилию автора’); readln(ss);
writeln(‘Список книг, автор которых ‘, ss: 15);
for i:= 1 to k do
with m[i] do
begin
if ss = author then
begin
writeln(title);
writeln(‘Город ‘,city);
writeln(‘Год издания ‘,year);
writeln(‘Цена ‘,cost:8:2);
writeln
end
end;
writeln(‘Нужно еще выдавать авторский список? – Да –[Y]’);
ch:= readkey;
until upcase(ch) <> ‘Y’;
repeat until keypressed;
10:end.
Вариантные записи
Состав данных зависит от вида объекта, на который составлена запись. Например, если выходные данные книги содержат ее название, год и место издания, название издательства, то для журнальной статьи важно знать название журнала, номер и год выпуска. Возникает необходимость внесения в структуру записи некоторой вариантной части. С помощью записей с вариантами можно одновременно сохранять различные структуры данных, которые имеют общую часть, одинаковую во всех структурах, и небольшие отличающиеся части в различных структурах.
Вариантный записной тип – это записной тип, содержащий несколько вариантов структуры записи.Различие может касаться как числа компонент, так и их типов.
Предположим, что есть, например, такое описание:
type status = (married, widowed, divorced, single),
что означает женатый, вдовец, разведенный, одинокий. В этом случае человека можно описать с помощью данных следующего типа:
type person = record
{здесь поля общие для всех person}
case status of
married: ({ поля только для семейных });
single: ({ поля только для одиноких });
...
end;
Каждому значению, относящемуся к типу, на основании которого идет различение вариантов (типу признака), должен соответствовать один из вариантов. Это означает, что перед вариантами должны появляться все значения типа признака. В нашем примере, кроме констант married и single, должны появиться и константы widowed и divorced.
Обычно некоторая компонента (поле) записи сама указывает, о каком варианте идет речь. В приведенном описании типа следовало бы иметь общее для всех вариантов поле ms: status. Описание определяющей вариант компоненты можно включить в заголовок варианта. Такое описание называется полем признака. Например:
case ms: status of
Прежде чем начать определять структуру записи с вариантами, соответствующую, например, типу person, полезно бывает выписать всю необходимую информацию.
1. Имя (name) – первое, последнее (first, last).
2. Рост (height) – целое число.
3. Пол (sex) – муж., жен. (male, fеmale).
4. Дата рождения (date) – год, месяц, день (year, month, day).
5. Число иждивенцев (depds) – целое число.
6. Семейное положение (status):
Если в браке (married) или вдов (widowed):
а) дата свадьбы (mdate) – год, месяц, день (year, month, day).
Если разведен (divorced):
а) дата развода (ddate) – год, месяц, день (year, month, day),
б) первый развод (firstd) – нет, да (false, true).
Если одинокий (single):
информации нет.
Определение записного типа person можно сформулировать так:
type
status = (married, widowed, divorced, single);
date = record
year: 1900..2100;
mo: (jan, feb, mar, apr, may, jun,
jul, aug, sep, oct, nov, dec);
day: 1..31;
end;
natural = 0..max;
person = record
name: record first, last: string[15] end;
height: natural;
sex: (male, female);
birth: date;
depdts: natural
case ms: status of
married, widowed: (mdate: date);
divorced: (ddate: date; firstd: boolean);
single: ()
end {person};
Синтаксическая диаграмма для вариантной части представлена на рис. 18.
Рис. 18
Синтаксическая диаграмма для варианта (рис. 19):
Рис. 19
Задача 20. Составить программу формирования перечня сведений по данным архива книг и журналов.
Данная задача отличается от задачи 19 тем, что состав данных в записи различается для книги и для журнала. Введем перечисляемый тип данных entrytype, определяющий варианты. type entrytype = (book, magazine).
Данные по каждому наименованию архива включают:
1) автора книги или статьи (author) – переменная типа string;
2) наименование книги или статьи (title) – string;
3) год издания книги (журнала) – интервальный тип 1... 1999;
4) если книга (book):
а) издательство (publisher) – string;
б) город (city) – string.
Если журнал (magazine):
а) наименование журнала (magname) – string;
б) номер журнала (vol) – integer;
в) количество страниц (page) – integer.
Программа:
program archives3;
uses crt;
label 10;
type
entrytype = (book,magazine);
{Вводится специальный тип данных, определяющий варианты}
entry = record
author, title: string;
year: 1..9999;
case tag:entrytype of
{Поле tag определяет ветвь варианта}
book: (publisher,city: string);
magazine: (magname: string; vol, page: integer)
end;
var
m: array[1..50] of entry;
i, j, k: integer;
ss: string;
ch: char;
begin
i:= 0;
{Блок формирования архива данных по книгам}
writeln (‘Пополняешь архив? – Да – [Y]’);
ch:= readkey;
if upcase(ch) = ‘Y’ then
repeat
i:= i+1;
writeln(‘Если вводите данные по книге, нажмите Y ‘);
ch:= readkey;
if upcase(ch) = ‘Y’ then
m[i].tag:= book
{Переменной варианта присваивается признак книги – book}
else
m[i].tag:= magazine;
writeln(‘Автор ‘); readln(m[i].author);
writeln(‘Название ‘); readln(m[i].title);
writeln(‘Год издания ‘); readln(m[i].year);
{Проверка условия принадлежности объекта к книге}
if m[i].tag = book then
{Ввод данных в поля, специфические для книги}
begin
writeln(‘Город ‘); readln(m[i].city);
writeln(‘Издательство’); readln(m[i].publisher);
end
else
{Ввод данных в поля, специфические для журнала}
begin
write (‘Название журнала’); readln(m[i].magname);
writeln(‘Номер ‘); readln(m[i].vol);
writeln(‘Страница ‘); readln(m[i].page);
end;
writeln (‘Пополняешь архив? – Да – [Y]’);
ch:= readkey;
until upcase(ch) <> ‘Y’
else
begin
write (‘Вы передумали? До свидания!’);
goto 10
end;
k:= i;
{Блок подборки списка трудов указанного автора}
repeat
writeln(‘Укажите фамилию автора’); readln(ss);
writeln(‘Список трудов ‘, ss: 15);
for i:= 1 to k do
begin
if ss = m[i].author then
begin
writeln(m[i].title);
writeln(‘Год издания ‘,m[i].year);
if m[i].tag = book then
begin
writeln(‘Город ‘,m[i].city);
writeln(‘Издательство ‘,m[i].publisher);
writeln
end
else
begin
writeln(‘Название журнала ‘,m[i].magname);
writeln(‘Номер ‘,m[i].vol);
writeln(‘Страница ‘,m[i].page);
writeln
end
end
end;
writeln(‘Нужно еще выдавать авторский список? – Да –[Y]’);
ch:= readkey;
until upcase(ch) <> ‘Y’;
repeat until keypressed;
10:end.
Множественные типы
Множество – это некоторый ограниченный неупорядоченный набор различных элементов одинакового типа. Можно говорить, например, о множестве фигур на плоскости (прямоугольник, круг, ромб, квадрат), о множестве радиодеталей, транспортных средств, станков и т. д.
В Турбо Паскале существует множественный тип для ввода множеств и организации операций над множествами. Множественный тип представляет собой некоторую совокупность элементов, которая является подмножеством допустимых значений определенного порядкового типа, именуемого базовым. Множественный тип описывает наборы однотипных логически связанных друг с другом объектов. Характер связей между объектами лишь подразумевается программистом и никак не контролируется Турбо Паскалем. Количество элементов, входящих в множество, может меняться в пределах от 0 до 256. Множество, не содержащее элементов, называется пустым. Именно непостоянством количества своих элементов множества отличаются от массивов и записей.
Множества должны быть объявлены либо в разделе переменных var, либо с использованием раздела типов type. Описание типа множества имеет вид:
<имя типа> = set of <базовый тип>
Здесь <имя типа> – правильный идентификатор; set, of – зарезервированные слова (множество, из); <базовый тип> – базовый тип элементов множества, в качестве которого может использоваться любой порядковый тип, кроме word, integer, longint.
Синтаксическая диаграмма:
Рис. 20
Пример:
var
year: set of 198..200;
c: set of char;
Year – множество, которое может состоять из элементов диапазона 198... 200, с – множество всех символов.
Или:
type
digitchar = set of ‘0’..’9';
digit = set of 0..9;
color = (red, blue, green, white, black, orange);
brush = set of color;
var
s1, s2, s3: digitchar;
s4, s5, s6: digit;
a: brush;
Тип digitchar описывает набор символов ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’. Тип digit состоит из чисел 1, 2, 3, 4, 5, 6, 7, 8, 9. Тип brush состоит из набора шести идентификаторов. Переменные s1, s2, s3 имеют тип digitchar, то есть могут включать только перечисленные элементы от ‘0’ до ‘9’. Множества s4, s5, s6 могут состоять из чисел от 1 до 9 в том виде, в котором они перечислены в типе digit. Переменная а может содержать от одного до шести значений перечисляемого типа color. Напомним, что элементы множества не упорядочены.
Конструкторы множеств
Множественное значение можно задать с помощью конструктора множества, в котором содержатся описания элементов множества, отделенные друг от друга запятыми и заключенные в квадратные скобки.
Описанием элемента могут быть константы или выражения базового типа, а также – тип–диапазон того же базового типа вида min..max, где значения выражений min и max представляют собой нижнюю и верхнюю границы группы элементов. Если нижняя граница больше верхней границы (т. е. min > max), то никакой элемент не описывается, то есть описывается пустое множество.
Все выражения должны относиться к одному порядковому типу, представляющему собой базовый тип для множественного типа данного конструктора. Конструктор множества [ ] обозначает пустое множество для любого множественного типа (рис. 21).
Рис. 21
Примеры конструкторов множеств:
[13]
[ i + j, i – j ]
[ ‘0’..’9']
Следующие записи не верны:
[true, 2] – разные базовые типы;
[200.. 300] – ord(300) > 255;
[4.0, 5.0] – базовый тип не является порядковым.
Операции над множествами
Если Х – переменная–множество, а Е – множественное выражение, то присваивание:
Х:= Е
допустимо только в том случае, если все элементы Е относятся к базовому типу Х.
К любым объектам со структурой множества применимы операции: объединение, пересечение, разность. Если предположить, что А и В – выражения одного типа, то:
А + В – множество из элементов А и В (объединение);
А * В – множество общих для А и В элементов (пересечение);
А – В – множество элементов А, не входящих в В (разность).
К множественным операндам применимы пять операций отношения. Предположим, А и В – множественные выражения одного типа, а е – порядковое выражение базового типа.
е in A – вхождение в множество; результат true, если е элемент А, иначе – false;
А = В – равенство множеств;
А <>В – неравенство множеств;
А <= В – включение; результат true, если А подмножество В;
А >= В – включение; результат true, если В подмножество А.
Два множества считаются эквивалентными тогда и только тогда, когда все их элементы одинаковы, причем порядок следования элементов в множестве безразличен. Если все элементы одного множества входят также и в другое, говорят о включении первого множества во второе. Пустое множество включается в любое другое.
Пусть
s1:= [‘1’, ‘2’, ‘3’];
s2:= [‘3’, ‘2’, ‘1’];
s3:= [‘2’, ‘3’];
s4:= [0..3, 6];
s5:= [4, 5];
s6:= [3..9];
Множества s1 и s2 эквивалентны, множество s3 включено в s2, но не эквивалентно ему.
s4 + s5 содержит [0, 1, 2, 3, 4, 5, 6].
s5 + s6 содержит [3, 4, 5, 6, 7, 8, 9].
s6 – s5 содержит [3, 6, 7, 8, 9].
s4 – s5 содержит [0, 1, 2, 3, 6].
3 in s6 возвращает true.
2 * 2 in s1 возвращает false.
Действия с множествами относительно быстрые, и их можно использовать для исключения более сложных проверок. Например, вместо проверки
if (ch = ‘a’) or (ch = ‘e’) or (ch = ‘i’) or (ch = ‘o’) or (ch = ‘u’) then...
можно написать более простую
if ch in [‘a’, ‘e’, ‘i’, ‘o’, ‘u’] then....
Значение типизированной константы–множества задается в виде правильного конструктора множества, например:
type
days = set of 1..31;
digc = set of ‘0’.. ‘9’;
error = set of 1..24;
const
workDays: days = [1..5, 8..12, 15..19, 22..26, 29,30];
evendigits: digc = [‘0’, ‘2’, ‘4’, ‘6’, ‘8’];
err: error = [ ];
Механизм работы с множествами Турбо Паскаля соответствует базовым математическим действиям с конечными множествами. Множества хорошо работают там, где нужно проводить анализ однотипных выборок значений или накапливать произвольно поступающие значения. Значения типа “множество” компактно кодируются: один элемент расходует 1 бит. Множество из 256 элементов займет всего 32 байта. Недостатком является невозможность вывода множеств. Не существует механизма изъятия элемента из множества, но и в математике такое действие не определено.