Программирование на языке Delphi/§4

Материал из Викиверситета

Символьный тип данных

Символьный тип данных на языке Delphi - char. Его размер 1 или 2 байта(в версиях ранее Delphi 2009 тип char был размерностью 1 байт, далее 2 байта. В современных версиях однобайтовым является тип AnsiChar ). Для кодировки используется код ASCII. В поздних версиях Delphi для кодировки используется Unicode, поэтому размер символьного типа увеличен до 2 байт. Значения переменных и литералов(констант) должны быть заключены в апострофы.

var c:char;
begin
 c:='a';//латинская строчная a
 c:=#$61;// то же, так как код этой буквы в шестнадцатеричной системе равен 61
 c:=#97; //то же, так как код этой буквы в десятичной системе равен 97
 c:='ф'; //кириллическая буква ф
 c:=#$444;// то же, так как код этой буквы в шестнадцатеричной системе равен U+444
 c:=#1092;// то же, так как код этой буквы в десятичной системе равен U+1092
 //функция ord(c) или прямое преобразование типов word(c) вернет целочисленное значение, равное 1092
 writeln(c,'=', ord(c));//вернет "ф=1092"
end.

Операторы цикла

В языке Delphi для создания циклов используются операторы while,repeat..until,for.

Операторы while

Оператор while(навываемый также оператором с предусловием) имеет следующую структуру:

 while <логическое условие> do <тело цикла>

Под телом цикла подразумевается либо одиночная, либо составная инструкция( то есть набор инструкций, вложенный в скобки begin/end) Тело цикла повторяется до тех пор, пока логическое условие(то есть выражение булева типа) истино. Когда оно становится ложным, управление передается следующему после while оператору.

{$APPTYPE CONSOLE}

var a:integer;
begin
readln(a);
while(a>10) do begin //Если а меньше либо равно десяти, то цикл не будет выполняться
    a:=a-1;
    writeln('a= ',a);
    end;
end.

В зависимости от логического условия, цикл может ни разу не выполниться. Если в цикле требуется использование более одного оператора, то необходимо использовать begin..end;

Оператор repeat

Оператор цикла repeat(называемый оператором цикла с постусловием) аналогичен оператору while, но отличается от него, во-первых, тем, что условие проверяется после очередного выполнения операторов тела цикла и таким образом гарантируется хотя бы однократное выполнение тем цикла, а во-вторых, тем, что критерием прекращения цикла является истинность булева выражения (постусловия). Оператор repeat имеет следующую структуру:

repeat
<тело цикла>
until <логическое условие выхода из цикла>

Также, для тела цикла не нужно использовать begin и end, так как в данном случае скобками тела цикла являются сами ключевые слова repeat/until.

{$APPTYPE CONSOLE}

var a:integer;
begin
a:=10;
repeat
writeln(a);
  a:=a-1;
  until a<5 ;
readln;
end.

Оператор for

Оператор for имеет следующую структуру:

for <счетчик цикла> := <нижняя граница> to/downto <верхняя граница> do <тело цикла>

В отличие от операторов while и repeat, при использовании оператора for мы (явно или неявно) задаем количество повторений тела цикла. Счетчик цикла - любая переменная порядкового типа. Для правильно работы цикла, равно как и в целях быстродействия, рекомедуется, чтобы счетчик цикла был локальной переменной(то есть эта переменная должна быть определена в той же функции, что и сам цикл).

Нижняя граница - выражение(константа либо вычисляемое), к которому будет приравнен счетчик цила, при первом выполнении тела цикла. Когда счетчик выйдет за пределы верхней границы(которая также может быть константой либо вычисляемым выражением), цикл прекратится.
Если нижняя граница больше верхней, и используется прямой цикл( то есть выбрана конструкция to, а не downto ), то цикл не будет выполнен ни разу (подобно этому,если нижняя граница равна верхней, то цикл выполнится ровно один раз). После выполнения тела цикла, параметр цикла увеличивается на единицу(или уменьшается, если вместо to мы используем downto).

Не следует использовать значение счетчика цикла, после выхода цикла.

{$APPTYPE CONSOLE}

var i,x,y:integer;
begin
readln(i); //Неважно какое значение у i, при входе в цикл, ее значение приравнивается к 10
for i:=10 downto 2 do  //После выполнения тела цикла, значение i уменьшается на единицу
   writeln(i);

for i:=2 to 1 do  //тело цикла не выполнится ни разу
   writeln(i);

for i:=1 to 1 do  //тело цикла  выполнится ровно один раз
   writeln(i);
x:=-1;y:=2;
for i:=x to y do begin //границы цикла могут быть неизвестны на этапе компиляции. В данном случае тело цикла  выполнится 4 раза
   writeln(i);
end;
for i:=x downto y do  //поскольку x<y, а цикл обратный(используется downto), то тело цикла выполнено не будет
   writeln(i);

readln;
end.

Не допускается присвоение внутри тела цикла значений переменной-счетчику цикла.

Обратный цикл (с использованием downto, когда счетчик цикла уменьшается от первого граничного значения ко второму) может быть слегка эффективнее(быстрее) прямого. Это имеет значение, если нужна максимальная производительность, а тело цикла маленькое и простое.

Продвинутые техники оператора for в поздних версиях Delphi

Цикл for может быть применен для для всех элементов значения перечисляемого типа в формате

for <итератор> in <значение_перечисляемого_типа> do <простая_или_составная_инструкция>;

В каждой итерации, переменная-итератор принимает значение текущего элемента значения перечисляемого типа.

procedure test3(x,y:integer);
var
    c:char;
    i:integer;
    s:string;
    sl:  TStringList;
    d:array of integer;

begin
 writeln('Диапазон A..Z');
  for c in ['A'..'Z'] do  write(c);
//вернет ABCDEFGHIJKLMNOPQRSTUVWXYZ
  x:=1; y:=3;
 writeln('');
 writeln('Диапазон 1..3');
  for i in [x..y] do begin
    write(i,',');
  end;
//вернет 1,2,3,
  writeln('');
  sl:=TStringList.Create();
  sl.Add('Строка1');
  sl.Add('Строка2');

  writeln('');
  writeln('Перечислимое выражение типа TStringList');
  for s in sl  do     write(s,',');
//вернет "Строка1,Строка2,"


    writeln('');
    if CompilerVersion>=28 then// Delphi XE7
        d:=[1,2,3]//в Delphi XE7 допускается такая инициализация динамического массива
    else begin
     Setlength(d,3);
     d[0]:=1;d[1]:=2;d[2]:=3;
    end;
 writeln('');
 writeln('Перечислимое выражение типа динамический массив целых( array of integer)');
  for i in d do begin  write(i,',');  end;
  // вернет 1,2,3,
  writeln('');
  writeln('Перечислимое выражение пользовательского типа -- числа фибоначчи');
  for i in TFibonacci.GetNumbers(20) do write(i,',');
  //вернет 0,1,2,3,5,8,13,
  writeln('');
end;

для полноты приведем реализацию записи(record ) TFibonacci

 TFibonacci=record
   private
   pp,p, max:integer;
   function GetCurrent:integer; inline;
   public
   function GetEnumerator():TFibonacci;
   function MoveNext():boolean;
   property Current:integer read GetCurrent;
   class function GetNumbers(max:integer):TFibonacci;static;
 end;

{ TFibonacci }

function TFibonacci.GetCurrent: integer; //процедура -- "геттер" для свойства Current
//всякий перечислитель изначально не инициализирован: перед первым вызовом Current,
//необходимо вызвать MoveNext, и убедится, что эта функция вернула true
begin
 result:=pp+p;
end;

function TFibonacci.GetEnumerator: TFibonacci;// всякий перечислимое значение структурного типа(класс или запись)
//должен иметь публичную функцию GetEnumerator, возвращающюю структурный тип,
// использующийся как перечислитель(Enumerator), реализующий MoveNext():boolean
// и свойство Current. В данном случае экземпляр возвращает самого себя
begin
   result:=self;
end;

class function TFibonacci.GetNumbers(max: integer): TFibonacci;//своего рода конструктор
var fb:TFibonacci;
begin
  result.max:=max;
  result.pp:=-1; result.p:=0;
end;

function TFibonacci.MoveNext: boolean;
// передвигает указатель перечислителя на следующую позицию, или в случае неинициализированного
// перечислителя -- на первую позицию перечисляемого значения,либо возвращает false, означающее
// конец перечисления
var prev:integer;
begin
 prev:=GetCurrent();
 case prev of
 -1:  pp:=0;
  0:  p:=1;
  else begin
     pp:=p;
     p:= prev;
  end;
 end;
 result:= GetCurrent()<max ;
end;

Операторы досрочного завершения цикла

Бывают ситуации, когда надо досрочно завершить цикл, либо пропустить остаток тела цикла, и начать новую итерацию цикла. В первом случае используется оператор break. Во втором continue. Данные операторы доступны для всех типов цикла: while, repeat и for.


Рассмотрим программу:

{$APPTYPE CONSOLE}

var i:integer;
begin
I:=-10;
while i<5 do begin
  writeln(10/i);
  i:=i+1;
  if (i=0) break;//досрочное завершение цикла
  end;
readln;
end.

Эта программа выводит результат деления 10 на i. Но как мы видим, i может принять значение равное 0, и тогда деление на 0 приведет к аварийному завершению программы. Чтобы это предотвратить, мы можем прервать цикл, если значение i становится равным 0.

Оператор continue передает управление оператору, стоящему в начале тела цикла. Попробуем исправить программу, используя оператор continue.

{$APPTYPE CONSOLE}
var i:integer;
begin
I:=-10;
while i<5 do begin
    i:=i+1;//модификация переменной, входящей в условие цикла
    if i=0 then continue;//мы пропускаем дальнейшее тело цикла, и начинаем новую итерацию. Помните, что это может 
                         //привести к бесконечному циклу, если модификация переменных, входящих в условие цикла, происходит
                         // в пропущенной(следующей ниже) части цикла
    writeln(10/i);
  end;
readln;
end.

Как мы видим, когда i становится равно 0, то управление передается началу цикла, в результате чего значение i становится равно единице. Благодаря оператору continue, цикл не прервется, и, когда значение i станет равно 5, цикл нормально завершится.

Следует добавить, что одним из немногих случаев, когда использование безусловного перехода (goto) оправдано, является выход из нескольких вложенных циклов.