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

Допустим, переменной schoolYear нужно присваивать значения, определяющие этап обучения некоего студента. В США для студентов колледжей принята следующая градация: freshman (первокурсник), sophomore (второкурсник), junior (младший) и senior (старший). Во многих компьютерных языках для присвоения таких значений необходимо или применить их нумерацию (т.е. условиться, что 1 – это freshman, 2 – это sophomore и т.д.), или объявить переменную как строковую и присваивать ей значения freshman, sophomore и т.д. Оба способа имеют существенные недостатки. Нумерация усложняет использование программы, так как человек плохо запоминает числа, а при использовании строковых переменных возникают проблемы неоднозначного написания и недопустимых значений. Object Pascal предоставляет программисту лучший способ решения этой задачи: перечислимые типы.

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

В Object Pascal типы данных определяются с помощью ключевого слова type. Для определения перечислимого типа используется следующий синтаксис:

type имя_типа = (значение1, ... , значениеN);

Входящие в синтаксис имя_типа, значение1 ... значениеN должны быть правильными идентификаторами. Например, оператор

Type

Year = (Freshman, Sophomore, Junior, Senior);

определяет перечислимый тип Year, значениями которого являются элементы Freshman, Sophomore, Junior и Senior.

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

Перечислимый тип можно объявить и без ключевого слова type:

Var

year1, year2:(Freshman, Sophomore, Junior, Senior);

При попытке объявить эти переменные отдельно

var yearl: (Freshman, Sophomore, Junior, Senior);

var year2: (Freshman, Sophomore, Junior, Senior);

компилятор вернет сообщение об ошибке вследствие конфликта имен.

Наиболее предпочтительным способом объявления является создание (т.е. определение) пользовательского типа.

Type

Year = (Freshman, Sophomore, Junior, Senior);

Var

year1, year2: Year;

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

Понятия множества в Object Pascal и в математике довольно похожи. Множество состоит из группы значений одного и того же порядкового типа данных. Значения в множестве не упорядочены, и ни одно значение не может входить в множество более одного раза.

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

В Object Pascal множество определяется с помощью ключевых слов set of.

Type

имя_множества = set of имя_базового_типа;

Имя_множества должно быть правильным идентификатором Object Pascal. Диапазон допустимых значений элементов множества совпадает со всеми допустимыми значениями базового типа, который должен быть порядковым типом данных. Базовый тип может быть также пользовательским типом данных, например перечислимым. Множество может содержать любые значения базового типа. Оно может быть также пустым множеством, обозначаемым в Object Pascal как [ ]. Кроме того, в Object Pascal на размер множеств наложен следующее ограничение: оно не может содержать более 256 значений. Поэтому порядковые значения базового типа должны находиться в диапазоне от 0 до 255.

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

Например, оператор

Type

LowercaseSet = set of 'a'..'z';

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

Type

LowercaseLetters = 'а' .. 'z' ;

LowercaseSet = set of LowercaseLetters;

Эти два объявления типа множества LowercaseSet эквивалентны.

Множество чисел можно объявить так:

Type

numberSet = 0..255;

numbers = set of numberSet;

Определив, таким образом, тип, можно объявлять переменные, являющиеся множествами этого типа, а затем присваивать этим переменным значения множеств. В Object Pascal значение множества записывается как заключенный в квадратные скобки список его элементов, разделенных запятыми.

[значение1, значение2, ... , значениеN]

Значения элементов множества, естественно, должны входить в определенный поддиапазон базового типа. Не путайте значения множества и значения элементов множества! Значение множества – это некоторый набор элементов. В следующем фрагменте кода объявляется и создается множество, содержащее буквы а, b, с, m, n, х, у и z (тип LowercaseSet считается предварительно определенным):

Var

myLetters: LowercaseSet;

Begin

...

myLetters := ['a'..'c1, 'm', 'n1, 'x'..'z'];

...

end;

Подобно тому как числовые переменные ассоциированы с арифметическими операциями, множества ассоциированы с набором операций над множествами, перечисленными в табл. 1. Например, оператор in проверяет вхождение элемента в множество: значение выражения 'a' in myLetters равно True, если элемент 'а' входит в множество myLetters, в противном случае оно равно False.

Таблица 1

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

Оператор Операция Типы операндов Тип результата Пример
+ Объединение Множество Множество set1+set2
- Разность Множество Множество set1-set2
* Пересечение Множество Множество set1*set2
<= Подмножество Множество Boolean subSet<=set1
>= Надмножество Множество Boolean superSet>=set1
= Равенство Множество Boolean set1 =set2
<> Неравенство Множество Boolean set1<>set2
in Вхождение Множество, порядковый тип Boolean value in set

Операторы +, - и * подчиняются следующим правилам:

· Порядковое значение X входит в множество set1+set2, если и только если X входит в set1 или в set2 (или в оба эти множества). Значение X входит в set1-set2, если и только если X входит в set1, но не входит в set2. Значение X входит в set1 *set2, если и только если X входит как в set1, так и в set2.

· Результаты операций +, - и * принадлежат множеству А..В, где А – элемент с наименьшим порядковым значением данного типа, а В – элемент с наибольшим порядковым значением.

Операторы <=, >=, = и <> подчиняются следующим правилам:

· Значение subSet<=set1 равно True, если каждый элемент множества subSet является элементом множества set1. Значение superSet>=set1 эквивалентно значению выражения set1<=superSet. Значение set1=set2 равно True, если множества set1 и set2 содержат одни и те же элементы, в противном случае истинно значение выражения set1<>set2.

· Значение X in set1 равно True, если элемент X входит в множество set1.

Выполните следующий пример.

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

Решение.

Будем использовать тип-диапазон для контроля вводимых оценок.

Перечислимые типы и множества - student2.ru

Массивы

Массив представляет собой последовательность индексированных элементов одного и того же типа. Каждый элемент массива имеет уникальный индекс.

Массив с одним индексом называется одномерным. Математическим эквивалентом одномерного массива является вектор. Двухмерный массив имеет два индекса, его математический эквивалент – матрица. Существуют также многомерные массивы (n-мерные). Количество индексов многомерного массива больше двух. В большинстве языков программирования массивы записываются аналогично тому, как это принято в математике. Например, i-и элемент вектора х принято записывать как хi, а i-й элемент массива х – как х[i]. Редактор кода Delphi использует стандартный набор символов ASCII, в который не входят нижние индексы, поэтому индекс вектора заменен выражением в квадратных скобках.

Статические массивы

В Object Pascal массивы объявляются с помощью ключевых слов array of. Синтаксис объявления N-мерного статического массива имеет вид

Var

имя массива: array[тип_индекса1, ... , тип_индексаN] of базовый тип;

Обратите внимание: в этом синтаксисе квадратные скобки являются символами объявления массива, а не символами, обозначающими необязательную фразу синтаксиса.

Имя массива может быть любым правильным идентификатором. Базовый тип – это тип, назначаемый каждому элементу массива. Каждый индекс должен иметь порядковый тип. Общее количество элементов массива равно произведению количества элементов по каждому индексу. В качестве типа индекса чаще всего используется целый поддиапазон, однако типом индекса может быть также, например, перечислимый тип.

Для одномерного массива в объявлении используется только один тип индекса, поэтому синтаксис объявления одномерного массива имеет вид

Var

имя_одномерного_массива: array[тип_индекса] of базовый_тип;

Например, оператор

Var

sampleArray: array[1..10] of Integer;

объявляет массив sampleArray, содержащий 10 чисел типа Integer, причем значение индексов изменяется от 1 до 10.

В следующем фрагменте кода объявляются двухмерный и многомерный массивы:

Type

Year = (Freshman, Sophomore, Junior, Senior);

Var

smp2DArray: array[1..10, 1..5] of Integer;

smp4DArray: array[Year, 1..20, 'A'..'F', 1..3] of Char;

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

Type

Year = (Freshman, Sophomore, Junior, Senior);

Var

smp2DArray: array[1..10] of array[1..5] of Integer;

smp4DArray: array[Year] of array[1..20] of

array['A'..'F'] of array[1..3] of Char;

В обоих случаях массив smp2DArray содержит 10x5=50 элементов типа Integer, a smp4DArray – 4x20x6x3=1440 элементов типа Char. Выбор способа объявления массива определяется стилем программирования. Однако помните: ваш стиль программирования должен быть неизменным на протяжении всего периода разработки программы.

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

имя_массива[значение_индекса1, ..., значение_индексаN]

или

имя_массива[значение_индекса1] ... [значение_индексаN]

Например, следующий оператор присваивает значение 10 пятому элементу массива sampleArray:

sampleArray[5] := 10;

Как и в случае двух разных способов объявления массивов, операторы

smp2DArray[5, 2] := 7;

smp4DArray[Junior, 10, 'А', 1] := 'X';

эквивалентны операторам

smp2DArray[5][2] := 7;

smp4DArray[Junior][10]['А'][1] := 'X';

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

Var

intArrayl: array[1..10] of Integer;

intArray2: array[1..10] of Integer;

count: Integer;

Begin

for count := 1 to 10 do begin

intArrayl [count] := count;

end;

intArray2 := intArrayl;

end;

Для определения эквивалентности типов переменных компилятор Object Pascal использует имена типов. Поэтому типы массивов intArray1 и intArray2 несовместимы. Эти массивы имеют одинаковое физическое строение, но их типы разные. Поэтому компилятор сгенерирует ошибку несовместимости типов в операторе intArray2 := intArray1. Чтобы исправить ошибку, нужно записать этот фрагмент кода следующим образом:

Type

Tenlnts = array[1..10] of Integer;

Var

intArray1: Tenlnts;

intArray2: Tenlnts;

count: Integer;

Begin

for count := 1 to 10 do begin

intArrayl[count] := count;

end;

intArray2 := intArray1;

end;

Динамические массивы

Для объявления динамического массива используется оператор array of без задания индексов. Синтаксис объявления одномерного динамического массива имеет вид

Var

имя_динамического_массива: array of базовый_тип;

Задание размера массива и выделение для него памяти выполняется с помощью процедуры SetLength().

SetLength(имя_динамического_массива, длина);

В этом синтаксисе выражение длина должно быть целого типа. После выполнения такого оператора значение индекса динамического массива может изменяться от 0 до длина-1. Процедуру SetLength() в процессе выполнения программы можно вызывать произвольное количество раз. Каждый вызов приводит к изменению длины массива, причем содержимое массива сохраняется. Если при вызове SetLength() длина массива увеличивается, то добавленные элементы заполняются произвольными значениями, так называемым мусором. Если длина массива уменьшается, то содержимое отброшенных элементов теряется.

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

1.Путем установки нулевой длины динамического массива: SetLength(имя_динамического_массива, 0);

2.Присвоением массиву (не его элементам, а индексной переменной, носящей имя массива) значения nil. В Object Pascal зарезервированное слово nil используется в качестве значения индексных (указательных) переменных. Если указательная переменная имеет значение nil, то она не указывает ни на какой объект. Таким образом, удалить динамический массив из памяти можно, воспользовавшись оператором – имя_динамического_массива := nil;

3.С помощью встроенной процедуры Finalize(): Finalize(имя_динамического_массива);

Для работы с динамическими массивами Object Pascal предоставляет еще несколько полезных функций. Функция Сору() возвращает заданную часть динамического массива.

Сору (имя_динамического_массива, начальное_значение_индекса, количество_копируемых_элементов);

Функции High() и Low() возвращают наибольшее и наименьшее значения индексов динамического массива, т.е. High() возвращает значение длина-1, а Low() – значение 0. Если динамический массив имеет нулевую длину, то функция High() возвращает -1. Синтаксис этих функций имеет вид

High(имя_динамического_массива);

Low(имя_динамического_массива);

Мы рассмотрели создание и использование одномерного динамического массива. Но как создать многомерный динамический массив? Как вы помните, многомерный массив – это не что иное, как массив массивов. Поэтому синтаксис объявления массива с несколькими индексами имеет вид

Var

имя_многомерного_динамического_массива :

array of array of ... array of базовый_тип;

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

Var

smp2DdynArray: array of array of Integer;

count: Integer;

Begin

SetLength(smp2DdynArray, 3);

for count = 1 to n do begin

SetLength(smp2DdynArray[count - 1], count * 2);

end;

{В этом месте массив smp2DdynArray можно использовать}

end;


  Второй индекс
Первый индекс
           
           
           

Рис. 1. Пример треугольного динамического массива

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

Var

array1, array2: array of Integer;

check: Boolean;

Begin

SetLength(array1, 1);

SetLength(array2, 1);

array1[0] := 5;

array2 [0] := 5;

check := (array1 = array2);

...

end ;

Какое значение принимает переменная check? Если бы это был статический массив, то значение check было бы равно True, потому что массивы содержат одну и ту же информацию. Однако для динамических массивов (как в данном случае) значение check равно False, так как указатели array1 и аrrау2 ссылаются на разные области памяти. Значение указателя равно адресу первого по счету элемента массива, а поскольку эти массивы хранятся в разных местах, то, естественно, указатели имеют разное значение. Для сравнения динамических массивов необходимо сравнивать в цикле каждую пару элементов отдельно. Похожая проблема возникает и в следующем фрагменте кода. Можете ли вы обнаружить ее?

Var

array1, array2: array of Integer;

Begin

SetLength(array1, 1);

SetLength(array2, 1);

array1[0] := 5;

array2 := array1;

array2[0] := 10;

end;

Какие значения принимают элементы array1[0] и array2[0]? Обратите внимание: значение array1 присвоено array2. Другими словами, array2 теперь ссылается на туже область памяти, что и array1. Поэтому оба элемента array1[0] и array2[0] ссылаются на одну и ту же область памяти, содержащую значение 10 (присвоенное этой области последним оператором).

Задачи

1. Объявить массив из 10 целых чисел. Инициализировать массив значениями, введенными с клавиатуры. Найти в массиве первое отрицательное число и вывести его на экран. Отсортировать элементы массива любым из приведенных ниже способов. Вывести результат на экран. В программе должны быть использованы циклы всех 3-х типов.

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

Контрольные вопросы

1. Чем отличаются статические структуры данных от динамических?

2. Что такое перечислимый тип? Приведите пример перечислимого типа.

3. Что такое множество в Object Pascal? Приведите пример множества.

4. Напишите операторы объявления одномерного, двухмерного, трехмерного и семимерного массивов.

5. Что является математическим эквивалентом одномерного массива? Двухмерного массива?

Задачи для самостоятельного выполнения

Наши рекомендации