Песни о Паскале
Шрифт:
p2:= p1;
p1^:= 'Привет!';
Dispose(p1);
Dispose(p2);
end.
Задачи на темы предыдущих глав
В) Ник обожал музыку. Но компьютерный музыкальный проигрыватель раздражал программиста, поскольку при случайном выборе мелодий повторял одни песни, напрочь забывая о других. Предположим, в списке 10 песен, но звучали только три из них: 3, 6, 5, 6, 3, 6, 5 и т.д.
Ник создал «справедливый» проигрыватель, выбирающий мелодии иначе. Все песни состояли в одном из двух списков: «белом» или «черном». Изначально все они были в «белом» списке, и очередная мелодия выбиралась из него случайно, а после проигрывания ставилась в конец «черного». Если в этот момент в «черном» списке состояла половина мелодий, то первая мелодия из «черного» списка возвращалась в «белый». Затем снова случайно выбиралась мелодия из «белого» списка. Так гарантировалось отсутствие повторов ранее проигранных песен в течение достаточно длительного времени. Создайте программу, генерирующую случайные числа (мелодии) в диапазоне от 1 до N представленным выше методом. Значение N не превышает 255.
Г) Распечатывая числовое множество, мы выводили все его элементы по одному, не заботясь об экономии бумаги или места на экране. Напишите экономную процедуру печати множества, учитывающую подряд идущие диапазоны чисел. Вот примеры желаемой распечатки:
1,5..255
0..200,210..255
0..255
2,5,7,10..20,30..40
Глава 53
Массив указателей
Мы научились создавать переменные по мере надобности – динамически. Эти переменные поселяются в куче, но доступны через указатели, что расположены в секции данных или в стеке – статической памяти программы. Взглянете на рис. 118, где каждая клеточка соответствует байту.
Рис.118 – Указатели на динамические переменные
Очевидно, что размещая в куче мелкие переменные, никакой экономии статической памяти не получишь. Но чем крупнее переменная, тем выгоднее выселять её в кучу. Эта идея лежит в основе следующей переделки полицейской базы данных.
Базу данных – в кучу
Последние улучшения полицейской базы данных связаны с её сортировкой и быстрым двоичным поиском. Вдобавок мы выяснили, что номер автомобиля – не единственное, что нужно полицейским. Хорошо бы держать в базе данных фамилию владельца, его адрес, телефон и прочее. Все это можно связать с номером автомобиля в структурном типе данных – записи.
type TRec = record { структура записи о владельце автомобиля }
mNum : integer; { номер авто – 2 байта }
mFam : string[31]; { фамилия владельца – 32 байта }
mAddr: string[63]; { адрес – 64 байта }
mTel : integer; { телефон – 2 байта }
end;
Для экономии памяти отведем под фамилию и адрес укороченные строки: 31 байт – для фамилии и 63 – для адреса. Легко посчитать, что для размещения такой записи потребуется: 2+32+64+2 = 100 байтов (размер строки на единицу больше объявленной длины, вы помните об этом?).
Объявим массив из тысячи таких записей (на первое время хватит).
type TBase = array [1..1000] of TRec;
И посчитаем размер массива, он составит 100 x 1000 = 100000 байтов. Ого! Сто тысяч, – это больше, чем могут позволить вам иные компиляторы (Borland Pascal, например). Но если такой массив и поместится в памяти, немалая его часть по понятным причинам будет пустовать.
А если разместить эти увесистые записи в куче? Тогда в статической памяти останется лишь массив указателей на эти переменные, который займет в памяти 4 x 1000 = 4000 байтов, что вполне приемлемо. А куча? Её вместимость сопоставима с объёмом памяти компьютера и исчисляется мегабайтами. Так мы убьем двух зайцев: сэкономим статическую память программы и выйдем за ограничения, налагаемые компилятором. Рис. 119 поясняет нашу задумку.
Рис.119 – Массив указателей на переменные в куче
Кстати, вы заметили какой-либо порядок записей в куче? Переменные разбросаны там и сям без всякой системы. Кучей, как вы помните, заведует операционная система, и требовать какого-то порядка от неё неуместно.
Итак, исполняя задуманное, учредим ещё один тип данных – указатель на запись.
type PRec = ^TRec;
Тогда база данных в статической памяти представится массивом.
type TBase = array [1..1000] of PRec;
Уловив идею будущей программы, приступим к деталям. План дальнейших действий таков. Сначала создадим вспомогательную программу с двумя процедурами: одна из них – для ввода базы данных из текста в кучу, а другая – для распечатки этой базы. Эта программа будет фундаментом для следующей, где добавим процедуру сортировки записей.
Как обычно, данные будем вводить из текстового файла. Каждая его строка содержит номер автомобиля и фамилию владельца (прочие данные вы добавите позднее). Вот пример входного файла, где номер автомобиля и фамилия разделяются несколькими пробелами.
6723 Иванов
2199 Петров
Первый вариант программы «P_53_1» перед вами, рассмотрим его.
{ P_53_1 – Ввод и вывод полицейской базы данных }
const CSize = 1000; { Емкость базы данных }
type TRec = record { Тип записи для базы данных }
mNumber : integer; { Номер авто }
mFam : string[31]; { Фамилия владельца }
end;
PRec = ^TRec; { Тип указатель на запись }
TBase = array[1..CSize] of PRec; { Тип массив указателей }
var DataBase : TBase; { База данных – это массив указателей }
Count: integer; { Фактическое количество записей в базе }
{ Чтение данных из текстового файла в базу данных }
function ReadData(var F : text): integer;
var N : integer; { номер авто }
S : string; { фамилия }
P : PRec; { временный указатель на запись }
i : integer; { счетчик записей }
begin
Reset(F); i:=0;
while not Eof(F) and (i<CSize) do begin
Inc(i); { i+1 }
{ Читаем строку с номером и фамилией }
Read(F, N); Readln(F, S);
{ Удаляем пробелы в начале строки с фамилией }
while (S[1]=' ') do Delete(S,1,1);
New(P); { Создаем новую запись в куче }
{ Заполняем поля записи }
P^.mNumber := N; P^.mFam := S;
{ Указатель на запись помещаем в массив }
DataBase[i]:= P;
end;
Close(F); ReadData:= i;