var k : integer;
begin
for k:=1 to 255 do { цикл по всем элементам множества}
if k in aSet { если входит в множество }
then Write(aFile, k:4); { печатаем в строке }
Writeln(aFile); { по окончании переход на следующую строку }
end;
{----- Программа для проверки процедуры ввода -----}
var S1 : TSet; F, D: text;
begin
Assign(F, ''); Rewrite(F); { вывод на экран }
Assign(D, ''); Reset(D); { ввод с клавиатуры }
S1:= []; { перед вводом опустошаем множество }
ReadSet(D, S1); { вводим множество из файла }
WriteSet(F, S1); Readln; { распечатаем для проверки }
Close(F); Close(D);
end.
Полагаю, что комментарии поясняют все. Обязательно проверьте работу этой программы. Учтите, что вводить данные вы будете с клавиатуры: напечатайте в одной строке несколько чисел, разделяя их пробелами, а затем нажмите клавишу Enter.
Директорская задача, первый вариант
Освоив ввод и вывод множеств, мы вплотную подошли к полному решению директорской задачи. Напомню, что суть решения заключается всего в одном операторе.
R:= [1..250] – (S1 + S2 + S3);
Теперь добавим ввод и вывод множеств. Чтобы не занимать место повторами показанных ранее процедур, я представлю решение в целом.
{ P_37_3 – решение директорской задачи, вариант 1 }
const CMax = 20; { мощность множества, реально 250 }
type TSet = set of 1..CMax; { объявление типа «множество» }
procedure WriteSet(var aFile: text; const aSet : TSet);
{ взять из P_37_2 }
procedure ReadSet(var aFile: text; var aSet : TSet);
{ взять из P_37_2 }
var R, S1, S2, S3 : TSet;
FileIn, FileOut: text;
begin {----- Главная программа -----}
{ Открытие входного файла }
Assign(FileIn, 'P_37_3.in'); Reset(FileIn);
{ Создание выходного файла }
Assign(FileOut, 'P_37_3.out'); Rewrite(FileOut);
{ Ввод множеств из входного файла }
S1:=[]; ReadSet(FileIn, S1);
S2:=[]; ReadSet(FileIn, S2);
S3:=[]; ReadSet(FileIn, S3);
R:= [1..CMax] – (S1+S2+S3); { Решение }
WriteSet(FileOut, R); { Вывод решения в выходной файл }
Close(FileIn); Close(FileOut);
end.
Для ввода и вывода множеств используем дисковые файлы, поэтому оператор Readln в конце программы не нужен. Для облегчения проверки я уменьшил число учеников – константу CMax – с 250 до 20. При тестировании программы входной файл содержал следующие строки.
2 11 4 13
9 17 12 11 3 5 18
14 2 13 15 20
А в выходной файл попали следующие числа.
1 6 7 8 10 16 19
Легко убедиться в том, что никто из этих учеников не состоит в кружках.
Директорская задача, второй вариант
Итак, задача решена, но директор не вполне доволен. Сейчас возможности программы ограничены тремя кружками и двадцатью учениками. При изменении этих данных надо менять и программу, – мы избавимся от этого недостатка.
Во-первых, слегка изменим входной файл. Пусть первая его строка содержит количество учеников в школе; и тогда файл станет таким.
20
2 11 4 13
9 17 12 11 3 5 18
14 2 13 15 20
Во-вторых, отведем для участников кружков не три, а лишь одну переменную типа множество. Затем, по мере чтения строк файла, будем накапливать в этой переменной всех, кто состоит в кружках. Цикл чтения завершится по достижении конца входного файла. Вот и все изменения, посмотрите на второй вариант (процедуры ввода и вывода множеств только обозначены).
{ P_37_4 – решение директорской задачи, вариант 2 }
type TSet = set of byte; { объявление типа «множество» }
{ Здесь надо поместить процедуры ввода и вывода множеств }
procedure WriteSet(var aFile: text; const aSet : TSet);
{ взять из P_37_2 }
procedure ReadSet(var aFile: text; var aSet : TSet);
{ взять из P_37_2 }
var R, S : TSet;
FileIn, FileOut: text;
N: integer; { общее число учеников }
begin
Assign(FileIn, ' P_37_4.in'); Reset(FileIn);
Assign(FileOut, ' P_37_4,out'); Rewrite(FileOut);
Readln(FileIn, N); { читаем общее число учеников }
S:= []; { очищаем перед вводом }
{ пока не конец файла, объединяем участников всех кружков }
while not Eof (FileIn) do ReadSet(FileIn, S);
R:= [1..N] – S; { Решение }
WriteSet(FileOut, R);
Close(FileIn); Close(FileOut);
end.
Согласитесь, программа стала и гибче, и проще. Однако к первому её варианту мы ещё вернемся.
Итоги
• Стандартные процедуры ввода и вывода не способны вводить и выводить множества, для этого создают специальные процедуры.
• Вывод (распечатка) множества выполняется циклом со счетчиком, внутри которого проверяется вхождение каждого элемента в множество.
• Ввод множества из текстового файла основан на операции объединения по отдельности прочитанных элементов.
А слабо?
А) Напишите процедуры для ввода и вывода множества символов. Можно ли здесь для счетчика цикла применить символьную переменную?
Б) Напишите функцию, принимающую числовое множество и возвращающую количество содержащихся в нём элементов.
В) На основе первого варианта директорской программы придумайте способ поиска учеников, записавшихся более чем в один кружок. Или слабо?
Г) Напишите две функции, принимающие строку и возвращающие:
• строку, в которой символы исходной строки встречаются лишь по разу и следуют в алфавитном порядке, например «PASCAL» –> «ACLPS»;
• то же, но порядок следования символов такой же, как в исходной строке, например «PASCAL» –> «PASCL».
Глава 38
Множества в «бою»
Множества, множества… – заполучив столь острое оружие, удержимся ли не пустить его в ход? Вот ещё несколько задач, – мы изрубим их в капусту!
Активисты, шаг вперед!
Прежде всего, отдадим долги Семену Семеновичу. Мы обещали директору выявить разгильдяев, что отлынивают от кружков, и сдержали слово. Теперь найдем активистов, состоящих в нескольких кружках. Откуда подступиться к этой задаче?
Положим для простоты, что в школе лишь три кружка, их списки представлены множествами S1, S2 и S3. Выявить тех, кто состоит одновременно в кружках S1 и S2 легко, – достаточно найти пересечение S1*S2. Точно так же поступим с другими парами: S1 и S3, S2 и S3. Объединив все три пересечения, мы выявим интересующих нас школяров. Итак, решение задачи выразится формулой.