Формат оператора цикла while

Формат оператора цикла while thumbnail

Наиболее мощным средством языка MQL4 является возможность организации циклов.

Довольно часто при создании прикладных программ приходится использовать повторяющиеся
вычисления, чаще это бывают одни и те же программные строки. Для того чтобы программирование
было удобным, а сама программа – понятной, используются операторы циклов. В составе
MQL4 имеется два оператора цикла – while и for. Здесь мы рассмотрим первый из них.

Формат оператора while

Полноформатный оператор цикла while состоит из заголовка, содержащего условие, и
исполняемого тела цикла, обрамлённого фигурными скобками.

while ( Условие ) // Заголовок оператора цикла
{ // Открывающая фигурная скобка
Блок операторов, // Тело цикла может состоять ..
составляющих тело цикла //.. из нескольких операторов
} // Закрывающая фигурная скобка

Если в операторе while тело цикла составляет один оператор, то фигурные скобки можно
опустить.

while ( Условие ) // Заголовок оператора цикла
Один оператор, тело цикла // Тело цикла – один оператор

Правило исполнения оператора while

Пока Условие оператора while является истинным: передать управление
первому оператору тела цикла, а после выполнения всех операторов тела цикла передать
управление в заголовок для проверки истинности Условия.
Если условие оператора
while является ложным, передать управление оператору, следующему за оператором
while.

Рассмотрим пример.

Задача 12. Вычислить коэффициент Фибоначчи с точностью до 10-го знака.

Кратко охарактеризуем коэффициент Фибоначчи. Итальянский математик Леонардо Фибоначчи
обнаружил уникальную последовательность чисел:

1 1 2 3 5 8 13 21 34 55 89 144 233 …

Каждое число в этой последовательности является суммой двух предыдущих чисел. Представленная
последовательность чисел обладает уникальными свойствами: отношение любого числа
к предыдущему равно 1.618, а к следующему – 0,618. Коэффициент 1.618 получил название
коэффициента Фибоначчи, а указанная последовательность называется последовательностью
Фибоначчи (отметим также, что 0,3819 – сопряжённая величина для коэффициента Фибоначчи,
получена в результате его умножения на самого себя: 0.3819 = 0.618 х 0.618).

Задача состоит в том, чтобы вычислить коэффициент Фибоначчи более точно. Если проанализировать
коэффициент Фибоначчи для нескольких десятков элементов последовательности, то
становится очевидным, что полученные коэффициенты колеблются вокруг иррационального
числа 1.61803398875…, принимая поочерёдно то большие, то меньшие значения. Чем
большие числа из последовательности участвуют в вычислениях, тем меньше отклонение
от указанного значения.

Заранее неизвестно, какие именно числа необходимо взять, чтобы их коэффициенты отличались
между собой не более, чем в десятом знаке. Поэтому необходимо составить программу,
которая будет последовательно перебирать коэффициенты до тех пор, пока разница
между ними не окажется меньше 0.0000000001.

В данном случае для решения Задачи 12 создан скрипт (fibonacci.mq4),
потому программа не будет использоваться в длительной работе на каждом тике.
После присоединения к окну финансового инструмента скрипт должен один раз выполнить необходимые вычисления (в том числе показать
результат), после этого он будет выгружен из окна клиентским терминалом.

int start()
{

int i;
double
A,B,C,
Delta,
D;

A=1;
B=1;
C=2;
D=0.0000000001;
Delta=1000.0;

while(Delta > D)
{
i++;
A=B;
B=C;
C=A + B;
Delta=MathAbs(C/B – B/A);
}

Alert(“C=”,C,” Число Фибоначчи=”,C/B,” i=”,i);
return;
}

В начале программы производится объявление переменных (и даётся их описание). В
последующих строках переменным присваиваются численные значения. А, В и С получают
значения первых чисел последовательности Фибоначчи. Здесь необходимо отметить,
что, хотя в самой последовательности идёт речь о целых числах, частное от их
деления должно быть учтено в программе как действительное число. Если бы в данном случае
был использован тип int для этих чисел, то не было бы возможности вычислить коэффициент
Фибоначчи, например, 8/5 = 1 (целое число 1, то есть без дробной части). Поэтому
в данном случае использован тип переменных double. Собственно оператор цикла выглядит
так:

while(Delta > D)
{
i++;
A=B;
B=C;
C=A + B;
Delta=MathAbs(C/B – B/A);
}

Рассмотрим функциональную схему оператора цикла while:

Рис. 42. Функциональная схема исполнения оператора while в программе
fibonacci.mq4.

Оператор цикла начинает свою работу с проверки условия. Многократное выполнение
цикла будет повторяться до тех пор, пока условие (Delta>D) является истинным.
Понимание кода программы существенно упрощается, если при чтении операторов произносить
ключевую фразу – правило исполнения. Например, при чтении оператора while это фраза:
До тех пор пока .., выполнять следующее: ..“. В данном случае заголовок оператора while звучит так: до тех пор, пока Delta
больше D, выполнять следующее.. И далее можно перейти к анализу программных
строк, составляющих тело цикла и исполняемых в случае истинности этого Условия.

Перед тем как управление в приведенном примере
fibonacci.mq4 передано оператору цикла while, значения этих переменных соответственно равны
1000.0 и 0.0000000001, поэтому при первом обращении к оператору цикла условие является
истинным. Это значит, что после проверки условия управление будет передано первому
оператору, составляющему тело оператора цикла.

В данном примере – это оператор:

i++;

В трёх последующих строках вычисляются значения очередного набора элементов последовательности:

A = B;
B = C;
C = A + B;

Легко увидеть, что переменные принимают значения следующих (ближайших больших) элементов.
Если до исполнения оператора цикла значения А, В и С были равны соответственно
1.0, 1.0 и 2.0, то в процессе первой итерации эти переменные принимают значения 1.0, 2.0 и 3.0.

Итерация – повторное выполнение некоторых вычислений; используется для обозначения факта исполнения
программных строк, составляющих тело оператора цикла.

В следующей строке вычисляется интересующая нас разница между коэффициентами Фибоначчи, полученным
на основе последующих (C/B) и предыдущих (B/A) элементов последовательности:

Читайте также:  Конкурс за цикл статей

Delta = MathAbs(C/B – B/A);

В этом операторе используется стандартная функция MathAbs(), вычисляющая абсолютное
значение выражения. Ранее указывалось, что по мере увеличения значений элементов
последовательности, коэффициенты Фибоначчи принимают поочерёдно то большие, то
меньшие значения, в сравнении с “эталоном”. Поэтому разница между соседними
коэффициентами будет принимать то отрицательное, то положительное значение. В то
же время, нас интересует собственно величина этого отклонения, т.е. её абсолютное
значение. Таким образом, независимо от того, в какую сторону отклонилось текущее
значение коэффициента Фибоначчи, значением выражения MathAbs(C/B – B/A), а также
значением переменной Delta, всегда будет положительное число.

Этот оператор является последним в списке операторов, составляющих тело цикла, о
чём свидетельствует наличие в следующей строке закрывающей фигурной скобки. После
исполнения последнего оператора тела цикла управление передаётся в заголовок оператора
цикла для проверки условия. В этом состоит ключевой момент, определяющий сущность
оператора цикла. В зависимости от того, является ли теперь истинным условие оператора
цикла, управление будет передано либо на очередную итерацию в тело цикла, либо за пределы оператора
цикла.

На первых итерациях значение переменной Delta оказывается больше значения, заданного
в переменной D. Это означает, что условие (Delta > D) является истинным, поэтому
управление будет передано в тело цикла на следующую итерацию. Все переменные, участвующие
в вычислениях, получат новые значения, и после достижения конца тела цикла управление
снова будет передано в заголовок для проверки истинности условия.

Этот процесс будет продолжаться до тех пор, пока условие оператора цикла не станет
ложным. В момент, когда значение переменной Delta окажется меньше или равным значению D,
условие (Delta > D) перестанет соответствовать истине, а значит управление будет
передано за пределы оператора цикла, в строку:

Alert(“C=”,C,” Число Фибоначчи=”,C/B,” i=”,i);

В результате на экране появится окно оператора Alert(), в котором будет напечатано
следующее:

С=317811 Число Фибоначчи=1.618 i=25

Это означает, что на 25-й итерации заданная точность достигнута, при этом максимальное
значение элемента последовательности Фибоначчи, которое было обработано, равно
317811, а сам коэффициент Фибоначчи, как и ожидалось, равен 1.618. Это сообщение
является решением поставленной задачи.

Здесь нужно заметить, что действительные числа в MQL4 вычисляются с точностью
до 15-го знака. В то же время, коэффициент Фибоначчи отражён с точностью до 3-го
знака. Это произошло потому, что свойством функции Alert() является
отображение чисел с точностью до 4-го знака, но все последние ноли при этом не
отображаются. Если бы у нас возникла необходимость отобразить на экране число Фибоначчи
с некоторой наперёд заданной точностью, то нам пришлось бы несколько изменить код,
например, так:

Alert(“C=”,C,” Число Фибоначчи=”,C/B*10000000,” i=”,i);

Особо нужно отметить следующее:

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

Зацикливание – бесконечное повторяющееся выполнение операторов, составляющих тело цикла; критическая
ситуация, возникающая в результате реализации ошибочного алгоритма.

При зацикливании программа бесконечно исполняет блок операторов, составляющих
тело цикла. Вот простой пример оператора цикла while с зацикливанием:

int i=1; // Формальн параметр (счётчик)
while (i > 0) // Заголовок оператора цикла
i++; // Увеличение значения i

В данном примере происходит накапливание (увеличение значения) переменной i на каждой
итерации. В результате условие никогда не станет ложным. Аналогичная ситуация возникает
в случае, когда в теле цикла вообще ничего не вычисляется. Например, в этом коде:

int i=1; // Формальн параметр (счётчик)
while (i > 0) // Заголовок оператора цикла
Alert(“i= “,i); // Сообщение на экран

значение переменной i в цикле не изменяется. На каждой итерации на экран будет выводиться
сообщение:

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

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

Источник

Здравствуйте, любители программирования и читатели сайта progmatem.ru. На одной из предыдущих страниц мы рассмотрели оператор цикла с параметром, поэтому пришло время изучить другие типы циклов – операторы while и repeat.

✎ Операторы цикла while и repeat используются тогда, когда заранее не известно общее количество итераций (повторений вычислений) цикла, а завершение вычислений зависит от некоего условия. Если условие ставится вначале цикла (на входе), то используется оператор While, если на выходе – Repeat.

А теперь расшифруем сказанное: сначала while, а потом ниже repeat.

Оператор цикла while имеет такую структуру:

while условие do
оператор

Это надо понимать так: пока истинно условие, стоящее между while и do, выполняется оператор после do, называемый телом цикла. Перед каждым заходом в цикл проверяется условие: если оно истинно, то выполняется оператор, если ложно, то автоматически осуществляется выход из цикла.

Читайте также:  Как долго восстанавливается цикл после кормления

Если оператор в цикле состоит из нескольких операторов, то поместить их нужно в операторные скобки begin – end (сравните оператор цикла for). Не стоит также забывать, что сами операторы разделяются оператором “точка с запятой” (но перед закрывающим END в конце ставить её не обязательно).

while условие do
begin
оператор 1;
оператор 2;
оператор 3;
………..
оператор N
end;

Продемонстрируем сказанное на примере вычисления суммы кубов всех чисел от 1 до 10. Из кода видно, что пока b ≤ 10 (строка 7), будем выполнять тело цикла, в котором на каждой итерации к сумме sum прибавляем b3. При последнем вычислении при b=10 вычислим сумму (строка 9), а ниже увеличим b на 1: b=11, поэтому следующим шагом будет выход из цикла, поскольку условие входа b ≤ 10 нарушится.

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
var
b, sum: integer;

begin
b := 1;
sum := 0; // начальная сумма
while b <= 10 do
begin
sum := sum + b*b*b;
inc(b)
end;
writeln(‘sum = ‘, sum); // выводим результат
readln
end.

Рассмотрим хорошо известный пример с разложением функции ex в ряд Тейлора:

Будем вычислять его значение с точностью, например, 0.000001 (одна миллионная), а само значение обозначим как S. как видно, первое значение ряда (суммы) равно a0=1, а для вычисления каждого последующего члена an+1 предыдущий an нужно умножить на x и разделить на n. Действительно, an+1 = xn+1/(n+1)! = xn·x/(n!·n) = an·x/n. Это и продемонстрировано в программе ниже.

Смысл таков: пока члены ряда больше 0.000001, будет выполняться тело цикла, в котором вычисляется указанная сумма. Как только член ряда a станет меньше или равен 0.000001, происходит выход из цикла, и в конце выводится результат.

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
var
a, x, S: real;
n: integer;

begin
x := 0.5;
n := 0; // начальный номер
a := 1; // начальное значение члена ряда
S := 0; // начальная сумма
while a > 0.0000001 do
begin
S := S + a;
inc(n);
a := a * x / n
end;
writeln(‘S = ‘, S:0:6);
readln
end.

Нужно избегать случая, когда условие входа в цикл всегда истинно, ибо в тогда программа зациклится. Такая ситуация называется “бесконечным циклом”.

Приведем пример.

Код Pascal

1  
2  
3  
4  
begin
while 1 < 100 do
writeln(‘Hello!’)
end.

Данная программа будет выводить приветствие “Hello!” бесконечно, то есть до тех пор, пока вы её не остановите. Происходит это потому, что условие 1 < 100 всегда истинно.

Посмотрите ещё пример с гармоническим рядом или другие программы из раздела While задачника Абрамяна.

Выведите наименьший делитель числа x, отличный от 1

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
var
x, d: integer;

begin
write(‘Введите x –> ‘);
readln(x);
d := 2; { <- минимальный делитель отличный от 1 }
while (x mod d <> 0) do inc(d);
writeln(‘d = ‘, d);
readln
end.

Напечатать минимальное число, большее 200, которое нацело делится на 17

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
var
n: integer;

begin
n := 201; { <- минимальное число большее 200 }
while (n mod 17 <> 0) do inc(n);
writeln(‘Ответ: ‘, n);
readln
end.

Оператор цикла repeat имеет такую структуру:

repeat
оператор
until условие;

Отличие оператора цикла repeat от while состоит в том, что в нем условие проверяется на выходе из цикла: если оно не выполняется, то цикл продолжается, если выполнится – сразу выход из цикла. Таким образом, пока условие истинно, программа идет на следующую итерацию, условие нарушается – выходим. Поэтому оператор repeat ещё называют оператором выхода. Ещё в операторе repeat не нужны операторные скобки begin – end для нескольких операторов:

repeat
оператор 1;
оператор 2;
оператор 3;
………..
оператор N
until условие;

Ещё одной особенностью оператора repeat – until является то, что по крайней мере один раз оператор в теле цикла выполнится, поскольку условие выхода проверяется в конце. Эта особенность приводит к тому, что любой оператор с предусловием while легко может быть преобразован в оператор с послеусловием repeat, а вот далеко не каждый оператор repeat легко записывается с помощью while.

Вычислить корень квадратный из введенного с клавиатуры числа. Запустите программу и попробуйте ввести отрицательное число: каждый раз вас будет возвращать в начало цикла, поскольку выйти из цикла можно, если ввести неотрицательное число x: x >= 0.

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
var
x: integer;

begin
repeat
readln(x)
until x >= 0; { <– выходим, если x>=0 }
writeln(‘Квадратный корень: ‘, sqrt(x):0:4);
readln
end.

Вводить с клавиатуры числа до тех пор, пока их сумма не превысит заданное наперед число. Как видим в строке 11, если сумма sum превысит число М, то выходим из цикла и выводим результат:

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
var
x, M, sum: real;

begin
write(‘Введите контрольное число –> ‘);
readln(M);
sum := 0; { <- начальное значение суммы }
repeat
readln(x); { <- вводим x }
sum := sum + x { <- к сумме прибавляем x }
until sum > M; { <== выходим, если сумма превысит M }
writeln(‘Результат: ‘, sum);
readln
end.

Вводится последовательность чисел, 0-конец последовательности. Определить, содержит ли последовательность хотя бы два равных соседних числа.

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
var
n, pre: integer;
twoEqual: boolean;

begin
twoEqual := false; { <- нету пока равных чисел }
pre := 0; { <- начальное значение предыдущего числа }
writeln(‘Введите элементы последовательности:’);
repeat
read(n);
{ Вычисление twoEqual имеет смысл, если мы ещё не встретили два
равных соседних числа (т.е. при twoEqual=false). В таком случае,
если последнее введенное число n равно предыдущему pre (pre=n),
то индикатор twoEqual станет истинным и больше не изменится: }
if not twoEqual then twoEqual := (n = pre);
pre := n { <- предыдущим pre становится n }
until n = 0; { <- выходим при вводе 0 }
writeln;
if twoEqual then writeln(‘Содержит’)
else writeln(‘Не содержит’);
readln
end.

Вводится последовательность из N целых чисел. Найти наибольшее из всех отрицательных чисел. Первый вариант решения.

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
var
N, i, a, maxNegNum: integer;

begin
i := 0; { <– Номера вводимых чисел }
N := 10; { <– Количество вводимых чисел }
{ Начальное значение максимального отрицательного числа: }
maxNegNum := -MaxInt-1;
repeat
inc(i); { <– Увеличиваем номер вводимого числа }
read(a); { <– Ввордим число }
{ Поскольку нужно найти максимальное число только среди
отрицательных элементов, то положительные или нулевые
элементы нас не интересуют: для этого достаточно добавить
условие a<0. Потом при каждой итерации будем сравнивать
введенное число а с максимальным maxNegNum: если оно(число)
больше максимального, то заменим ним максииальное: maxNegNum=а. }
if (a < 0) and (a > maxNegNum) then maxNegNum := a
until i = N; { <– Выходим из цикла, если введены все N чисел }
writeln(‘Ответ: ‘, maxNegNum); { <– Выводим результат }
readln
end.

Вводится последовательность из N целых чисел. Найти наибольшее из всех отрицательных чисел. Второй вариант решения.

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
var
N, i, a, maxNegNum: integer;

begin
i := 0; { <– Количество введенных чисел }
N := 10; { <– Количество чисел для ввода }
maxNegNum := 0;{ <– Максимальное значение }
writeln(‘Введите ‘, N, ‘ целых чисел:’);
repeat
inc(i);
write(i, ‘) ‘);
readln(a);
if (a < 0) then { <– Проверяем только отрицательные числа }
{ Когда в наборе мы нашли первое отрицательное число,
величина maxNegNum равна 0, поэтому меняем её на а – это
и будет первое ненулевое значение для maxNegNum: }
if maxNegNum = 0 then maxNegNum := a
else { В остальных случаях maxNegNum сравниваем с а и находим большее: }
if (a > maxNegNum) then maxNegNum := a
until i = N; { <– Выходим, когда введены все числа }
{ Когда переменная maxNegNum отрицательная, то это означает, что в наборе
чисел есть отрицательные – тогда выводим максимальное из них, в противном
случае сообщаем об отсутствии отрицательных чисел: }
if maxNegNum < 0 then writeln(‘Максимальное отрицательное число: ‘, maxNegNum)
else writeln(‘В последовательности нет отрицательных чисел’);
readln
end.

Вводится последовательность из N целых чисел. Найти наибольшее из всех отрицательных чисел. Третий способ решения.

Этот вариант решения – это по сути перефразировка предыдущего способа, только здесь используется дополнительная логическая переменная-индикатор bln для указания присутствия или отсутствия отрицательных чисел. Сначала bln ставим false (строка 8). Заходим в цикл, вводим числа, и как только нашли отрицательное число (строка 14), первый раз, когда ещё bln=false, (или not bln), запоминаем это число как maxNegNum, а значение логической переменной меняем: bln=true (строки 15 – 18), что означает наличие отрицательных чисел в наборе. Для остальных отрицательных элементов сравниваем введенное а и maxNegNum, и запоминаем максимальное из них как maxNegNum – оно и будет максимальным среди отрицательных (строки 19 – 20).

Код Pascal

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
var
N, i, a, maxNegNum: integer;
bln: boolean;

begin
i := 0;
N := 10;
bln := false;
writeln(‘Введите ‘, N, ‘ целых чисел:’);
repeat
inc(i);
write(i, ‘) ‘);
readln(a);
if (a < 0) then
if not bln then begin
maxNegNum := a;
bln := true
end
else
if (a > maxNegNum) then maxNegNum := a
until i = N;
if not bln then writeln(‘Нет отрицательных чисел’)
else writeln(‘Ответ: ‘, maxNegNum);
readln
end.

Это коротко об операторах цикла while и repeat. Что еще упущено выше, так это возможность каждый цикл с параметром for преобразовать в оператор с предусловием while или послеусловием repeat. Другие задачи на использование этих операторов вы найдете в разделе while по ссылке Задачник, или разделе Proc (процедуры и функции) того же раздела. Если что не понятно, то комментарии находятся ниже.

Источник