вторник, 15 февраля 2011 г.

Циклы

Алгоритм, включающий повторение команд до выполнения какого-то условия, называется циклическим.
Циклы в Pascal можно оформить тремя способами:
1) цикл с параметром (счетчик )
For i:=1 to n do ...;
или
 For i:=n downto 1 do ...;
2) цикл с предусловием
while true do ...;
3) цикл с постусловием
 Repeat ... until false;
Цикл с постусловием не использует операторные скобки begin ... end. 
Рассмотрим простой пример: вычислить сумму цифр в заданной строке S.
Если известна длина и она не превышает 255, то можно использовать цикл с параметром:
Sum:=0;
For i:=1 to length(S) do 
 if S[i] in ['0'..'9'] then Sum:=Sum+ord(S[i])-ord('0'); 
В этом примере функция определения длины length(S) будет вызвана только один раз (!) в начале формирования цикла. Выражение ord(S[i])- ord('0') вычислит разность между числами в таблице ASCII и получит нужную цифру. Очень удобно использовать вместо процедуры val. В среде Delphi/Lazarus для этого можно использовать функцию StrToInt(S[i]);
Если использовать цикл с предусловием, то каждый раз придется вызывать эту функцию:
Sum:=0;
i:=1;
While i<=length(S) do begin
   if S[i] in ['0'..'9'] then Sum:=Sum+ord(S[i])-ord('0'); 
   inc(i);
end;
Но можно предварительно запомнить длину строки во временной переменной L:
Sum:=0;
i:=1; L:=length(S);
While i<=L do begin
   if S[i] in ['0'..'9'] then Sum:=Sum+ord(S[i])-ord('0'); 
   inc(i);
end;
Ничего не изменилось, но если нужно это повторить много раз, то по времени цикл с параметром будет быстрее. Даже если применить 2-й вариант цикла с предусловием, то проверка условия тоже требует времени и будет идти медленнее цикла с параметром. Цикл For работает как счетчик и ему не надо делать лишние проверки, только задать начальный и конечный параметр.
Пример, когда же лучше использовать цикл For, нежели While уже обсуждался (см. Простые числа). Теперь рассмотрим случай, когда цикл For нельзя использовать.
Нужно удалить из строки S символ * и продублировать каждый символ, отличный от *. Приведем неправильный код:
For i:=1 to length(S) do begin
if S[i]='*' then delete(S, i, 1)
 else insert (S[i], S, i);
end; 
На первый взгляд, что здесь неверно? Но теперь,зная, что длина строки вычисляется только один раз в самом начале цикла, то ясно, что выполняться он будет ровно столько, сколько символов было в строке до удаления или добавления. Теперь представим, что в строке удалены половина символов. Что будет проверяться после этого? Скорее всего мусор в памяти компьютера, следующий после слова, или что-то важное. Поэтому может произойти сбой программы при ее выполнении. Как исправить положение? Менять на переменную и исправлять ее в теле цикла бесполезно. Вот в этом случае лучше использовать цикл While:
i:=1;
While i<=length(S) do 
if S[i]='*' then delete(S, i, 1)
 else begin insert (S[i], S, i); i:=i+2; end;

Отметим, что переход на следующий символ здесь нужен только при вставке символа и точно на 2 символа вперед, чтобы пропустить добавленный ранее и перейти к следующему.
Можно запомнить длину строки в переменной L и менять ее в теле цикла:

i:=1; L:=length(S);
While i<=L do 
if S[i]='*' then begin delete(S, i, 1); dec(L); end
 else begin insert (S[i], S, i); i:=i+2; inc(L); end;
Рекомендуется использовать функции inc и dec вместо оператора присваивания.
Решите следующие задачи:
1. Определите количество слов в строке, разделенных одним пробелом.
2. Тоже, но с несколькими пробелами.
3. Уберите лишние пробелы в строке.
4. Вставьте пробелы после запятой, точки, и других знаков препинания, и удалите лишние пробелы перед ними.

Комментариев нет:

Отправить комментарий