Книга: Песни о Паскале
Глава 37 Ввод и вывод множеств
Разделы на этой странице:
Глава 37
Ввод и вывод множеств
Мы узнали о множествах и приспособили их к директорской задаче. Чтобы покончить с нею доделаем ещё пару пустяков: организуем ввод и вывод множеств. Для ввода-вывода строк и простых типов данных годятся процедуры Read[ln] и Write[ln]. Но сейчас все не так просто, – эти процедуры не способны работать, ни с множествами, ни с другими сложными типами данных. Однако ж «нормальные герои всегда идут в обход», – пойдем так и на этот раз.
Вывод множества в текстовый файл
Начнем с вывода числового множества на экран (или в файл, – что одно и то же). Так мы получим средство для последующей проверки вводимых множеств.
Раз уж процедура Writeln не печатает множество одним махом, выведем каждый его элемент по отдельности – ведь это обычные числа или символы. Проверяя все возможные элементы множества, будем печатать лишь те, что входят в него – в этом основная идея. Напомню, что для такой проверки подходит операция IN. Дополнив её циклом со счетчиком, соорудим несложную процедуру распечатки числового множества. Вот она вместе с программой для её проверки.
{ P_37_1 – вывод множества в файл }
type TSet = set of 1..255; { объявление типа «множество» }
{----- Процедура вывода множества в файл -----}
procedure WriteSet(var aFile: text; const aSet : TSet);
var k : integer;
begin
for k:=1 to 255 do { цикл по всем элементам множества}
if k in aSet { если K входит в множество }
then Write(aFile, k:4); { печатаем в строке }
Writeln(aFile); { по окончании – переход на следующую строку }
end;
{----- Программа для проверки процедуры WriteSet -----}
var S1 : TSet; F: text;
begin
Assign(F, ''); Rewrite(F); { связываем файл с экраном! }
S1:= [3, 10, 25]; { значение множества }
WriteSet(F, S1); { печатаем }
Readln;
Close(F);
end.
В первой строке объявлен тип данных TSet, он может содержать целые числа от 1 до 255. Процедура распечатки WriteSet принимает по ссылке два параметра: файловую переменную и множество, которое надо распечатать. Внутри процедуры работает цикл FOR, перебирающий все возможные элементы множества. Те из них, что содержатся в нём, печатаются в текущей строке. По завершении цикла оператор Writeln переводит позицию записи на следующую строку файла.
Обратите внимание: множество передано в процедуру по ссылке CONST. Передача в процедуры множеств, строк и других сложных типов данных по ссылкам CONST и VAR — это обычная практика. Так повышается скорость работы программ и уменьшается объём памяти, занимаемый параметрами.
Теперь взгляните на оператор Assign(F,''), который назначает файловой переменной пустое имя файла. Так файловая переменная связывается с экраном дисплея (при выводе данных), либо с клавиатурой (при вводе). А когда вам потребуется вывести результаты в дисковый файл, достаточно будет задать нужное имя файла, не меняя процедуры WriteSet (этот прием – подстановка пустого имени – не работает в Pascal ABCNet).
Примечание. В современные версии Паскаля (Delphi) для обработки множеств введён вариант цикла FOR-IN-DO. С ним распечатка множества станет ещё проще:
for k in aSet do Write(aFile, k:4);
Ввод множества из текстового файла.
Разобравшись с распечаткой множества, перейдем к вводу его из файла. Есть соображения на этот счет? Здесь пригодится опыт чтения чисел из строки текстового файла, – вспомните обработку классного журнала. Добавить число к множеству мы тоже умеем: для этого надо объединить его с множеством, состоящим из добавляемого числа. На этих идеях построена процедура ввода, показанная ниже вместе с тестирующей её программой.
{ P_37_2 – ввод и вывод числового множества }
type TSet = set of 1..255; { объявление типа «множество» }
{----- Процедура чтения множества из файла -----}
procedure ReadSet(var aFile: text; var aSet : TSet);
var k : integer;
begin
aSet:= [];
While not Eoln(aFile) do begin { пока не конец строки }
Read(aFile, K); { читаем очередное число }
aSet:= aSet+[K]; { и добавляем к множеству }
end;
Readln (aFile); { переход на следующую строку }
end;
{----- Процедура распечатки множества в файл -----}
procedure WriteSet(var aFile: text; const aSet : TSet);
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».
- Только для взрослых
- Детям до 16–ти
- Глава 1 Путь далек у нас с тобою…
- Глава 2 Вместо теории
- Глава 3 Консольный интерфейс
- Глава 4 Оружие – к бою!
- Глава 5 Программа номер один
- Глава 6 Подготовка к следующему штурму
- Глава 7 Развиваем успех
- Глава 8 Постоянные и переменные
- Глава 9 Переменные: продолжение знакомства
- Глава 10 Условный оператор
- Глава 11 Операторный блок
- Глава 12 Цикл с проверкой в конце
- Глава 13 Правда и кривда
- Глава 14 Дважды два – четыре
- Глава 15 Айда в Монте-Карло!
- Глава 16 Делу время, а потехе час
- Глава 17 И вновь за парту
- Глава 18 Аз, Буки
- Глава 19 Процедуры и функции: разделяй и властвуй
- Глава 20 Процедуры: первый опыт
- Глава 21 Отладка
- Глава 22 О передаче параметров
- Глава 23 Функции
- Глава 24 Криптография
- Глава 25 Текстовые файлы
- Глава 26 Я не читатель, – я писатель!
- Глава 27 Дайте кораблю минутный отдых!
- Глава 28 Редактор и справочная система
- Глава 29 Читайте по-новому
- Глава 30 Журнальная история
- Глава 31 Финал журнальной истории
- Глава 32 Порядковые типы данных
- Глава 33 Вещественные числа
- Глава 34 Структура программы
- Глава 35 Множества
- Глава 36 Множества в Паскале
- Глава 37 Ввод и вывод множеств
- Глава 38 Множества в «бою»
- Глава 39 Командная игра (массивы)
- Глава 40 Пристрелка на знакомых мишенях
- Глава 41 По порядку, становись!
- Глава 42 Кто ищет, тот всегда найдет
- Глава 43 Сортировка по-взрослому
- Глава 44 Строки
- Глава 45 Очереди и стеки
- Глава 46 Огромные числа
- Глава 47 Системы счисления
- Глава 48 Железная логика
- Глава 49 Сложные массивы
- Глава 50 Неспортивные рекорды (записи)
- Глава 51 Указатели в море памяти
- Глава 52 Динамические переменные
- Глава 53 Массив указателей
- Глава 54 Односвязные списки
- Глава 55 Слова, слова, слова…
- Глава 56 И снова очереди, и снова стеки…
- Глава 57 Графомания
- Глава 58 По графу шагом марш!
- Глава 59 Крупные проекты
- Глава 60 Мелкие хитрости
- Глава 61 «Кубики» программиста (ООП)
- Глава 62 Самое интересное только начинается!
- Приложение А Установка и настройка IDE Borland Pascal
- Приложение Б Консольная программа в среде Delphi
- Приложение В Особенности IDE Pascal ABCNet
- Приложение Г Зарезервированные слова
- Приложение Д Ошибки компиляции
- Приложение Е Ошибки исполнения
- Приложение Ж Директивы управления компиляцией
- Приложение З Назначение пунктов меню
- Приложение И Стандартная кодировка символов MS–DOS
- Приложение К Некоторые встроенные процедуры и функции
- Приложение Л Перечень программ
- Приложение М Пример олимпиадной задачи
- Библиография
- Содержание книги
- Популярные страницы
- Ввод и вывод в классе Console
- Листинг 15.1. Простой код файлового ввода-вывода, иллюстрирующий различия между локальной и удаленной передачей данных
- Листинг 15.9. Неэффективная организация диалога с Web-службой, в которой используется множество вызовов
- 3.6 ВЫВОДЫ
- Включение и отключение синхронного вывода
- Операции с множествами узлов
- 5.21 IP-адреса, интерфейсы и множественное пребывание
- Множественные интерфейсы и имена методов
- 2. Правила вывода Армстронга
- 3. Производные правила вывода
- 1.6 Драйверы и буферы ввода-вывода
- 1.8 Ввод-вывод типичного приложения хранения данных