Книга: Песни о Паскале

Глава 40 Пристрелка на знакомых мишенях

Глава 40

Пристрелка на знакомых мишенях


Итак, из арсенала Паскаля мы извлекли ещё одно мощное оружие – массивы. Опробуем его на знакомых мишенях, – некоторые наши программы можно улучшить, например, программу «вопрос-ответ» или полицейскую базу данных.

Вопрос-ответ – добиваемся гибкости

В 16-й главе мы смастерили шуточную программку, невпопад отвечающую на вопросы пользователей. Жаль только, что ответы намертво вбиты в саму программу. Скоро пользователям надоест смеяться над одним и тем же, и они забросят игрушку. Так пусть ваши приятели сами сочиняют смешные ответы и помещают их в текстовый файл, и тогда программа при запуске будет загружать их оттуда.

Прежде всего, подумаем над размещением вводимых из файла строк, где поселить их? «В массиве строк», – скажете, и будете правы. А сколько элементов запасти в этом массиве? Чем больше, тем лучше? Некоторые компиляторы накладывают ограничение на размер массива, но сотню строк они позволят, и этого пока достаточно. Итак, для хранения ответов объявим массив из 100 строковых переменных.

Перейдем к процедуре ввода этих строк. Техника ввода массива рассмотрена в предыдущей главе. Но теперь надо ещё и подсчитать введенные строки, иначе в дальнейшем мы не всегда сможем правильно индексировать массив, — ведь фактическое количество строк в файле может быть и меньше ста. С этой целью объявим переменную Fact, в которой и сделаем нужный нам подсчёт.

Обсудив эти моменты, обратимся к программе «P_40_1».

{ P_40_1 – Программа "вопрос-ответ" с применением массива }
const CAnswers = 100; { размер массива с ответами }
      { объявление типа для массива ответов }
type TAnswers = array[1..CAnswers] of string;
var Answers : TAnswers; { объявление массива ответов }
      Fact : integer;       { фактическое количество ответов }
      F : text;       { файл с ответами }
      S : string;       { строка с вопросом }
{ Процедура ввода ответов из файла с подсчетом введенных строк }
procedure ReadFromFile(var aFile: text);
var i: integer;
begin
Fact:=0; { для начала подсчета строк обнуляем счетчик }
{ цикл по массиву строк }
for i:=1 to CAnswers do begin
      if Eof(aFile) then Break; { если конец файла – выход}
      Readln(aFile, Answers[i]); { читаем строку в элемент массива }
      Fact:= Fact+1;       { наращиваем счетчик строк }
end;
end;
begin {--- Главная программа ---}
      Assign(F, 'P_40_1.in'); Reset(F);
      ReadFromFile(F);
      Close(F);
      Randomize; { чтобы порядок вопросов не повторялся }
      { Начало главного цикла }
      repeat
      Write('Введите вопрос: '); Readln(S);
      if S<>'' then Writeln(Answers[Random(Fact)+1]);
      until S='';
end.

Открыв файл ответов «P_40_1.IN», мы вызываем процедуру ReadFromFile (читать из файла), которая загружает строки в массив Answers (ответы). Она же подсчитывает введенные строки в переменной Fact. Таким образом, если файл содержит больше сотни строк, то в массив попадёт первая сотня, а иначе — столько, сколько там есть фактически, и это количество покажет переменная Fact. Дальше всё работает, как в прежнем варианте: после ввода вопроса ответ случайным образом выбирается из массива. Индекс элемента с ответом определяется выражением Random(Fact)+1. Если помните, функция Random(Fact) возвращает значения в диапазоне от 0 до Fact-1, а индексы нашего массива начинаются с единицы.

Полицейская база данных – ускоряем поиск

А теперь освежите в памяти другое наше творение – программу поиска угнанных автомобилей в полицейской базе данных (глава 29). Её слабость в том, что поиск номеров выполняется в текстовом файле. Ах, если б вы знали, как «тормозит» такой поиск! Вы не заметили? Да, на десятках строк этого не ощутить, иное дело – сотни тысяч, или миллионы. Итак, перенесем список номеров из текстового файла в массив, и тогда поиск ускорится многократно!

В программе «P_40_2» обратите внимание на пропуск пустых строк в процедуре ReadFromFile. Если этого не сделать, счётчик Fact может оказаться на 1 больше, чем должно, – так случится, если за последним числом будут пустые строки. Следующий далее оператор чтения числа пренебрегает границами между строками, поэтому в одной строке допустимы несколько чисел.

{ P_40_2 – Полицейская база данных с применением массива }
const CNumbers = 1000; { размер массива с номерами автомобилей }
      { объявление типа для массива номеров }
type TNumbers = array[1..CNumbers] of integer;
var Numbers : TNumbers; { объявление массива номеров }
      Fact : integer;       { фактическое количество номеров в файле }
      F : text;       { файл с номерами }
      Num : integer;       { номер проверяемого автомобиля }
      { Процедура ввода номеров из файла }
procedure ReadFromFile(var aFile: text);
var i: integer;
begin
Fact:=0; { для начала подсчета номеров обнуляем счетчик }
for i:=1 to CNumbers do begin { цикл по массиву номеров }
while Eoln(aFile) do { Пропуск пустых строк }
      if Eof(aFile) then Break else Readln(aFile);
if Eof(aFile) then Break; { если конец файла – выход из цикла }
Read(aFile, Numbers[i]); { читаем номер в элемент массива }
Fact:= Fact+1;       { наращиваем счетчик номеров }
end;
end;
{ Функция поиска в массиве номеров автомобилей }
function FindNumber(aNum: integer): boolean;
var i: integer;
begin
      FindNumber:= false;
      for i:=1 to Fact do
      if aNum=Numbers[i] then begin
      FindNumber:= true;       { нашли ! }
      Break;       { выход из цикла }
      end
end;
begin       {--- Главная программа ---}
      { открываем файл и читаем номера автомобилей }
      Assign(F, 'P_38_2.in'); Reset(F);
      ReadFromFile(F);       { ввод номеров из файла }
      Close(F);
      repeat       { Главный цикл }
      Write('Укажите номер автомобиля: '); Readln(Num);
      if FindNumber(Num)
      then Writeln('Эта машина в розыске, хватайте его!')
      else Writeln('Пропустите его');
      until Num=0; { 0 – признак завершения программы}
end.

Ещё раз о статистике

Следующая программка будет маленькой, да удаленькой. Вернемся к статистике, с которой познакомились при обработке классного журнала. Напомню, что статистика – это наука, изучающая массовые явления. В текстах наших программ полным-полно разных букв, – давайте посчитаем их. Результатом работы программы будет таблица, похожая на эту.

a 119
b 45
c 72
...

Здесь левый столбец составляют буквы, а правый – количество этих букв в некотором файле. Упростим себе задачу, ограничившись подсчетом лишь маленьких латинских букв от «a» до «z».

Для подсчета общего количества символов в файле хватило бы одного счетчика. Но здесь 26 букв, а значит и счетчиков надо столько же. Массив счетчиков напрашивается сам собой, его тип можно объявить так:

type TCounts = array [1..26] of integer;

Однако не спешите этого делать. Вспомните о том, что индексом массива может быть любой порядковый тип данных. А к ним, наряду с числами, относятся символьный и даже булев тип. Стало быть, допустимы такие массивы.

type TA = array ['A'..'F'] of integer;
      TB = array [false..true] of integer;

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

type TCounts = array ['a'..'z'] of integer;

Теперь символ, прочитанный из файла, можно использовать как индекс в массиве счетчиков, надо лишь предварительно проверить его на попадание в нужный диапазон.

Входным файлом программы будет текст её самой же. Вот она, простая и красивая.

{ P_40_3 – Подсчет количества различных букв в файле }
      { Тип массива из целых чисел, индекс – символьный }
type TCounts = array ['a'..'z'] of integer;
var Counts : TCounts; { массив из счетчиков букв }
      c: char; { текущий символ файла, он же – индекс счетчика }
      F : text; { файл с текстом программы }
begin       {--- главная программа ---}
{ Перед началом подсчета все счетчики обнуляем }
for c:='a' to 'z' do Counts[c]:=0;
{ Открываем входной файл для чтения }
Assign(F, 'P_40_3.pas'); Reset(F);
while not Eof(F) do begin { Цикл чтения и подсчета букв }
Read(F, c);       { чтение одного символа из файла }
if c in ['a'..'z']       { если символ в нужном диапазоне }
      then Counts[c]:= Counts[c]+1; { наращиваем его счетчик }
end;
Close(F);
{ После подсчета распечатаем все счетчики }
for c:='a' to 'z' do Writeln (c, Counts[c]:6);
Write('Нажмите Enter'); Readln;
end.

Здесь осталась лишь одна шероховатость – при печати результатов часть строк не поместится на экране. Так направьте вывод в текстовый файл. Или слабо?

Итоги

• Массивы, как любые переменные, «живут» в оперативной памяти. Переместив данные из файлов в массивы, мы многократно ускорим их обработку.

• Для индексации массивов допустимы любые порядковые типы данных. Выбор подходящего типа для индекса упрощает и украшает программу.

• При чтении чисел из текстового файла в «боевых» программах необходимо учитывать возможное наличие в файле пустых строк. Такие строки могут привести к чтению оператором Read несуществующего пустого числа (см. процедуру ReadFromFile в программе «P_40_2»).

А слабо?

А) Напишите программу для подсчета различных цифр в файле полицейской базы данных (считать надо именно цифры, а не числа!).

Б) Объявите массив из сотни целых чисел, заполните его случайными числами в диапазоне от 0 до 255 и распечатайте этот массив.

В) Найдите в массиве (задание Б) все элементы, хранящие число 7 (если таковые найдутся). Напечатайте индексы элементов, которые содержат это число.

Г) Заполните массив (задание Б) случайными числами в диапазоне от 0 до 255 так, чтобы ни одно из них не повторялось. Воспользуйтесь вспомогательным множеством чисел, где будут запоминаться сгенерированные ранее числа.

Д) Найдите в массиве (задание Г) наименьшее и наибольшее числа, напечатайте их, а также соответствующие им индексы элементов массива.

Е) Вращение массива вправо. Объявите массив из 10 чисел и заполните его случайным образом. Напишите процедуру, перемещающую 1-й элемент на 2-е место, 2-й – на 3-е место и т.д. Последний элемент должен занять 1-е место.

Ж) Вращение массива влево. Напишите процедуру для перемещения 2-го элемента на 1-е место, 3-го – на 2-е место и т.д. При этом первый элемент должен стать последним.

И) Напишите функцию для подсчета количества номеров в полицейской БД при условии, что одна строка может содержать несколько номеров, а некоторые строки (в т.ч. в конце файла) могут быть пустыми.

Оглавление книги

Оглавление статьи/книги

Генерация: 1.729. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз