English 中文 Español Deutsch 日本語 Português
preview
От начального до среднего уровня: Операторы BREAK и CONTINUE

От начального до среднего уровня: Операторы BREAK и CONTINUE

MetaTrader 5Примеры | 3 февраля 2025, 09:49
363 0
CODE X
CODE X

Введение

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

В предыдущей статье, "От начального до среднего уровня: Операторы WHILE и DO WHILE", мы начали говорить о циклах. Поэтому первостепенным для нас является понимание материала, изложенного в предыдущей статье, чтобы мы смогли правильно понять то, о чем пойдет речь в данной статье.

Я знаю, что многие начинающие программисты испытывают большие трудности с правильным пониманием работы операторов, генерирующих циклы. Тем не менее, циклы очень важны и являются частью почти каждой программы, которую мы будем создавать на протяжении всей жизни программиста, даже если программирование это только наше ХОББИ. Понимание и умение работать с операторами, создающими циклы, очень важны для программиста.

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

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


Один из возможных ответов

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Factorial();
07.     Factorial();
08.     Factorial();
09.     Factorial();
10.     Factorial();
11. }
12. //+------------------------------------------------------------------+
13. void Factorial(void)
14. {
15.     static uchar counter = 0;
16.     static ulong value = 1;
17. 
18.     Print("Factorial of ", counter, " => ", value);    
19.     counter = counter + 1;
20.     value = value * counter;
21. }
22. //+------------------------------------------------------------------+

Код 01

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

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

В общем и целом, можно просто использовать модификацию кода 07, показанную в предыдущей статье. Это делается для получения того же результата, что и при выполнении кода 01. Как? Просто используем код 02, который показан ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     uchar counter = 0;
07. 
08.     while (counter < 5)
09.         Factorial(counter);
10. }
11. //+------------------------------------------------------------------+
12. void Factorial(uchar &counter)
13. {
14.     static ulong value = 1;
15. 
16.     Print("Factorial of ", counter, " => ", value);    
17.     counter = counter + 1;
18.     value = value * counter;
19. }
20. //+------------------------------------------------------------------+

Код 02

Это только ОДИН из способов решения проблемы. Есть и другие, более или менее сложные, но с учетом того, что уже было объяснено, это было бы хорошим решением. Если вы не понимаете, что на самом деле делает код 02, и особенно почему ему удается получить тот же результат, что и коду 01, вам лучше остановиться и вернуться к предыдущим статьям. Это необходимо для того, чтобы понять код 02. Только когда вы поймете его, вы будете готовы к продолжению этой и следующих статей.

Учитывая данное предупреждение, мы можем начать с первого оператора, который будет проанализирован в этой статье. Мы начнем с самого простого оператора, который может присутствовать в цикле. Но перед тем, как поговорить об этом, мы затронем новую тему.


Оператор RETURN

Я знаю, что многие из вас, у кого есть минимальный опыт в программировании на MQL5, скажут мне: "Но этот оператор не имеет ничего общего с циклами, он больше связан с функциями". На самом деле оператор RETURN имеет гораздо больше общего с функциями, чем с циклами. Однако, хотя этот оператор является обязательным в функции, в процедурах он необязателен. Именно из-за того, что он влияет на процедуры, в которых появляется, он может возникнуть в цикле.

Теперь обратите внимание: оператор RETURN предназначен для возврата потока выполнения вызывающей функции или процедуры. Однако, и это часто бывает трудно понять новичкам, если они находятся в главной процедуре или функции приложения, например OnInit или OnStart, в зависимости от случая, оператор RETURN при выполнении завершит приложение, заставив MetaTrader 5 удалить его с графика. Это связано с тем, что в конечном итоге любое приложение - это функция или процедура, которую выполняет операционная система. Понимая это, легче усвоить, почему оператор RETURN, установленный в OnInit или OnStart, может привести к преждевременному завершению приложения.

В случае появления RETURN в OnInit, приложение может завершиться или нет, в зависимости от того, какое значение мы возвращаем MetaTrader 5. Однако, если RETURN появится и выполнится в процедуре OnStart, то приложение будет фактически завершено.

Чтобы понять это, давайте начнем с простого примера, как показано ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
07. 
08.     return;
09. 
10.     Print(__FUNCTION__, " ", __LINE__, " This line will never be executed...");
11. }
12. //+------------------------------------------------------------------+

Код 03

Результат выполнения данного кода можно увидеть ниже:


Рисунок 01

Нам нужно понять, что оператор RETURN, расположенный в восьмой строке, находится внутри процедуры. С логической точки зрения, будучи процедурой, она НЕ ДОЛЖНА иметь никакого значения, связанного с ней. Это необходимо для того, чтобы избежать конфликтов с компилятором при создании исполняемого файла. Однако, если бы RETURN присутствовал в функции, было бы обязательно связать с ним значение. Это нужно для того, чтобы сообщить вызывающей программе, какое значение возвращает функция. Мы уже видели, как это применяется, когда говорили о переменных.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
07. 
08.     Procedure_Loop();
09. 
10.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
11. }
12. //+------------------------------------------------------------------+
13. void Procedure_Loop(void)
14. {
15.     char info = 10;
16. 
17.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
18.     
19.     while (info)
20.     {
21.         info = info - 3;
22.         
23.         Print("Loop : ", info);
24.         
25.         if (info < -5) return;
26.     }
27.     
28.     Print(__FUNCTION__, " ", __LINE__, " This line will never be executed...");
29. }
30. //+------------------------------------------------------------------+

Код 04

Код 04 при выполнении приведет к результату, показанному на рисунке ниже:


Рисунок 02

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

Из-за этой детали строка 28 никогда не будет выполнена в коде 04, так как значение info не должно достигнуть нуля до того, как тестовое выражение в IF вызовет выполнение оператора RETURN. По этой причине на изображении 02 напечатаны значения от семи до восьми с отрицательным знаком.

Очень хорошо, но почему тогда приложение не завершается в момент выполнения строки 25, позволяя при этом выполнить строку 10, что отличается от того, что произошло в коде 03? Причина этого, как раз в том, что OnStart - это начальная процедура или функция, которую вызывает MetaTrader 5, а Procedure_Loop вызывается OnStart. Поэтому, когда оператор RETURN выполняется, он возвращается к вызывающей процедуре или функции, в которой она находится, в данном случае - OnStart.

Позже расскажем более подробно о том, какую функцию вызывает MetaTrader 5. В основном MetaTrader 5 вызывает две функции: OnStart и OnInit. Только в этих случаях оператор RETURN приведет или может привести к завершению работы нашего приложения. В противном случае использование оператора RETURN не приведет к завершению работы приложения.

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


Оператор BREAK

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

Одна из самых распространенных ошибок при использовании оператора BREAK - это считать, что оператор BREAK, будучи связанным с циклом, повлияет на другой цикл или даже на другого оператора управления потоком. Так происходит из-за того, что BREAK используется не только в циклах, но и в других типах управления, которые мы рассмотрим позже. Однако пока не стоит беспокоиться об этом. Мы сосредоточимся только на его применении внутри циклов.

В этом контексте вам нужно понять кое-что, о чем многие забывают или игнорируют: оператор BREAK работает по тому же принципу, что и оператор RETURN. То есть он не «болтается» в цикле. Обычно это часть подпрограммы оператора IF. Поэтому мы можем использовать тот же код 04, что и в предыдущей теме, и вместо оператора RETURN использовать оператор BREAK. Таким образом, код выглядит так:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
07. 
08.     Procedure_Loop();
09. 
10.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
11. }
12. //+------------------------------------------------------------------+
13. void Procedure_Loop(void)
14. {
15.     char info = 10;
16. 
17.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
18.     
19.     while (info)
20.     {
21.         info = info - 3;
22.         
23.         Print("Loop : ", info);
24.         
25.         if (info < -5) break;
26.     }
27.     
28.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
29. }
30. //+------------------------------------------------------------------+

Код 05

При выполнении кода 05 результат, отображаемый в терминале MetaTrader 5, выглядит так:


Рисунок 03

Интересно! На этот раз у нас есть информация, которая до этого не была показана. В самом деле интересно, кажется, что работать с циклами довольно просто. Теперь мы готовы к работе с более сложными кодами. (СМЕХ). Спокойно, дорогой читатель, спокойно, мы только начинаем. Есть одна загвоздка (на самом деле это недопонимание), которое часто возникает у начинающих программистов в отношении этих операторов. Поэтому я прошу вас потренироваться в использовании небольших вариаций приведенных здесь кодов.

Первое недоразумение связано с использованием оператора BREAK во вложенных циклах. Да, точно так же, как мы можем вложить несколько операторов IF, мы можем вложить несколько циклов друг в друга. Это очень часто встречается при работе с многомерными матрицами, когда мы не хотим вычислять индексы вручную. Но давайте рассмотрим простой пример вложенного цикла, чтобы понять, как ведет себя оператор BREAK в этом случае. Простой пример показан ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
07. 
08.     Procedure_Loop();
09. 
10.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
11. }
12. //+------------------------------------------------------------------+
13. void Procedure_Loop(void)
14. {
15.     char    info = 10,
16.             limit = 5;
17. 
18.     do
19.     {
20.         Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")  Limit (", limit, ")");
21. 
22.         while (info > limit)
23.         {
24.             if (limit < 0) break;
25. 
26.             info = info - 3;
27. 
28.             Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")  Limit (", limit, ")");
29.         }
30.         info = info + limit;
31.         limit = limit - 2;
32. 
33.         Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")  Limit (", limit, ")");
34. 
35.     }while (info > 0);
36. 
37.     Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")  Limit (", limit, ")");
38. }
39. //+------------------------------------------------------------------+

Код 06

Хотя это может показаться сложным из-за количества напечатанных сообщений, код 06 на самом деле довольно прост и служит отличной иллюстрацией поведения оператора BREAK внутри вложенных циклов. Когда вы запустите код 06, результат действительно будет интересным. Его можно увидеть на изображении ниже:


Рисунок 04

А теперь обратите внимание на кое-что интересное. Внутри цикла, который начинается в строке 18, у нас есть еще один цикл, который начинается в строке 22. В этом новом цикле мы находим проверку в строке 24. Вот тут-то всё и становится интересным. Это необходимо для того, чтобы в дальнейшем вы могли поэкспериментировать с заменой оператора BREAK на оператор RETURN и посмотреть, что произойдет в коде. Но давайте сосредоточимся на том, что происходит при использовании оператора BREAK.

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

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

Возвращаясь к коду, можно заметить, что в рисунке 04 есть только несколько пунктов, где на самом деле выводится строка 28, хотя выражение цикла в строке 22 указывает на то, что их должно быть больше. Такое меньшее количество выполнений строки 28 объясняется тем, что оператор BREAK приводит к завершению внутреннего цикла до того, как выражение укажет, что он должен завершиться. Однако факт завершения внутреннего цикла, не означает, что в данном случае, когда используется оператор BREAK, внешний цикл также завершен. Поэтому важно поэкспериментировать с использованием оператора RETURN и здесь тоже, чтобы проверить, что произойдет, поскольку есть разница в конечном результате, который мы увидим в терминале.

В принципе, если следить за ходом выполнения, то станет понятно, что это очень просто. Поскольку поток выполнения оператора WHILE и пары DO WHILE, а также оператора IF уже были показаны в предыдущих статьях, мы сможем нарисовать их здесь. Однако пока не было показано, как будет выглядеть поток в случае встречи с оператором BREAK. Но это можно увидеть ниже:


Рисунок 05

Здесь LOOP обозначает любой оператор, позволяющий создавать циклы. Однако оператор BREAK отображается так, как будто он находится в коде изолированно. На этом этапе нам необходимо сделать уточнение. Это связано с тем, что если бы мы показали весь набор, необходимый для правильного выполнения оператора BREAK, то туда же пришлось бы включить и поток оператора IF. Когда тестовое выражение оператора IF будет истинным, появится оператор BREAK, и процесс пойдет так, как показано на рисунке. Также обратите внимание, что оператор BREAK может появиться до, во время или после того, как подпрограмма цикла была выполнена. Как правило, рано или поздно так и происходит. Когда он находится посередине, это означает, что мы разделили подпрограмму на две части. Но для упрощения процесса выполнения мы не включаем этот тип операций.

Однако я думаю, что вы правильно поняли концепцию. Но запомните кое-что важное: оператор BREAK также связан с другим типом оператора управления потоком. Однако на данный момент мы оставим всё как есть и будем двигаться вперед шаг за шагом.

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


Оператор CONTINUE

Оператор CONTINUE очень опасен при использовании внутри цикла, особенно в цикле, где используется оператор WHILE или пара DO WHILE. Это связано с тем, что очень часто возникают бесконечные циклы при появлении этого оператора в цикле. Однако даже в цикле FOR, который может показаться более устойчивым к превращению в бесконечный цикл, такое тоже возможно. Достаточно отвлечься совсем немного на этапе выполнения при котором используется оператор CONTINUE, чтобы создать бесконечный цикл. Поэтому будьте внимательны всякий раз, когда вам придется использовать или встречать этот оператор CONTINUE в части кода. Данный оператор всегда будет связан с каким-либо циклом, но будьте особенно внимательны при работе с циклами WHILE и DO WHILE.

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


Рисунок 06

Здесь действует то же предупреждение, что и для других операторов, BREAK и RETURN. Однако в данном конкретном случае совет использовать эти операторы, либо BREAK, либо RETURN, как подпрограмму внутри оператора IF, становится еще более актуальным. Это происходит так, потому что, как видно на рисунке 06, ничего не будет выполнено внутри того, что было бы подпрограммой цикла. И, в отличие от других операторов, которые приводят к завершению цикла, оператор CONTINUE заставляет поток управления немедленно перейти к началу цикла.

Поэтому, если к событию не приложено условие, которое делает выражение цикла ложным, цикл неизбежно перейдет в бесконечный цикл. Поэтому при использовании оператора CONTINUE следует соблюдать особую осторожность. Во многих языках программирования оператор GOTO не используется и не применяется именно из-за такой потенциальной проблемы. Однако при правильном использовании и применении, оператор CONTINUE действительно очень полезен в ряде ситуаций. Но будьте внимательны при его использовании.

Чтобы продемонстрировать потенциальную опасность небрежного выполнения этого оператора, давайте создадим код, который будет намеренно закрываться только по какому-то внешнему событию. Для этого мы используем следующий код:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
07. 
08.     Procedure_Loop();
09. 
10.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
11. }
12. //+------------------------------------------------------------------+
13. void Procedure_Loop(void)
14. {
15.     char info = 10;
16. 
17.     Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
18.     
19.     while ((info) && (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) && (!IsStopped()))
20.     {
21.         if (info) continue;
22. 
23.         info = info - 3;
24.                
25.         Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
26. 
27.         if (info < -5) return;
28.     }
29.     Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
30. }
31. //+------------------------------------------------------------------+

Код 07

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


Анимация 01

А теперь внимание. Это произошло только из-за того, что мы осуществили выход из бесконечного цикла, который мы создаем. Если бы мы не осуществили данную реализацию, то мы застряли бы там навсегда, или до тех пор, пока кто-нибудь не решил бы принудительно удалить приложение, заставив его завершиться из MetaTrader 5. Ни в какой другой ситуации он не был бы закрыт. Учитывая это предупреждение, давайте посмотрим, что происходит в коде 07.

Поскольку данный код является разновидностью других кодов, которые мы использовали в этой статье, мы можем сосредоточиться на действительно важных частях. Первое - это то, что переменная в строке 15 объявлена таким образом, что позволит запустить цикл WHILE, как видно в строке 19, где начинается цикл. Следующее, что мы должны учитывать, - это три условия завершения цикла. Первое - переменная info становится ложной. Второе - нажатие клавиши ESC. И третье - вы просите закончить скрипт. В случае с третьим условием, если бы оно не было добавлено, то при попытке закрыть скрипт, MetaTrader 5 пытался бы сделать это до тех пор, пока не добился бы успеха. В случае успеха в терминале появится еще одно сообщение, как можно увидеть на изображении ниже.


Рисунок 07

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

Но здесь речь идет именно о строке 21. Обратите внимание, что несмотря на наличие оператора IF, проверяющего, будем ли мы менять что-либо в цикле, тот факт, что подпрограмма внутри оператора IF является всего лишь оператором CONTINUE, заставляет весь цикл войти в бесконечный цикл. Это происходит, потому что никакая другая часть подпрограммы оператора WHILE не будет выполнена.

Однако небольшие вариации этих же кодов могут сделать их менее рискованными, даже если строки с 23 по 27 никогда не будут выполнены. В качестве примера можно изменить код 07 на что-то похожее на показанное в коде 08:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
07. 
08.     Procedure_Loop();
09. 
10.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
11. }
12. //+------------------------------------------------------------------+
13. void Procedure_Loop(void)
14. {
15.     char info = 10;
16. 
17.     Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
18.     
19.     while (((info = info - 3) > 0) && (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) && (!IsStopped()))
20.     {
21.         if (info) continue;
22. 
23.         info = info - 3;
24.                
25.         Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
26. 
27.         if (info < -5) return;
28.     }
29.     Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
30. }
31. //+------------------------------------------------------------------+

Код 08

В этом случае при выполнении кода 08 мы увидим на терминале информацию, отличающуюся от той, которая показана в анимации 01. Это можно увидеть ниже:


Рисунок 08

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

                   .
                   .
                   .
12//+------------------------------------------------------------------+
13void Procedure_Loop(void)
14{
15    char info = 10;
16
17    Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
18    
19    while (((info = info - 3) > 0) && (!TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)) && (!IsStopped()));
20
21    Print(__FUNCTION__, " 29 : Info (", info, ")");
22}
23//+------------------------------------------------------------------+

Фрагмент 01

Это происходит именно из-за присутствия оператора CONTINUE в строке 21, внутри оператора IF, который заставляет его выполняться каждый раз внутри цикла. Что ж, это было интересно. А что, если вместо оператора WHILE мы по какой-то причине используем оператор DO WHILE? Что может произойти? В этом случае, нас ждет довольно интересный сценарий, который многих заинтригует, ведь в зависимости от того, как мы реализуем свой код, он может вести себя довольно сложно.

Итак, давайте попробуем очень простую версию, цель которой - показать, что происходит, когда мы используем оператор CONTINUE в цикле DO WHILE. Этот пример показан в коде 09:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
07. 
08.     Procedure_Loop();
09. 
10.     Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway...");
11. }
12. //+------------------------------------------------------------------+
13. void Procedure_Loop(void)
14. {
15.     char info = 10;
16.     ulong counter = 0;
17. 
18.     Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
19.     
20.     do
21.     {
22.         Sleep(500);
23. 
24.         Print(__FUNCTION__, "  ", __LINE__, " : Counter (", counter = counter + 1, ")");
25. 
26.         if (info) continue;
27.               
28.         Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
29. 
30.     }while ((info = info - 2) > 0);
31. 
32.     Print(__FUNCTION__, "  ", __LINE__, " : Info (", info, ")");
33. }
34. //+------------------------------------------------------------------+

Код 09

Хотя вам, вероятно, кажется, что здесь создается бесконечный цикл, это не произойдет благодаря одной детали, которую мы уже показали в предыдущих кодах данной темы. Однако многие программисты без должного опыта по работе с циклом DO WHILE, часто представляют себе, что после выполнения строки 26 цикл вернется к строке 20. Но такой тип мышления возникает из-за незнания порядка выполнения оператора DO WHILE. В предыдущей статье мы рассмотрели, каким будет этот поток выполнения. Поэтому здесь не будем вдаваться в подробности. Но один факт может быть проверен, когда вы изучаете этот код: строка 28 никогда не будет выполнена в рамках того, что показано в коде 09. Это происходит именно из-за строки 26 и из-за того, как работает поток выполнения в операторе DO WHILE.

Чтобы проверить это, вместо изображения я решил использовать анимацию. Тогда, с моей точки зрения, обучение становится гораздо более понятным, и вы сможете начать размышлять о том, почему код 09 работает именно так, как он работает. Соответствующая анимация показана ниже: Обратите на это внимание и постарайтесь понять, как и почему всё происходит именно так.


Анимация 02


Заключительные идеи

В данной статье я постарался максимально просто объяснить, как работать с операторами RETURN, BREAK и CONTINUE, когда мы используем их внутри цикла. Хотя всё содержание статьи было посвящено использованию циклов WHILE и пары DO WHILE, то, что было продемонстрировано в этой статье, применимо и к оператору FOR, хотя это еще не было объяснено.

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

Поэтому используйте по максимуму коды, которые будут доступны в приложении. Изучайте материалы внимательно, пытаясь понять, как три оператора, рассмотренные в этой статье, влияют на поток кода во время выполнения наших приложений. Без лишних слов, мы прощаемся до следующей статьи. До скорой встречи!


Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15376

Прикрепленные файлы |
Anexo.zip (2.72 KB)
Диалектический поиск — Dialectic Search (DA) Диалектический поиск — Dialectic Search (DA)
Представляем Диалектический Алгоритм (DA) — новый метод глобальной оптимизации, вдохновленный философской концепцией диалектики. Алгоритм использует уникальное разделение популяции на спекулятивных и практических мыслителей. Тестирование показывает впечатляющую производительность до 98% в задачах малой размерности и общую эффективность 57.95%. Статья объясняет эти показатели и представляет детальное описание алгоритма и результаты экспериментов на различных типах функций.
Интеграция MQL5 с пакетами обработки данных (Часть 1): Расширенный анализ данных и статистическая обработка Интеграция MQL5 с пакетами обработки данных (Часть 1): Расширенный анализ данных и статистическая обработка
Интеграция обеспечивает бесперебойный рабочий процесс, при котором необработанные финансовые данные из MQL5 можно импортировать в пакеты обработки данных, такие как Jupyter Lab, для расширенного анализа, включая статистическое тестирование.
От начального до среднего уровня: Директива Include От начального до среднего уровня: Директива Include
В сегодняшней статье мы поговорим о директиве компиляции, широко используемой в различных кодах, которые можно найти в MQL5. Хотя данную директива будет объяснена здесь довольно поверхностно, важно, чтобы вы начали понимать, как ее использовать, поскольку вскоре она станет незаменимой при переходе на более высокий уровень программирования. Представленные здесь материалы предназначены только для обучения. Ни в коем случае не рассматривайте это приложение как окончательное, цели которого будут иные, кроме изучения представленных концепций.
Добавляем пользовательскую LLM в торгового робота (Часть 5): Разработка и тестирование торговой стратегии с помощью LLM (I) - Тонкая настройка Добавляем пользовательскую LLM в торгового робота (Часть 5): Разработка и тестирование торговой стратегии с помощью LLM (I) - Тонкая настройка
Языковые модели (LLM) являются важной частью быстро развивающегося искусственного интеллекта, поэтому нам следует подумать о том, как интегрировать мощные LLM в нашу алгоритмическую торговлю. Большинству людей сложно настроить эти модели в соответствии со своими потребностями, развернуть их локально, а затем применить к алгоритмической торговле. В этой серии статей будет рассмотрен пошаговый подход к достижению этой цели.