Смекни!
smekni.com

Алгоритмический язык Паскаль (стр. 11 из 31)

НАПРИМЕР: MAP1:= [FR]; MAP1:= MAP1 + [USA]; MAP2:= MAP1;

MAP1:= MAP1 * (MAP2 + [IT]).

ПРИМЕР 2. РЕШЕТО ЭРАТОСФЕНА. Найти простые числа, не превосходящие заданного.

Алгоритм базируется на вычеркивании чисел, кратных выбранному:

program ERATOS;

const MAXPRIM = 15;

var PRIMES: set of 2..MAXPRIM;

COUNT, MULTIPLE: integer;

begin

¦ writeln('простыечисла, меньше ', MAXPRIM);

¦ PRIMES:= [2..MAXPRIM];

¦ for COUNT:= 2 to MAXPRIM do

¦ if COUNT in PRIMES then

¦ begin

¦ ¦ writeln(COUNT);

¦ ¦ for MULTIPLE:=1 to (MAXPRIM div COUNT) do

¦ ¦ PRIMES:= PRIMES-[COUNT*MULTIPLE]

¦ end;

end.

ПОЯСНЕНИЕ. Начинаем с набора множества, состоящего из всех целых чисел в интервале 2..15. Программа при помощи цикла FOR проверяет каждое целое число, входящее в множество. Если целое число является элементом множества, то оно печатается, и из множества удаляются все целые числа, кратные данному числу.

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

Операция IN весьма полезна, и она позволяет, например, выяснить, являются ли два множества равными. Например, если мы хотим узнать, равны ли множества MAP1 и MAP2, то можно написать:

EGALE:= true;

for MEMBER:= ENG to IT DO

if (MEMBER in MAP1) <> (MEMBER in MAP2) then EGALE:= false.

Это громоздко, поэтому в Паскале есть булевские выражения с применением операций сравнения: =, <>, >=, <=.

НАПРИМЕР: MAP1 = MAP2;

MAP1 <> MAP2;

MAP1 - MAP2 <> [FR];

MAP1 + MAP2 <> [ENG..IT];

MAP1 >= MAP2 (eсли выражение истинно, то MAP2 есть подмножество MAP1).


8.3 Печать множеств

При работе с множествами немаловажным является вопрос распечатки элементов множества. Отметим, что в большинстве версий языка в операторах WRITE нельзя называть переменные типа "множество". Например, нельзя распечатать множество таким образом:

VAR A: SET OF 1..9;

WRITE(A).

Здесь нет ничего удивительного, т.к. даже если А есть массив, то его тоже нельзя распечатать сразу с помощью одного оператора WRITE(А). Для вывода элементов массива организуются циклы.

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

if K in SET1 then write(K).

Как правило, для целей распечатки элементов множеств организуются свои процедуры. Пусть мы имеем дело с множествами, состоящими из целых чисел в границах NIZ и VERH. Зададим множественный тип TS для этих границ:

type INT = NIZ..VERH; TS = set оf INT.

Тогда можно написать процедуру, содержащую в качестве параметра множество:

procedure PRINTSET (OS: TS);

var M: INT;

begin

¦ for M:= NIZ to VERH do

¦ if M in OS then writeln(M);

end.

Теперь можно обращаться к этой процедуре для печати множеств, если только они состоят из элементов, не выходящих из интервала NIZ..VERH. Пусть в разделе констант было описано:

const NIZ = 0; VERH = 10;

тогда можно распечатать множества, обратившись к процедуре:

а) PRINTSET ([5,6,7]); б) PRINTSET ([2]); в) PRINTSET ([3..8]).

Обращение к процедуре можно организовать также в виде:

var SET1, SET2: TS;

SET1:= [..... ]; SET2:= [......]

PRINTSET (SET1); PRINTSET (SET1+SET2); ит.д.

ПРИМЕР 3. В заключение рассмотрим пример целиком, где продемонстрируем все те действия, которые определены над множествами:

program IGRA;

type KOST = 1..6; BROSOK = set of KOST;

var A,B,C: BROSOK;

procedure SRAWNENIE (D: BROSOK);

var K: KOST;

begin

¦ for K:= 1 to 6 do

¦ if K in D then write(K:4); writeln;

end;

begin

¦ A:= [1,3,4]; B:= [2,4,6]; C:= A + B;

¦ write('[1,3,4] + [2,4,6] ='); SRAWNENIE (C);

¦ C:= A - B;

¦ write('[1,3,4] - [2,4,6] ='); SRAWNENIE (C);

¦ C:= A * B;

¦ write('[1,3,4] * [2,4,6] ='); SRAWNENIE (C);

end.

ПОЯСНЕНИЕ. В программе определяются множества A, B, C типа BROSOK, элементами которых являются целые числа из диапазона [1..6], и процедура вывода на печать элементов таких множеств.

ЗАМЕЧАНИЕ 1. Если множество задано перечислимым типом, то его элементы напечатать нельзя. На печать можно вывести элементы только ординального типа: INTEGER, CHAR, BOOLEAN, интервальный.

ЗАМЕЧАНИЕ 2. Один и тот же набор данных можно организовать в виде линейного массива ARRAY, в виде множества SET и в виде строки типа STRING. Какой из этих видов предпочтительнее? Если над элементами (числами) производятся действия, то лучше ARRAY. Если же стоит задача о взаимосвязи элементов нескольких множеств или вопрос о вхождении каких-то объектов в множество, то лучше SET.


9. КОМБИНИРОВАННЫЙ ТИП - ЗАПИСИ. ДАННЫЕ ТИПА RECORD

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

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

9.1 Определение типа RECORD

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

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

type RY = array [1..10] of integer;

var A: RY,

можно писать А[1],...,A[10]. Аналогичная ситуация имеет место и для записи. Здесь также можно использовать поля записи как отдельные переменные.

ПРИМЕР: type PATIENT = record

NAME: string [20];

MALADI: string [40];

AGE: integer;

MARIE: boolean;

end;

var NEKTO:PATIENT.

Это есть описание типа RECORD. Структура записи такого типа определяется здесь с помощью всех полей между RECORD и END.

В рассмотренном выше примере всей структуре этого типа присвоено имя PATIENT (пациент). Запись типа PATIENT состоит из четырех отдельных переменных, т.е. полей, которые имеют имена: NAME, MALADI, AGE, MARIE. Каждое из этих полей имеет свой тип. В разделе TYPE описывается тип PATIENT, который затем присваивается переменной NEKTO. Именно NEKTO есть переменная типа PATIENT, т.е. переменная типа RECORD.

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

ПРИМЕР:

NEKTO.NAME: = 'MANUELA'; NEKTO.AGE:= 20;

NEKTO.MALADI: = 'GRIP'; NEKTO.MARIE: = true.

Отметим, что поле записи, например поле NEKTO.AGE, может рассматриваться как обычная простая переменная целого типа:

NEKTO.AGE:= NEKTO.AGE + 1. Вместе с тем, запись может рассматриваться как единое целое. Пустьимеетсяследующееописание:

type DATE = record

DAY: 1...31;

MONTH: (JAN, FEB, MAR, APR, MAY, JUN, JUL,

AUG, SEP, OCT, NOV, DEC);

YEAR: integer;

end;

var HB, MB: DATE.

Мы видим, что HB и MB имеют тип DATE. Помимо действий над отдельными полями записей HB и МB можно выполнять операции над всей записью: HB:= MB.

Это присваивание эквивалентно следующей последовательности операторов: HB.DAY:= MB.DAY;

HB.MONTH: = MB.MONTH;

HB.YEAR:= MB.YEAR.

Для переменных этого типа вводятся сравнения: " = " и " <> ".

Так в нашем случае логическое выражение МB=HB является истинным.

Так как на тип компонент массива не накладывается ограничений, то можно образовывать массивы, компонентами которых являются записи. Например, вместоVAR NEKTO: PATIENT можнозаписать VAR NEKTO: ARRAY [1..N] OF PATIENT. Тогда фамилию первого пациента можно указать как NEKTO [1].NAME. Аналогично можно задать множество дат рождений N персон VAR BD: ARRAY[1..N] OF DATE.

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

WRITELN (BD[3].YEAR).

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


9.2 Оператор WITH

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

with NEKTO do

begin

NAME:= 'MANUELA'; AGE:= 20;

MALADI:= 'GRIP';

MARIE:= true;

end.

Другими словами, в рамках оператора, помещенного внутри оператора WITH, к полям определенной переменной можно обращаться просто по имени (префикация имен опускается).

Особенно эффективно использовать WITH, когда речь идет о вложенных записях, т.е. таких, где поля есть тоже записи. Например, запись типа PATIENT можно расширить добавлением поля DATE, которое снова есть запись с 3-мя полями:

type PATIENT = record

NAME: string [10];

MALADI: string [30];

DATE: record

DEN: integer;

MESJATS: string [10];

GOD: integer;

end;

MARIE: boolean;

end;

var NEKTO: PATIENT.

При таком вложении доступ, например, к полю GOD уже должен сопровождаться указанием двух префиксных имен, например:

read (NEKTO.DATE.GOD).

Здесь уже WITH может значительно упростить работу с полями:

with NEKTO, DATE do

begin

NAME:= 'MANUELA'; AGE:= 20;

MALADI:= 'GRIP';

DEN:= 18;

MESJATS:= 'MART';

GOD:= 1944;

MARIE:= TRUE;

end.

Оператор WITH принято называть оператором присоединения. В общем случае он выглядит так: WITH R1, R2,..., Rn do S, что эквивалентно WITH R1 do WITH R2, R3,..., Rn do S.

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

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

Пусть, например, имеется простая переменная AGE и поле AGE некоторой комбинированной переменной NEKTO. В этом случае их можно отличить, т.к. простая переменная имеет имя AGE, а переменная-поле имеет полное имя NEKTO.AGE. А что будет в операторе WITH, где префикс NEKTO опускается?