Песни о Паскале
Шрифт:
type TDesk = array [1..800] of Boolean;
Здесь разумеем, что первые 40 элементов массива хранят верхнюю строку щита, следующие 40 элементов, – вторую строку и так далее. Не очень удобно, правда?
Можно сделать иначе: сначала объявить отдельную строку щита TLine как массив из 40 лампочек.
type TLine = array [1..40] of Boolean;
И тогда весь щит представится массивом из 20 таких строк, – это будет массив массивов.
type TDesk = array [1..20] of TLine;
То
type TDesk = array [1..20] of array [1..40] of boolean;
Подчеркнутое означает отдельную строку щита. Паскаль разрешает собрать все индексы объявления внутри одних скобок и записать всё это ещё короче.
type TDesk = array [1..20, 1..40] of boolean;
Так мы получили структуру, которую математики называют матрицей, а программисты – двумерным массивом. Матрицы состоят из строк и столбцов. Для доступа к элементам матрицы нужны два индекса, один из которых указывает номер столбца, а другой – номер строки. Например, элемент матрицы Desk, стоящий в 5-м столбце 3-й строки, доступен так:
Desk[3, 5]
Разумеется, что для индексов позволены числовые выражения, значения которых должны лежать в объявленных пределах. При обработке матриц применяют циклы, их можно организовать как по строкам, так и по столбцам. Возьмем для примера наш рекламный щит, объявим его тип, а потом заполним значением FALSE.
const Cx = 40; { количество столбцов (ширина) }
Cy = 20; { количество строк (высота) }
type TDesk = array [1..Cy, 1..Cx] of boolean; { тип «рекламный щит» }
var Desk : TDesk; { переменная «рекламный щит» }
Здесь пределы для индексов указаны через константы Cx и Cy. Заполнить матрицу значением FALSE можно двумя вложенными циклами:
for y:=1 to Cy do
for x:=1 to Cx do Desk[y, x]:= False;
То же самое делается быстрее и короче известной вам процедурой заполнения FillChar:
FillChar(Desk, SizeOf(Desk), false);
Здесь значение SizeOf(Desk) составит 800 – это количество элементов матрицы.
Можно обрабатывать и отдельные строки, и отдельные столбцы матрицы. Например, заполнить значением TRUE 5-й столбец:
for y:=1 to Cy do Desk[y, 5] := True;
А для заполнения 3-й строки организовать такой цикл:
for x:=1 to Cx do Desk[3, x] := True;
Если вам понятна техника работы с матрицами, перейдем к программе «P_49_2».
Начнем с процедуры ReadDesk, что вводит матрицу из файла. Условимся считать, что крестикам в матрице Desk соответствует TRUE, а ноликам – FALSE. Входной файл обрабатываем
Desk[y,x]:= S[x]='+'; { TRUE, если S[x] содержит крестик }
Следовательно, для ввода матрицы нужны два вложенных цикла: внешний – по строкам и внутренний – по столбцам.
Схоже работает и процедура WriteDesk, выводящая матрицу на экран. Здесь внутренний цикл формирует строку из 40 символов, каждый из которых может быть либо крестиком либо ноликом. Выбор пары символов – дело вкуса, в нашем случае пара определяется строковой константой CSymbols.
const CSymbols : string = '0+';
Нужный символ из этой строки выбирается по индексу.
S:= S + CSymbols[1+ Ord(Desk[y, x])];
Так, для значений Desk[y,x], равных FALSE, будет выбран первый символ строки ('0'), а для TRUE – второй ('+'), что равнозначно следующему громоздкому оператору.
if Desk[y, x]
then S:= S + CSymbols[2]
else S:= S + CSymbols[1]
Далее следуют две простые процедуры зеркального отражения матрицы относительно горизонтальной и вертикальной осей, – они всего лишь переставляют симметрично расположенные элементы.
Процедура инверсии рекламного щита ещё проще, – она меняет значения элементов матрицы на противоположные. Наконец, в главной программе после чтения из файла исходного изображения организован цикл ввода и обработки команд пользователя. Вводя одну из трёх команд (1, 2 или 3), пользователь крутит изображение туда-сюда, а также инвертирует его. Вот полный текст этой программы.
{ P_49_2 – Рекламная панель "крестики-нолики" }
const Cx = 40; { количество столбцов (ширина) }
Cy = 20; { количество строк (высота) }
type TDesk = array [1..Cy, 1..Cx] of boolean;
var Desk : TDesk;
{ Чтение исходного состояния панели из текстового файла }
procedure ReadDesk(var F: Text);
var x, y: integer; { x – индекс столбца, y – индекс строки }
S: string;
begin
FillChar(Desk, SizeOf(Desk), false);
y:=1;
while not Eof(F) and (y<=Cy) do begin
Readln(F, S);
x:=1;
while (x<=Length(S)) and (x<=Cx) do begin
Desk[y,x]:= S[x]='+';
Inc(x); { x:= x+1 }
end;
Inc(y); { y:= y+1 }
end
end;
{ Вывод текущего состояния панели в текстовый файл }
procedure WriteDesk(var F: Text);
const CSymbols : string = '0+';
var x, y: integer; S: string;
begin
for y:=1 to Cy do begin
S:='';
for x:=1 to Cx do S:= S + CSymbols[1+ Ord(Desk[y, x])];