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

2.4. Cтруктурированные типы данных

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

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

tyре

s=аrrау [i] of а

Здесь ARRAY и ОF – зарезервированные слова, имеющие смысл массив и из; S – имя регулярного типа; I – тип индексов (указывается в квадратных скобках); А – тип элементов массива (базовый тип). Элементы массива могут представлять собой значения любого типа. Для индексирования массива в Тurbо Раsсаl используется тип данных. Это может быть любой из целочисленных типов (за исключением Longint), и Вооlеаn, а также перечислимый и интервальный типы (за исключением интервальных типов на основе Longint). Вещественные типы для индексирования массивов не годятся.

Вот примеры описания регулярных типов (анонимных):

var

a1rrау [byte] оf bооlеаn;

a2:аrrау [сhаr] оf bооlеаn;

a3:аrrау [Red,Yеllоw,Grееn] оf сhаr;

а4:аrrау [1..100] оf integеr;

Здесь А1 (первый пример) представляет собой массив из 256 (0..255) значений типа Вооlеаn. А2 – это массив также из 256 значений типа Вооlеаn, однако, в отличие от массива А1, он индексируется значениями типа Сhаr. Обращения к отдельным элементам массивов А1 и А2 выглядят так:

а1 [90]:=fаlsе;

а2 [z]:=truе;

Содержимое массивов А1 и А2 может быть следующим:

1-й элемент 2-й элемент 3-й элемент 4-й элемент ... 256-й элемент

TRUE FALSE TRUE FALSE … TRUE

(Элементы 1 – 256 массива А1 будут обозначаться числами с 0 по 255, а массива А2 – символами таблицы ASCII по порядку.)

Третий пример (А3) представляет собой массив из трех элементов типа Сhаr. Элементы этого массива обозначаются именами Red, Yеllоw и Grееn (для индексирования здесь использован перечислимый тип). Примеры обращений к элементам этого массива можно видеть в табл.6.

Содержимое массива А3 может выглядеть так:

А3[Red] А3[Yе11оw] А3[Grееn]

'А' '$' '8'

Таблица 6

Манипуляции над элементами массива

Пример обращения

Комментарий

A3[Red]:=chr(100)

Элементу Red массива A3 присваивается значение типа Char, соответствующее букве ‘d

A3[Red]:=’d’

Эквивалент предыдущего оператора

Write(a3[Yellow])

Значение элемента Yellow массива A3 выводится на экран

read(a3[Green])

Значение элемента Green массива A3 вводится с клавиатуры

Наконец четвертый пример (массив А4) – это массив из 100 элементов, принадлежащих типу integеr. Индексным типом здесь служит интервальный тип (диапазон). (Кстати, для индексирования массива этот тип используется очень часто, поскольку наиболее наглядно задает границы изменения индексов.)

Следует понимать, что индексы массива могут представляться не только в виде явно заданных значений, но и переменных, и выражений. Предположим, в некоторой программе объявлены переменные А5 и I:

Var

а5:аrrау[1..20] оf integеr;

i:integеr;

Затем элементам массива А5 и переменной I присвоены некоторые значения. После этого в программе могут фигурировать следующие обращения к элементам массива А5:

а5[i+1];

а5[2*i];

а5[i/2-5];

а5[22-i];

При этом необходимо, чтобы значения выражений в квадратных скобках (т.е. значения индекса) не выходили за пределы допустимых значений (в нашем случае 1..20).

Само собой разумеется, массивы можно объявлять не только в качестве анонимных типов, но и как типы с собственным именем – в разделе описания типов:

Tуре

LogScalе=аrrау [bytе] оf bооlеаn;

vаr

а1:LogScale;

Можно заметить, что массивы очень похожи на строки. Действительно, массив – это последовательность однотипных элементов, так же, как и строка, однако если символы строки представляют собой только значения типа Сhаr, то элементы массива могут принадлежать различным типам – как простым, так и структурированным.

Обращения к отдельным элементам строк и массивов выглядят одинаково – для этого достаточно указать имя строки или массива и индекс. Однако имеются и отличия: если текущей длиной строки можно манипулировать, то для массивов ничего подобного не предусмотрено. Длина строки также ограничена 255 символами, в то время как для массивов такого ограничения нет. Кроме того, можно ввести или вывести значение всей строковой переменной с помощью единственного оператора (например, Read(Х) или Write(Х), где Х – значение типа STRING), а для массивов это невозможно.

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

2.4.1.1. Многомерные массивы. В Тurbo Раsсаl элементы массива могут представлять собой значение не только простых, но и структурированных типов. Допускается, например, существование массивов, элементами которых являются также массивы. Описание подобного массива выглядит так:

а1=аrrау[1..5] оf аrrау [1..4] оf integer;

Этот массив можно представить в качестве двумерной матрицы с 20 элементами (4 на 5). Возможна сокращенная запись приведенного выше описания массива:

а1rrау [1..5, 1..4] оf integеr;

Эти описания эквивалентны. Обращение к одному из элементов данного массива выглядит так:

а1[3][4]

или

а1[3,4]

А вот так выглядит этот двумерный массив (или матрица) полностью:

а1[1,1] а1[1,2] а1[1,3] а1[1,4]

а1[2,1] а1[2,2] а1[2,3] а1[2,4]

а1[3,1] а1[3,2] а1[3,3] а1[3,4]

а1[4,1] а1[4,2] а1[4,3] а1[4,4]

а1[5,1] а1[5,2] а1[5,3] а1[5,4]

Возможны не только двумерные, но и многомерные массивы, причем число измерений не ограничивается. Однако при этом сохраняется ограничение в 64К (или 65520 байт) на размер переменных регулярного типа. Это связано с максимальным объемом памяти, выделяемой для данной переменной.

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

Действия над элементами массива

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

Таблица 7

Действия над элементами массива

Пример действия

Комментарий

Write(6?a1[5])

На экране отображается число 6, а затем значение 5-го элемента массива A1

a4[5]:=а4[3]4[2]

Значения 3 и 2-го элементов массива А4 складываются, и полученная сумма в качестве значения присваивается эле­менту 5 того же массива

abc:=аbс+а4[88]

Значение 88-го элемента массива А4 суммируется со значением переменной abс, и полученная сумма присваивается этой же переменной

abс:=а4[55]4[56]

Значения 55 и 56-го элементов массива А4 cкладываются, и полученная сумма присваивается переменной a

a4[33]:=а4[33]+20

Значение 33-го элемента массива А4 увеличивается на 20

Действия над массивами

Что касается набора операций над массивами в целом, то скопировать все элементы из одного массива в другой можно единственным оператором присваивания. Например, если Х и Yмассивы, принадлежащие одному типу, то правомерен оператор

X := Y;

Этот оператор копирует значения всех элементов массива X в массив Y.

Однако нельзя использовать с массивами операции сравнения. Неверно, например, было бы к массивам Х и Y применить оператор

while х=у do...

Тем не менее, если такая необходимость существует, можно организовать поэлементное сравнение массивов.

Также абсолютно неприменимы к массивам арифметические и логические операции.

Кроме того, напомним, что к массивам (в отличие от строк) нельзя применять стандартные процедуры Read и Write. Однако можно организовать считывание или вывод на экран каждого элемента массива в отдельности.

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

Запись (или комбинированный тип данных) очень хорошо подходит для объединения разнородной информации о каком-либо объекте. Это могут быть технические характеристики некоторого устройства, экономические данные предприятия или сведения о человеке. Элементы, образующие запись, называются полями.

Описание записи имеет вид:

Tyре

а=rесоrd

х,у:m;

. . .

z:n

еnd;

Здесь А – имя объявляемого комбинированного типа.

RECORD и ЕND – зарезервированные слова, имеющие смысл запись и конец;

X, Y, Z – имена полей;

М и N – типы, которым принадлежат те или иные поля.

После зарезервированного слова RECORD точка с запятой не ставится. Описания отдельных полей (или групп полей, принадлежащих одному типу) завершаются точкой с запятой (кроме последнего поля перед служебным словом END). Описания полей записи похожи на описания обычных переменных. Поля могут принадлежать любым типам – как простым, так и структурированным, как стандартным, так и опреде­лимым пользователем. Например, допустимы записи, поля которых представляют собой также записи или, например, массивы.

Количество полей в записи фиксировано и определяется описанием записи. Имена полей в пределах записи не должны повторяться. Если в программе объявлены несколько комбинированных типов, имена полей, принадлежащих разным типам, могут совпадать; конфликта имен при этом не будет, поскольку обращение к отдельным полям производится с оказанием имени записи (об этом далее). Как и с другими идентификато­рами в программах на Тurbо Раsсаl, следует стремиться, чтобы имена по­лей соответствовали смыслу информации в том или ином поле. А инфор­мация, в свою очередь, должна соответствовать типу своего поля.

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

Tyре

Еmрlоуее=rесоrd

ID:word; {Идентификатор (личный номер)}

FirstName, SecondName, SurName : string[20];

{Имя, фамилия, отчество}

Standing: bytе; {Стаж}

Salary:rеаl {Зарплата}

end;

var

Аssistantmрlоуее;

Описанный выше комбинированный тип Еmрlоуее включает шесть полей, три из них – FirstName (Имя), SecondName (Отчество) и SurName (Фамилия) – представляют собой строки по 20 символов каждая. Для остальных полей – ID (Идентификатор), Standing (Стаж) и Salary (Зарплата) – выбраны типы, также подходящие для соответствующей информации.

Содержимое одной из записей, принадлежащих типу Еmрlоуее, выглядит так:

ID SurName FirstName SecondName Standing Salary

14873 Петров Иван Кузьмич 15 1000

Доступ к отдельным полям проводится с помощью составных имен, включающих имя записи и имя поля, разделенные точкой. Вот как можно обратиться к различным полям переменной Assistant из примера выше:

Assistant.ID:=19876;

write(Аssistant.FirstName);

read(Аssistant.SecondName);

Аssistant.Surname:='Петров';

а:=Аssistant.Standing;

Аssistant.Salary/100;

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

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

write(Аssistant:FirstName[1]);

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

writе(Петров ');

write(Аssistant.FirstName[1],'. ');

write(Аssistant.SecondName[1],'.');

В результате выполнения этой последовательности операторов на экране отобразится:

Петров И. К.

2.4.2.1. Оператор WITН. Как указывалось выше, если в операторах используются составные имена (имя записи плюс имя поля), такие операторы получаются чересчур громоздкими. Ситуация еще больше ухудшится, если несколько подобных операторов окажутся сосредоточены в одном месте программы. В этом случае неплохо бы каким-то образом вынести имя записи, над полями которого проводятся различные действия, в некоторый заголовок. Создать такой заголовок позволяет оператор WIТН (его еще называют оператором над записями).

Оператор WITН имеет вид:

With p do s;

Использованные здесь зарезервированные слова WIТН и DO имеют смысл с и выполнить соответственно. Идентификатор Р – имя переменной комбинированного типа. Идентификатор S – оператор (часто составной).

Обращения к шести полям записи Аssistant, представленные выше, можно преобразовать так (предполагается, что эти операторы следуют в программе один за другим):

with Assistant dо

begin

ID:=19876;

write(FirstName);

read(SecondName);

SurName:='Петров';

а:=Standing;

Salary/100 end;

Здесь имя переменной Аssistant употребляется всего один раз –после оператора WIТН. Затем все действия над полями (в составном операторе) выполняются только с указанием имен полей.

Иными словами, оператор WIТН особенно полезен, когда обрабатывается несколько полей одной переменной комбинированного типа. Однако злоупотреблять использованием оператора WIТН также не следует, поскольку в результате часто неочевидно, к чему относится тот или иной идентификатор. Например, идентификатор Аssistant.ID более информативен, чем просто ID. Это особенно верно, если составной оператор, выполнение которого инициирует оператор WITН, включает множество операторов.

2.4.2.2. Иерархические (вложенные) записи. Возможно существование записей, отдельные поля которых представляют собой также записи. Для того чтобы понять, как это реализуется, попробуем добавить в описание комбинированного типа Еmрlоуее пару полей-записей. Пусть это будут поля, содержащие информацию об адресе служащего (почтовый код, город, улица, дом, квартира) и его дате рождения (число, месяц, год). В принципе соответствующие поля (пять полей и три поля с данными соответственно об адресе и дате рождения) можно добавить непосредственно в описание комбинированного типа Еmрlоуее. А что если эту информацию впоследствии потребуется использовать в качестве полей еще в каких-нибудь комбинированных типах? Поэтому лучше для адреса и даты рождения создать самостоятельные комбинированные типы в разделе описаний программы, которые затем добавить как поля в тип Еmрlоуее. Описание этих типов может выглядеть так:

tуре

Address = rесоrd

РоstСоdе:1.. 999999; {Почтовый код}

City,Street:string[20]; {Город, улица}

House:word; {Дом}

Араrtment:word {Квартира}

end; {address}

Date = rесоrd

Dау:1..31; {Число}

Моnth:1..12; {Месяц}

Yеаr;1900..2000 {Год}

end; {date}

NеwЕmрlоуее = record

ID:word; {Идентификатор (личный номер)}

FirstName, SecondName, SurName : string[20];

{Имя, фамилия, отчество}

Standing: bytе; {Стаж}

Salary:rеаl {Зарплата}

BirthDate:date; {Дата рождения}

Habitation:address {Место жительства}

end;

vаr

NewAssistant: NеwЕmрlоуее;

Описанный выше комбинированный тип wЕmрlоуее включает восемь полей, два из которых – BirthDate (Дата рождения) и Habitation (Место жительства) – представляют собой записи, типы которых (Date и Address соответственно) объявлены перед описанием типа NеwЕmрlоуее. Структура комбинированного типа NеwЕmрlоуее представлена на рис.1.

Рис.1. Тип NewEmployee

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

NеwЕmрlоуее.Аddress.РоstСоdе

или

NеwЕmрlоуее.Date.Dау

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

2.4.2.3. Записи с вариантами. Иногда возникает необходимость, чтобы в зависимости от значения определенного поля записи структура этой записи частично изменялась. Например, одно из полей комбинированного типа Еmрlоуее, которое представляет собой вложенную запись, содержит информацию о месте жительства служащего. Однако городской адрес обычно выглядит не так, как загородный. Что если кто-либо из членов коллектива проживает за пределами города? Для решения этой проблемы Turbo Раsсаl позволяет в структуре комбинированного типа Аddress создать вариантную часть, в которой набор полей мог бы изменяться в соответствии с видом адреса. Итак, в структуре адреса необходима некоторая фиксированная часть – группа полей, присутствующих постоянно, независимо от характера адреса, и вариантная часть с изменяющимся набором полей. Фиксированная часть в нашем случае могла бы включать поля PostCode (почтовый код), Street (улица) и Ноusе (дом) – элементы, присутствующие в любом адресе. Вариантная часть записи для городского адреса могла бы включать поля City и Apartment (город и квартира), а для загородного адреса – поля Аrеа (область), District (район) и Community (населенный пункт). Для управления вариантной частью адреса введем в структуру комбинированного типа Address поле City_оr_Country (город/загород), для которого допустимы только значения City и Country. Вот как может выглядеть описание такого преобразованного комбинированного типа (присвоим ему имя NewAddress):

tуре

NewAddress = rесоrd

РоstСоdе:1.. 999999; {Почтовый код}

Street:string[20]; {Город, улица}

House:word; {Дом}

саsе City_оr_Country : City,Country оf

City:(City:string[20]; {Город}

Appartment:word); {Квартира}

Соuntrу :(Аrеа,District, {Область, район}

Community:string[20]); {Населенный пункт}

end; {NewAdress}

Фиксированная часть записи всегда предшествует вариантной части. Описание фиксированной части ничем не отличается от описания обычной записи (без вариантов). Вариантная часть начинается со строки,

саsе City_оr_Countrу : City,Countrу оf

которая объявляет специальное поле Cityr_Countrу, способное принимать только два значения (Citу и Сountrу – т.е. принадлежит перечислимому типу). Это поле, которое называется полем признака (или селектором выбора – по аналогии с оператором САSЕ), определяет вариантную часть записи.

Строка, начинающая вариантную часть, включает зарезервированные слова CASE и OF, имеющие смысл вариант и из соответственно Дальше следует перечень меток, представляющих собой все допустимы значения поля признака (в нашем случае две метки – City и Country). После каждой метки (за двоеточием, в круглых скобках) представлен перечень полей для данного варианта, причем для каждого поля указывается тип. Завершение данной конструкции CASE-OF – перечень меток ключевым словом ЕND не предусмотрено. Правда, в конце описание комбинированного типа NewAddress слово ЕND все же имеется, однако оно здесь служит парой зарезервированному слову RECORD, а не CASE. Иначе говоря, то, что мы здесь видим, вовсе не представляет собой оператор CASE, а является всего лишь конструкцией, внешне похожей на оператор САSЕ, описывающей вариантную часть записи.

Структура комбинированного типа NewAddress представлена на рис.2.

При использовании записей с вариантами следует иметь в виду следующие особенности.

• В записи может быть только одна вариантная часть, которая должна быть описана непременно после фиксированной части.

• Поле признака вариантной части не принадлежит ни к фиксированной, ни к вариантной частям и выносится в заголовок вариантной части.

• Идентификаторы всех полей записи – как в фиксированной, так и в вариантной части – должны быть уникальными (не совпадать).

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

• Объем памяти, выделяемый для записи, определяется суммой длин полей. Объем памяти для записи с вариантами определяется по самому объемному варианту.

Рис.2. Тип NewAddress

В созданном нами комбинированном типе wЕmрlоуее мирно сосуществуют вложенные записи (Date и NewAddress) и записи с вариантами. Собственно, вложенная запись NewAddress одновременно является записью с вариантами. Этот пример наглядно иллюстрирует, насколько сложные структуры данных можно создавать с использованием записей.

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

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

Действия над полями записи

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

Действия над записями

Здесь, вероятно, уместно повторно упомянуть оператор WITH, который так и называется: оператор над записями.

Кроме того, как и с массивами, значения переменных, принадлежащих комбинированному типу, можно присваивать другим переменным того же типа. Например, если переменные Х и Y представляют собой записи, принадлежащие одному комбинированному типу, то допустим оператор присваивания

х:=у;

В результате значения всех полей записи Х окажутся присвоены полям записи Y.

Однако нельзя использовать с записями операции сравнения. Неверно, например, было бы к записям Х и Y применить оператор

while х=у do...

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

Совершенно не применимы к записям (в целом) арифметические и логические операции.

Кроме того, следует особо подчеркнуть, что к записям (подобно массивам, но в отличие от строк) нельзя применять стандартные процедуры Read и Write. Однако можно организовать считывание или вывод на экран каждого поля записи в отдельности.

2.4.3. Множества. Подобно массиву, множество – это совокупность связанных дан­ных, состоящая из элементов одного типа, который называется базо­вым. Однако, в отличие от массива (в котором число элементов фикси­ровано), множество может включать произвольное число элементов.

Описание множественного типа:

Tуре

s=set of а

Здесь SЕТ и OF – зарезервированные слова, имеющие смысл мно­жество и из; S – имя объявляемого множественного типа; А – базовый тип множества.

Вот примеры описаний множественных типов и переменных, принадлежащих этим типам:

tуре

а1=set of 1..3;

а2=set of а..е;

а3=set of char;

r

х:а1; у:а2; z3;

В качестве базового для множественного типа А1 задан интервальный тип (1..3). Переменная X, принадлежащая множественному типу А1, может принимать значения [], [I], [2], [1,2], [3], [1,3], [2,3], [1,2,3] (всего 8 (2³) значений). Иными словами, значениями множества могут быть все входящие в него подмножества – от нуля элементов (пустое множество) до всех возможных значений базового типа. Кстати, в Тurbo Pascal допускается использование (например, в операторах присваива­ния) явно заданных значений множественного типа, подобно, например, целочисленным или вещественным значениям. В этом случае элементы множества задаются через запятую и все значение заключается в квадратные скобки. Например:

х:=[1,3];

х:=[];

Во втором случае переменной Х присваивается так называемое пустое множество, т.е. множество, не содержащее ни одного элемента.

В качестве базового для множественного типа А2 задан диапазон а..е. Переменная Y, принадлежащая множественному типу А2, может принимать значения, соответствующие всем подмножествам, входя­щим в это множество (всего 32 (25) значения).

Наконец, в качестве базового для множественного типа А3 выбран стандартный тип Char. Число значений, которые может принимать переменная Z (объявленная как принадлежащая типу А3), равно 2256, причем значения эти представляют собой произвольные наборы символов из таблицы ASCII. Например, переменная Z может иметь значение

[‘3’, ‘f’, ‘(‘, ‘#’, ‘п’, ‘Л’]

Значение переменной Z можно представить и так:

[сhr(45),сhr(54),сhr(58),сhr(65),сhr(73),сhr(78),сhr(89)].

Помимо того, что множество может включать произвольное число элементов, имеет место еще одно отличие множеств от массивов. Если массив – это упорядоченная совокупность элементов, то в множестве порядок элементов никак не фиксируется. Так. [1,2,3,4,5] и [5,2,1,4,3] – это одно множество. Кроме того, все элементы в множестве должны быть различными. Иными словами, [1,2,3] и [1,1,2,3,3,3] – это также одно множество.

Представление множества возможно путем перечисления входящих в него элементов или заданием диапазона (если члены множества образуют неразрывную последовательность), т.е. множество [1,2,3,4,5] можно также представить как [1..5]. Допускается сочетание двух методов: [1..5,7,9].

Число элементов множества в Turbo Pascal не должно превышать 256 (причем, для целочисленных типов эти значения должны лежать в диапазоне (0,...,255). Вследствие этого ограничения в качестве базовых типов для множеств (из стандартных типов) могут служить только Byte, Char и Boolean. Не удастся использовать для множества в качестве базового, например, тип Integer, хотя подходят интервальные типы, образованные на основе integer.

2.4.3.1. Применимые действия. Набор допустимых для множеств операций включает проверку принадлежности элемента множеству; объединение, пересечение и вычитание множеств; сравнение множеств (проверку равенства и неравенств множеств, а также проверку принадлежности одного множества другому).

Проверка принадлежности элемента множеству

Для этой операции в Turbo Pascal имеется специальный оператор IN. Выражение с этим оператором возвращает значение типа Boolean. Вот пример использования оператора IN:

а in [1,3,5,7,9]

Это выражение проверяет, принадлежит ли значение переменной A данному множеству и возвращает значение TRUE либо FALSE. В случае, если бы оператора IN не существовало, нам пришлось бы воспользоваться гораздо более громоздким выражением для проверки данного условия:

(а=1) оr (а=3) оr (а=5) оr (а=7) оr (а=9)

В случае, если переменная А принадлежит множеству [1,3,5,7,9] оба выражения возвратят значение TRUE. Еще примеры:

3 in [1,3,5,7,9];

4 in [1,3,5,7,9];

Эти выражения возвратят значения TRUE и FALSE соответственно.

Объединение, пересечение и вычитание множеств

Эти операции, будучи применены к двум множествам, формируют третье. В Turbo Pascal эти операции обозначаются символами: + (объединение), * (пересечение), - (вычитание).

В результате объединения двух множеств А и В формируется третье, содержащее все элементы, встречающиеся в первых двух множествах (см. рис.3). Примеры можно видеть в табл.8.

Таблица 8

Примеры объединения двух множеств

Объединение множеств

Результат

[1,2,3,4,5]+[3,4,5,6,7]

[1,2,3,4,5,6,7]

[1,2]+[3,4]

[1,2,3,4]

[‘a’,’b’,’c]+[‘A’,’B’,’C]

[‘a’,’b’,’c’,’A’,’B’,’C]

[‘1’,’3’,’5’,’7’,’9’]+[‘1’,’3’,’5’,’A’,’B’]

[‘1’,’3’,’5’,’7’,’9’,’A’,’B’]

Часто возникает необходимость добавить в множество новый элемент (частный случай операции объединения). Эта задача может быть решена следующим образом:

[1,2,3,4,5]+[6]

В результате пересечения двух множеств А и В формируется третье множество, содержащее только элементы, одновременно принадлежащие первым двум множествам (см. рис.3). Примеры можно видеть в Табл.9.

Таблица 9

Примеры пересечения двух множеств

Пересечение множеств

Результат

[1,2,3,4,5]*[3,4,5,6,7]

[3,4,5]

[1,2]*[3,4]

[]

[‘a’,’b’,’c]*[‘A’,’B’,’C]

[]

[‘1’,’3’,’5’,’7’,’9’]*[‘1’,’3’,’5’,’A’,’B’]

[‘1’,’3’,’5’]

При вычитании множества В из множества А формируется третье множество, содержащее только элементы, принадлежащие множеству А и не принадлежащие множеству В (см. рис.3). Примеры можно видеть Табл.10.

Таблица 10

Примеры вычитания двух множеств

Вычитание множеств

Результат

[1,2,3,4,5]-[3,4,5,6,7]

[1,2]

[1,2]-[3,4]

[1,2]

[‘a’,’b’,’c]-[‘A’,’B’,’C]

[‘a’,’b’,’c]

[‘1’,’3’,’5’,’7’,’9’]-[‘1’,’3’,’5’,’A’,’B’]

[’7’,’9’]

Необходимо иметь в виду, если в случае объединения и пересечения множества А и В поменять местами, результат не изменится, но, однако это вовсе не так для вычитания множеств. Пример можно видеть в табл.11.

Таблица 11

Перестановка множеств при вычитании

Вычитание множеств

Результат

[‘1’,’3’,’5’,’7’,’9’]-[‘1’,’3’,’5’,’A’,’B’]

[’7’,’9’]

[‘1’,’3’,’5’,’A’,’B’]-[‘1’,’3’,’5’,’7’,’9’]

[‘A’,’B’]

Операции объединения, пересечения и вычитания множеств А и B можно представить графически, если изобразить эти множества в виде некоторых фигур (например, кругов), закрашенные части которых соответствуют множествам-результатам (рис.3).

Рис. 3. Объединение, пересечение и вычитание множеств

Сравнение множеств

К операциям сравнения множеств относятся проверка равенства неравенства множеств, а также проверка принадлежности одного множества другому. Символы, которыми в Turbo Pascal обозначаются эти операции, представлены в табл.12.

Таблица 12

Операции сравнения множеств

Обозначение

Действие

=

Проверка равенства множеств

<>

Проверка неравенства множеств

<=

Проверка вхождения множества слева в множество справа

> =

Проверка вхождения множества справа в множество слева

Результат сравнения представляет собой значение типа Boolean (TRUE или FALSE – в зависимости от успеха проверки). Два сравниваемых множества должны иметь один базовый тип.

Операции = и <> проверяют, совпадает ли набор элементов в сравниваемых множествах. Примеры этих операций можно видеть в табл.13 и 14.

Таблица 13

Проверка равенства множеств

Сравниваемые множества

Результат

[1,2,3]=[1,2,4]

FALSE

[1,2,3]=[1,2,3]

TRUE

[1,2,3]=[3,2,1]

TRUE

[1,2]=[1,1,2,2,3]

FALSE

Таблица 14

Проверка неравенства множеств

Сравниваемые множества

Результат

[1,2,3]<>[1,2,4]

TRUE

[1,2,3]<>[1,2,3]

FALSE

[1,2,3]<>[3,2,1]

FALSE

[1,2]<>[1,1,2,2,3]

TRUE

Операции <= и >= проверяют, входит ли одно множество в другое. Множество слева входит в множество справа, если все элементы левого множества встречаются среди элементов правого множества. Примеры проверок по вхождению одного множества в другое представлены втабл.15 и 16.

Таблица 15

Проверка вхождения левого множества в правое

Сравниваемые множества

Результат

[1,2,3]<=[1,2]

FALSE

[1,2,3]<=[1,2,3,4,5]

TRUE

[1,2,3]<=[4,3,2,1]

TRUE

[1,2,3]<=[1,3]

FALSE

Множество справа входит в множество слева, если все элементы правого множества встречаются среди элементов левого множества.

Таблица 16

Проверка вхождения правого множества в левое

Сравниваемые множества

Результат

[1,2,3]>=[1,2]

TRUE

[1,2,3]>=[1,2,3,4,5]

FALSE

[1,2,3]>=[4,3,2,1]

FALSE

[1,2,3]>=[1,3]

TRUE

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