http://sulfurzona.com/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Forum
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
 

Паскаль для новичков (часть 11)

Оператор цикла с параметром

 
Паскаль для новичковДовольно часто возникает необходимость в организации циклического выполнения некоторого процесса с заданным числом повторений. Необходимую конструкцию можно собрать из уже известных операторов while- и repeat- циклов. Но тогда программисту придётся позаботиться об инкременте или декременте переменной, ведущей отсчёт итераций, а также задании начального и конечного значения диапазона этой переменной, когда она выступает ещё и в роли индекса, например, для обращения к некоторому массиву, с которым работает организуемый процесс.
 
Для упрощения решения таких задач существует оператор for-цикла с параметром. Предположим, что необходимо перебросить значения элементов M1[100]..M1[150] массива в элементы с соответствующими номерами другого массива M2, тогда решение может выглядеть так:
 
var M1, M2 : array [0..1000] of integer;
j : integer;
begin
for j := 100 to 150 do M2[ j ] := M1[ j ];
 
Таким образом, переменная j будет изменять своё значение согласно последовательности 100,101,102,..,149,150, что обеспечит 51 выполнение тела цикла.
Аналогичную задачу можно решить, задав выполнение цикла в обратном порядке:
 
for j := 150 downto 100 do M2[ j ] := M1[ j ];
 
т.е. переменная j последовательно примет значения 150,149,..,101,100. Итак, теперь уже ясно, что переменная j используется для ведения отсчёта итераций, и в дальнейшем я буду называть её переменная-счётчик.
 
For-цикл допускает указание диапазона значений не только константами, но и выражениями, которые должны возвращать значение дискретного типа. При этом результат выражений вычисляется один раз (предварительно) и только после этого начинается выполнение цикла. Оператор цикла с параметром предусматривает указание в теле только одного оператора, так что частенько может возникать необходимость в использовании составного оператора:
 
for j := Min + 1 to Max - 1 do begin
M2[ j ] := M1[ j ];
Sum := Sum + M2[ j ];
end;
 
При использовании переменной-счётчика цикла, следует учитывать ограничения:
1) Переменная-счётчик должна быть описана в текущем блоке;
2) Переменная-счётчик должна иметь дискретный тип;
3) Значения границ диапазона должны иметь тип, совместимый с типом переменной-счётчика;
4) Категорически запрещается изменять значение переменной-счётчика в теле цикла, например, при помощи оператора присваивания.
 
Как уже, наверно, стало понятно, тело цикла выполняется один раз на каждое значение переменной-счётчика, в пределах диапазона включительно. Служебное слово to указывает компилятору, что необходимо сформировать цикл с наращиванием (инкрементом) значения переменной-счётчика. Перед началом выполнения первой итерации цикла проводится проверка, и если начальное значение больше чем конечное, то цикл не производит ни единой итерации, т.е. тело цикла ни разу не выполняется.
 
Служебное слово downto указывает обратное - на необходимость формировать цикл с убыванием (декрементом) значения переменной-счётчика. Перед началом выполнения первой итерации проводится проверка, и если начальное значение меньше чем конечное, то цикл не производит ни единой итерации.
Если же обе границы диапазона равны, то тело цикла выполняется один единственный раз, независимо от направления изменения значения переменной-счётчика.
Согласно тому, что переменная-счётчик должна иметь дискретный тип, можно сконструировать такой цикл:
 
var Simb : char;
begin
for Simb := 'A' to 'Z' do write( Simb );
end.
 
в итоге получим на экране строчку прописных букв латинского алфавита.
С таким же успехом можно создать цикл, в котором в качестве переменной-счётчика будет переменная перечислимого типа:
 
type TColor = ( clBlack, clWhite, clBlue, clYellow, clRed, clGreen );
const ColorName : array [ clBlack..clGreen ] of string = ( 'Black', 'White', 'Blue', 'Yellow', 'Red', 'Green' );
var Color : TColor;
begin
for Color := clBlack to clGreen do writeln( ColorName[Color] );
end.
 

Оператор над записями

 
Как я уже рассказывал, для применения в программе переменных комбинированного типа (записей) необходимо, при каждом обращении к полям записи указывать имя записи и имя поля, разделяя их точкой. Когда речь идёт о записи с двумя или тремя полями, то тут всё просто, но когда необходимо инициализировать большое количество полей записи, то творческая работа программиста превращается в чисто машинальную. Это, что касается набора исходного текста программы.
 
Теперь, что касается эффективности кода, получаемого вследствие такой громоздкой конструкции обращения к полям записи. Рассматривая имя записи, компилятор формирует адрес, указывающий на начало всей структуры записи, а считывая имя поля этой записи, компилятор добавляет к полученному адресу смещение, соответствующее данному полю. Таким образом, компилятор, формируя последовательное обращение к многочисленным полям записи в пределах одного составного оператора или блока, создаёт неэффективный код, так как приводит к загрузке одного и того же адреса записи многократно.
На это ещё можно закрыть глаза, если такая конструкция единична и не находится в теле цикла и не выполняется многократно. А что делать, если она всё-таки выполняется многократно?
 
Вот как раз для оптимизации таких ситуаций и предназначен оператор над записями. Он позволяет связать серию обращений к полям некоторой записи в единый оператор, с тем, чтобы адрес этой записи считывался только один раз, а затем программа в пределах этого оператора обращалась к полям по их смещениям. В пределах такого оператора отпадает надобность указывать имя записи для каждого поля. Например, конструкцию инициализации записи Image из следующей процедуры:
                                                                                            
                                                                                            
 
 
 
 
procedure InitImage;
type
TBuf = array [0..65000] of byte;
PBuf = ^TBuf;
TImage = record
               Width, Height, BitCount : word;
               Bits : PBuf;
               end;
var Image : TImage;
begin
Image.Width := 320;
Image.Height := 200;
Image.BitCount := 8;
Image.Bits := nil;
GetMem(Image.Bits,Image.Width*Image.Height*(Image.BitCount div 8));
end;
 
можно заменить на эквивалентную ей, но более эффективную и
компактную:
 
begin
with Image do begin
Width := 320;
Height := 200;
BitCount := 8;
Bits := nil;
GetMem(Bits, Width*Height*(BitCount div 8));
end;
end;
 
Из данного примера видно, что достаточно указать имя записи после служебного слова with, и после служебного слова do разместить составной оператор, содержащий серию манипуляций с полями этой записи.
 
Могут возникать случаи, когда нужно манипулировать полями нескольких записей, тогда в with-операторе можно перечислить имена записей через запятую:
 
with Rec1, Rec2 do begin
end;
 
что абсолютно аналогично конструкции:
 
with Rec1 do
with Rec2 do begin
end;
 
Таким образом, действует правило: если в обеих записях имеется поле с именем, например, Field, то компилятор подразумевает, что это поле принадлежит записи Rec2.
 
Аналогичным образом, если в объемлющем блоке описана переменная с именем, совпадающим с именем одного из полей записи, указанной в with-операторе, то в пределах with-оператора идентификатор такой переменной будет считаться именем поля записи. Пример:
 
procedure
type TRec = record
                    …
                    Field : word;
                    end;
var Field : integer;
      Rec2 : TRec;
begin
with Rec2 do begin
{ эквивалентно Rec2.Field := 10 }
Field := 10;
end;
end;
 
К выше сказанному хочу добавить, что применительно к статическим переменным типа запись или к статическим массивам с элементами типа запись и индексом в виде нетипизированной константы компилятор формирует достаточно эффективный код, так как способен вычислить смещение каждого поля ещё на этапе компиляции. Если в качестве индекса элемента такого массива используется переменная, а не константа, то тогда эффективность на стороне with-оператора.
Чтобы добиться высокой производительности при использовании динамических переменных типа массив записей следует применять with-оператор.
 

Пустой оператор

 
Несмотря на то, что пустой оператор не выполняет никаких действий, иногда он позволяет соорудить код программы особым образом. Например, если в ходе составления case-оператора, программист точно знает, что необходимо предусмотреть обработку некоторой альтернативы, но реализацию способа этой обработки он ещё не рассматривал, то в такой альтернативе можно поставить точку с запятой, т.е. заглушку для данной альтернативы:
 
case key of
VK_Tab:;
VK_Return:;
VK_Space:;
end;
 
В данном примере зарезервированы пустые операторы для реагирования на нажатия клавиш Tab, Enter и Space соответственно.
Ещё пустой оператор может помочь в осуществлении ожидания нажатия на клавишу клавиатуры или мыши:
 
repeat until Ms_Click(mbLeft);
 
В данном примере считается, что repeat-цикл содержит в своём теле пустой оператор, а функция Ms_Click(mbLeft) возвращает true, если указанная клавиша мыши была нажата и отпущена.
 
Есть ещё одно очень полезное применение пустого оператора. Предполагается что, например, функция SetTextMode устанавливает текстовый режим с указанным номером, и в случае успеха возвращает true. И в конце программы перед её завершением необходимо вернуться в обычный текстовый режим с помощью вызова данной функции с номером 0, но результатом этого вызова можно пренебречь.
 
А поскольку синтаксис языка Turbo Pascal версии 6.0 требует оформлять вызов функции в виде оператора присваивания или, как в следующем примере, условного оператора:
 
begin
if not SetTextMode(5) then begin
writeln('Ошибка: режим не поддерживается');
halt;
end;
if SetTextMode (0) then;
end.
 
то достаточно в строке if SetTextMode (0) then; указать пустой оператор сразу за служебным словом then, образованный точкой с запятой, и проблема будет решена.
Хочу заметить, что в версии Turbo Pascal 7.0 имеется директива {$X+}, которая включает расширенный синтаксис языка, при котором вызов любой функции можно оформлять как вызов процедуры, например:
 
{$X+}
function One : integer;
begin
One := 1;
end;
 
begin
One;
end.
 
Если эту директиву отключить {$X-}, то придётся вызывать функции в соответствии со старым синтаксисом:
 
{$X-}
function One : integer;
begin
One := 1;
end;
 
var j : integer;
 
begin
j := One;
end.
 
Только хочу предупредить, что функции SetTextMode и Ms_Click, упомянутые здесь, не являются стандартными, а взяты из моих модулей, о которых постараюсь рассказать в дальнейшем.
 
Надеюсь, после всего, что я тут нахомутал, вы смогли сохранить трезвый ум и ясную память ;O)
 
Продолжение следует…
 
© Владислав Демьянишин
 
 
Вы находитесь на официальном сайте Владислава Демьянишина - разработчика игры Dune IV (Dune 4). На нашем сайте Вы можете бесплатно скачать игры Dune IV (Dune 4), Battle City (Танчики с Dendy/Nintendo), читы к играм и многое другое. Также Вы можете скачать бесплатно программы и полезные утилиты. Все программы чистые, т.е. не содержат вирусов и иного вредоносного ПО.
 
Среди доступных программ есть мобильная читалка книг, менеджер переноса файлов с фото- и видеокамер на компьютер, текстовый редактор, WYSIWYG редактор, 3D аниматор, GIF аниматор, AVI аниматор, пакетный конвертор изображений, редактор электрических схем, программа для скриншотов, диспетчер тем рабочего стола и другие.
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
 
 

Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 11): Оператор цикла с параметром
 
 
 
896
 
ВКонтакте
Facebook
 
 
 
На главную страницу На предыдущую страницу На начало страницы
 
 
Украинский портАл Украина онлайн Рейтинг@Mail.ru Рейтинг Сайтов YandeG Rambler's Top100