
От начального до среднего уровня: Массив (III)
Введение
В предыдущей статье От начального до среднего уровня: Массив (II) я объяснил основы использования динамических и статических массивов, разницу между ними и основные меры предосторожности, которые следует учитывать при использовании массивов в наших приложениях.
Сейчас мы продолжим разбирать тему массивов. И для понимания этой статьи, необходимо хорошенько усвоить содержание предыдущей. Кроме того, безусловно, необходимо понимать, как работают передача по значению и передача по ссылке. Все эти темы подробно разбирались нами в предыдущих статьях. Если у вас есть какие-либо вопросы, или вы еще не знакомы с этими концепциями, поскольку пока являетесь новичком в программировании, я предлагаю вам прочитать предыдущие материалы серии, прежде чем продолжать. Тогда вы сможете следить за тем, что здесь будет объяснено.
Как и всегда, мы начнем новую тему, чтобы продолжить наши исследования и обучение.
Использование массивов в функциях и процедурах
Одна из тем, которая пугает многих новичков, — это использование массивов как способа передачи значений между функциями и процедурами. Здесь я буду предельно откровенен относительно одного момента, связанного с этой концепцией. Способ, которым MQL5 обрабатывает подобные вещи, на порядок проще, чем в других языках программирования.
Такие языки, как C и C++, чрезвычайно сложны для решения этого типа задач. Это связано с тем, что в C и C++ НЕТ понятия массивов в том виде, как это реализовано в MQL5. Вместо этого мы используем другую структуру, называемую указателем. А проблема указателей, которая делает их такими сложными и трудными для освоения, заключается в том, что в некоторых случаях можно использовать косвенные ссылки. Это, хоть и дает больше возможностей, также значительно усложняет понимание кода для программиста. Особенно если у последнего небольшой опыт, и он еще не привык к работе с указателями.
В MQL5 же все гораздо проще для усвоения и понимания. Начиная с того, что:
Любой массив ВСЕГДА передается по ссылке — будь то функция или процедура.
В MQL5 не существует передачи по значению, когда массив используется в качестве аргумента функции или процедуры.
Хорошо, но теперь вы можете задуматься: если все аргументы массива всегда передаются по ссылке, будь то функция или процедура, не делает ли это код небезопасным? На самом деле нет, мой дорогой читатель. Как бы невероятно это ни звучало, использование массивов в MQL5 гораздо безопаснее, чем использование других типов переменных.
И скажу больше: работать с массивами в MQL5 намного проще, чем применять любой другой подход, встречающийся в других языках программирования. Это связано с тем, что в MQL5 вы полностью контролируете, что может или не может произойти с массивом, даже несмотря на то, что он всегда передается по ссылке в функцию или процедуру.
Такой уровень безопасности и надежности гарантируется именно большей тщательностью со стороны программиста. Когда я объяснял, как можно использовать передачу по ссылке или передачу по значению, я показал преимущества и недостатки каждого подхода. Но здесь все гораздо проще, поскольку само объявление, как функции, так и процедуры, делает это предельно ясным и очевидным.
Есть еще один момент, который следует учитывать. Он касается того факта, что:
Всякий массив, объявленный как параметр функции или процедуры, должен ВСЕГДА быть динамическим.
Применяя эти два принципа, вы сможете реализовать любую логику в MQL5, связанную с использованием массивов. Часто бывает практичнее и целесообразнее выполнять вычисления внутри функций или процедур, чем встраивать их в код inline.
Примечание: для тех, кто не знаком с термином «inline-код», поясню: он относится к ситуации, когда программист не использует функции или процедуры в своем приложении. Он просто пишет одну процедуру за другой, как если бы следовал пошаговому кулинарному рецепту. Хотя такой подход встречается редко, он тоже может быть эффективным. Основной характеристикой этого типа кода является полное отсутствие функций или процедур в реализации.
Хорошо, но давайте вернемся к нашему вопросу: как использовать массивы в качестве параметров в функциях и процедурах. Несмотря на кажущуюся простоту темы, не стоит думать, что здесь все легко и безобидно, и не существует риска допустить ошибку при программировании.
На самом деле есть некоторые нюансы, которые становятся понятны только тогда, когда применяешь все это на практике. В теории все очень красиво и великолепно — нужно лишь усвоить некоторые основные понятия. Но на практике все зачастую усложняется, и может хорошенько подпортить жизнь прежде, чем нам удается с этим справиться. Так что давайте переходить к тому, как все работает на практике. И начнем с кода, показанного ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. const char Infos[] = {2, 3, 3, 5, 8}; 07. 08. Print("Based on the values:"); 09. ArrayPrint(Infos); 10. PrintFormat("There are %d possibilities.", Possibilities(Infos)); 11. } 12. //+------------------------------------------------------------------+ 13. ushort Possibilities(const uchar &arg[]) 14. { 15. ushort info = 0; 16. 17. for (uchar c = 0; c < arg.Size(); c++) 18. info = (c ? info * arg[c] : arg[c]); 19. 20. return info; 21. } 22. //+------------------------------------------------------------------+
Код 01
При запуске этого кода 01, вы увидите то, что показано на изображении ниже.
Рисунок 01
То, что вы видите на рисунке 01, могло быть получено несколькими разными способами. Способ, реализованный с использованием кода 01, применен исключительно в учебных целях. Здесь мне важно объяснить, что именно происходит, и показать, как мы можем передавать массив внутри функции или процедуры.
Итак, давайте разберемся в происходящем. Напоминаю, что мы сосредоточимся только на тех моментах, которые, по моему мнению, не были объяснены ни в одной из предыдущих статей. Давайте, перейдем к ключевым моментам, начиная с шестой строки. Хотя мы используем статический массив, это делается только для упрощения кода. Мы могли бы использовать и динамический массив, и это ничуть не изменило бы происходящее. Цель — передать значения в функцию в тринадцатой строке. Таким образом, все, что происходит до строки 10 в коде 01, не влияет на передачу массива. Конечно, при условии, что функция была объявлена так, как показано в строке тринадцать.
А теперь, дорогой читатель, обратите внимание на то, что будет объяснено дальше. Как мы видели в предыдущих статьях, когда массив объявляется как константа, он ОБЯЗАТЕЛЬНО должен быть инициализирован прямо в момент объявления. Это делается для того, чтобы избежать ошибок при компиляции. Но посмотрите внимательно на эту тринадцатую строку. Вы увидите, что здесь мы объявляем массив как константу. Однако, несмотря на это, компилятор создает исполняемый файл.
Почему в этом случае мы можем так сделать, но если попробуем то же самое в шестой строке, это не сработает? Причина в том, что, в отличие от того, что происходит в строке шесть, здесь массив объявляется не как переменная, а как параметр. Как упоминалось в начале этой темы, каждый массив должен передаваться по ссылке. Именно это здесь и происходит.
Возможно, некоторые из вас, кто только начинает программировать, совершенно ошибочно полагают, что массив, объявленный в тринадцатой строке, НЕ ОБЪЯВЛЕН как константа из-за массива в строке шесть. Это не так, и одно с другим не связано. Вы вполне можете объявить и инициализировать массив в строке шесть, как полностью динамический. Однако, это никак не отменяет то, что объявлено в строке тринадцать, поскольку это совершенно разные вещи.
В коде 01 есть еще одна деталь, которая, по моему мнению, заслуживает того, чтобы ее здесь еще раз подчеркнуть, хотя об этом уже ранее упоминалось. Вопрос связан со значением, используемым для проверки в строке 17. Здесь мы используем arg.Size(). Однако это допустимо и вполне корректно, поскольку результат будет таким же, что и при использовании функции ArraySize. В любом случае, проверка выполнялась бы одинаково при любом из этих подходов. Я предлагаю вам в качестве домашнего задания поэкспериментировать с функцией ArraySize вместо arg.Size(), чтобы лучше понять, как код реализуется на практике.
Отлично, думаю, что это первое знакомство оказалось относительно приятным и весьма полезным, и дало вам общее представление о том, как следует реализовать код для передачи массива в функцию или процедуру. Способ работы и объявления массива в качестве параметра не изменится совершенно. Только вот цель может быть другой.
Это была самая простая часть, теперь можно перейти к чему-то посложнее. Однако, чтобы вы могли спокойно изучать каждую тему, давайте более сложное разбирать в новом разделе.
Удаленное изменение массива
Одной из причин, по которой использование массивов в функциях и процедурах может быть затруднено, как раз и является возможность удаленного изменения их содержимого. То есть, вы передаете массив в функцию или процедуру, и он изменяется. Подобные вещи могут привести к возникновению довольно запутанных и сложно решаемых проблем. Но поскольку у нас есть такая возможность, и, во многих случаях, мы будем это делать, крайне важно, чтобы вы хорошо понимали, что происходит. Но прежде всего, важно разобраться, почему это происходит.
В отличие от дискретных значений, таких как char, int, long, double и им подобных, языки программирования обычно НЕ позволяют возвращать массивы, за исключением особых случаев, когда сам язык каким-либо образом реализует эту функцию. Как, например, это происходит со строками в MQL5.
В предыдущих статьях было показано, что строка — это по сути специальный массив. Таким образом, это один из немногих случаев, когда мы можем вернуть массив из функции в MQL5. Это уже избавляет нас от риска и возможных проблем, которые могли бы возникнуть в некоторых реализациях, которые нам иногда приходится делать в случаях, когда действительно есть необходимость вернуть измененный массив.
Но прежде, чем говорить об этом подробнее, так как это действительно сложно для понимания на реальных примерах кода, нам нужно понять, как другие языки справляются с этой проблемой. В случае с C и C++, это просто оставлено на усмотрение программиста. Эти два языка предусматривают возможность модифицировать массив и возвращать полностью новый массив. Но не обманывайтесь: из-за того, что в C и C++ массивы не обрабатываются так, как в MQL5, вы не становитесь менее подвержены ошибкам. Напротив, вы еще более подвержены ошибкам, к тому же, трудноустранимым. Именно поэтому C и C++ так сложны для усвоения.
Другие языки, такие как Python и JavaScript, просто игнорируют существование обычных типов данных, создавая свои собственные методы, чтобы мы могли возвращать массивы и модифицировать их. Это встречается гораздо реже, чем в других языках, поэтому факторизация некоторых типов данных несколько упрощается. Однако, целью нашего обсуждения является MQL5.
Итак, начнем с чего-то максимально простого. Для этого мы будем использовать код 01, в котором изменим одну маленькую деталь. Так мы получим то, что показано ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[] = {2, 3, 3, 5, 8}; 07. 08. Print("Based on the values:"); 09. ArrayPrint(Infos); 10. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 11. ArrayPrint(Infos); 12. } 13. //+------------------------------------------------------------------+ 14. ushort Possibilities(uchar &arg[]) 15. { 16. ushort info = 0; 17. 18. for (uchar c = 0; c < arg.Size(); c++) 19. info = (c ? info * arg[c] : arg[c]); 20. 21. arg[0] += 2; 22. 23. return info; 24. } 25. //+------------------------------------------------------------------+
Код 02
Запустив код 02, мы получим следующий результат, показанный ниже.
Рисунок 02
Подчеркну, что на этом рисунке 02 я отмечаю два значения. Специально для того, чтобы привлечь ваше внимание к происходящему.
Обратите внимание, что разница между этим кодом и кодом 01 как раз и заключается в том, что теперь строка объявления параметра, принимаемого функцией в строке 14, БОЛЬШЕ НЕ ЯВЛЯЕТСЯ КОНСТАНТНОЙ. В результате, передача по ссылке, которая всегда происходит с массивами, позволяет нам использовать строку 21, где мы изменяем значение одного из элементов массива, объявленного в строке шесть.
Поскольку мы работаем в дидактическом ключе и концентрируемся на объяснении определенных концепций, применимых в MQL5, это изменение довольно легко заметить. Однако, в реальном коде это изменение может находиться столь глубоко и быть так переплетено с остальной частью кода, что может возникнуть желание сдаться и переписать все с нуля, из-за того уровня сложности, который это изменение может создать.
Поэтому, мой дорогой читатель, если ты только начинаешь изучать программирование, или у тебя пока мало опыта, обязательно попрактикуйся в применении концепций, которые мы здесь рассматриваем. Только практика способна дать необходимый опыт для решения проблем, с которыми, несомненно, придется столкнуться.
Итак, этот код 02 действительно является самым простым из возможных — он почти тривиален. Но все усложняется по мере того, как мы продолжаем углубляться в тему. Поэтому давайте рассмотрим другой пример, на этот раз немного более сложный. Он приведен ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[]; 07. 08. ArrayResize(Infos, 7); 09. ZeroMemory(Infos); 10. 11. Infos[0] = 2; 12. Infos[1] = 3; 13. Infos[2] = 3; 14. Infos[3] = 5; 15. Infos[4] = 8; 16. 17. Print("Based on the values:"); 18. ArrayPrint(Infos); 19. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 20. ArrayPrint(Infos); 21. 22. ArrayFree(Infos); 23. } 24. //+------------------------------------------------------------------+ 25. ushort Possibilities(uchar &arg[]) 26. { 27. ushort info = 0; 28. 29. for (uchar c = 0; c < arg.Size(); c++) 30. info = (c ? (arg[c] ? info * arg[c] : info) : arg[c]); 31. 32. arg[arg.Size() - 1] = 9; 33. 34. return info; 35. } 36. //+------------------------------------------------------------------+
Код 03
Ну вот, здесь, в коде 03, мы имеем дело с чем-то немного более сложным. Но оно не стало таковым из-за строки 30 — эта строка все также очень проста для понимания.
Код 03 стал более сложным именно потому, что в строке 6 мы объявляем чисто динамический массив, и вот здесь все начинает действительно усложняться. Хотя этот код вполне может быть полностью понят начинающим программистом, изучающим материалы, представленные в статьях — шаг за шагом. И если между этими шагами он практикует все то, что демонстрируется.
Давайте посмотрим на результат выполнения этого кода, он показан на рисунке ниже.
Рисунок 03
И снова я выделяю определенную информацию, присутствующую на изображении. Она крайне важна для правильного понимания.
Давайте разберемся, что здесь произошло. Так как в строке восемь мы указываем, что массив будет содержать семь элементов, и в строках 11-15 мы инициализируем некоторые из этих элементов, можно увидеть, что в строке 18 содержимое массива включает в себя несколько нулей. Это нормально, и объясняется тем, что в строке девять мы освобождаем память, в которой был размещен массив.
Именно из-за этих нулей потребовалось добавить дополнительный тернарный оператор в строке 30. В противном случае, результат функции был бы равен нулю. Но в данном случае, это не главное. Интерес представляет конкретно строка 32. Обратите внимание, здесь мы указываем, что присваиваем значение определенному элементу, в данном случае, последнему в массиве.
Но зачем я это делаю и демонстрирую именно в такой последовательности? Причина в том, что в MQL5 есть способ работы с массивами, который позволяет нам делать то, что в противном случае было бы невозможным. И все это — даже без понимания некоторых концепций, связанных с использованием массивов. В то же время, сами того не осознавая, вы начнете замечать, как здесь происходят некоторые вещи, которые сделают многое из того, что будет показано далее, гораздо более естественным и понятным.
Отлично. Теперь у нас уже есть хорошая доза сложности, добавленная в систему. Однако, мы все еще можем усложнить процесс, хотя лично я бы на этом закончил статью, чтобы вы, дорогие читатели, могли попрактиковаться в том, что было показано до этого момента. А надо сказать, что материала уже достаточно, и он содержит много вещей, довольно непростых для понимания. Но давайте сделаем последнее усилие, чтобы посмотреть на кое-что интересное, напрямую связанное с тем, что мы сейчас рассматриваем. Но так как это уже заметно сложнее — перейдем к новой теме.
Удаленная инициализация
В этот момент я попрошу вас, мой дорогой и уважаемый читатель и друг, остановиться и изучить то, что уже было представлено. И только после того, как вы полностью поймете материал, и в вашей голове не останется никаких сомнений или внутренних конфликтов в отношении предыдущих примеров кода, тогда можно переходить к тому, что будет показано в этом разделе. Потому что ситуация становится слишком запутанной, чтобы с ней можно было справиться без должной подготовки.
До настоящего момента мы узнали, что можно передавать данные с помощью массивов. Мы также увидели, что можно изменять данные в массиве внутри другой функции или процедуры. Подобные вещи требуют особой внимательности, особенно если хочется создать более сложное приложение. Затем мы увидели, что можем работать с массивом и уточнять, в каком именно месте массива мы хотим изменить значение. Теперь пришло время сделать кое-что еще, и это показано в коде ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[]; 07. 08. InitArray(Infos); 09. 10. Print("Based on the values:"); 11. ArrayPrint(Infos); 12. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 13. ArrayPrint(Infos); 14. 15. ArrayFree(Infos); 16. } 17. //+------------------------------------------------------------------+ 18. void InitArray(uchar &arg[]) 19. { 20. ArrayResize(arg, 7); 21. ZeroMemory(arg); 22. 23. arg[0] = 2; 24. arg[1] = 3; 25. arg[2] = 3; 26. arg[3] = 5; 27. arg[4] = 8; 28. } 29. //+------------------------------------------------------------------+ 30. ushort Possibilities(uchar &arg[]) 31. { 32. ushort info = 0; 33. 34. for (uchar c = 0; c < arg.Size(); c++) 35. info = (c ? (arg[c] ? info * arg[c] : info) : arg[c]); 36. 37. arg[arg.Size() - 1] = 9; 38. 39. return info; 40. } 41. //+------------------------------------------------------------------+
Код 04
И вот, дорогой читатель, это кульминация того, что будет показано в этой статье. Однако, не стоит обманываться кажущейся простотой кода 04, поскольку, хотя результат и показан ниже, он гораздо сложнее, чем кажется.
Рисунок 04
Видно, что результат тот же, что и на рисунке 03 — идентичен во всех отношениях. Тем не менее, в текущем коде 04 я показываю нечто, что на практике может стать очень и очень сложным, но если будет реализовано правильно и должным образом, может заменить собой множество вещей — особенно тех, которые, по утверждениям некоторых опытных программистов, НЕВОЗМОЖНО реализовать в MQL5. Все это в чистом виде и без использования каких-либо других ресурсов, которые не доступны в MQL5.
Я вносил эти изменения постепенно, чтобы вы могли следить за тем, что и как делается. Но хотя, глядя на код 04, можно подумать, что это просто модификация того, что делается в коде 03, в свой основе этот код 04 показывает, что мы можем сделать что-то, что было бы невозможно без хорошего усвоения определенных концепций.
Одна из таких концепций — и именно ее мы исследуем здесь, в коде 04 — заключается в том, что любой массив передается по ссылке. И когда мы применим это, используя принципы, рассмотренные в предыдущих статьях, мы действительно сможем сделать то, что многие посчитают невозможным. А именно: инициализировать или модифицировать массивы вне места их объявления.
Мы можем довести это до несколько более экстремального случая, не увеличивая при этом продемонстрированный до сих пор уровень сложности. Чтобы объяснить это, мы снова изменим код 04, очень тонким и почти незаметным образом, так, как показано ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. char Infos[]; 07. 08. if (ArraySize(Infos)) 09. { 10. Print("Based on the values:"); 11. ArrayPrint(Infos); 12. }else 13. Print("Array has not been initialized yet."); 14. PrintFormat("There are %d possibilities.\nValue after call:", Possibilities(Infos)); 15. ArrayPrint(Infos); 16. 17. ArrayFree(Infos); 18. } 19. //+------------------------------------------------------------------+ 20. void InitArray(uchar &arg[]) 21. { 22. const char init [] = {2, 3, 3, 5, 8}; 23. 24. ArrayCopy(arg, init); 25. } 26. //+------------------------------------------------------------------+ 27. ushort Possibilities(uchar &arg[]) 28. { 29. ushort info = 0; 30. 31. InitArray(arg); 32. 33. for (uchar c = 0; c < arg.Size(); c++) 34. info = (c ? (arg[c] ? info * arg[c] : info) : arg[c]); 35. 36. ArrayResize(arg, arg.Size() + 1); 37. arg[arg.Size() - 1] = 9; 38. 39. return info; 40. } 41. //+------------------------------------------------------------------+
Код 05
При запуске приведенного выше кода 05, вы увидите то, что показано на изображении ниже.
Рисунок 05
Теперь вы, вероятно, задаетесь вопросом: «Как это возможно? Это же вообще не имеет никакого смысла». Но здесь я всего лишь немного играю с тем же кодом, который мы рассматриваем с самого начала этой статьи, дорогой читатель. Здесь нет никаких причин для паники или отчаяния.
Разница в том, что, в отличие от того, что делает большинство людей, здесь мы доводим до предела концепции, которые отлично можно использовать в MQL5, хотя в этих последних кодах мы заходим немного дальше, чем многие могут себе представить. Это происходит потому, что они не используют идею, лежащую в основе реализации, а просто копируют и вставляют код или его фрагменты, фактически не понимая, почему этот код работает. А моя цель – не в этом. Я хочу, дорогой читатель, чтобы вы на самом деле разобрались в изложенных здесь концепциях. Если вам удастся этого достичь, значит, имело смысл писать эти статьи и демонстрировать все эти вещи.
Хорошо. Поскольку я пока не хочу, особенно в рамках текущей статьи, углубляться в еще одну тему, которую уже можно было бы рассмотреть на основе последних двух примеров, я посвящу оставшуюся часть материала подробному объяснению одного из этих кодов. В данном случае, мы сосредоточимся на коде 05, поскольку код 04 немного проще.
Итак, давайте посмотрим, как работает этот код 05. Во-первых, в строке шесть у нас есть объявление полностью динамического массива, который должен быть в какой-то момент выделен, чтобы память можно было использовать в качестве хранилища информации.
Предположим, что мы не знаем, был инициализирован массив в строке шесть или нет. Мы используем строку восемь, чтобы проверить, существует ли хотя бы один элемент в массиве. Напоминаю: это чисто динамический массив. Если вы инициализируете его непосредственно в строке шесть, строка восемь его обнаружит. Даже если он будет инициализирован между строкой шесть и строкой восемь, строка восемь также его обнаружит.
Однако, если инициализация выполняется конкретно в шестой строке, где объявлен массив, то здесь возникнут другие проблемы. Попробуйте сделать это, чтобы понять, какого типа проблемы появятся. Таким образом вы попрактикуетесь и приобретете опыт работы с массивами.
В любом случае, если в массиве есть хотя бы один элемент, будут выполнены строки 10 и 11, показывая, что элементы в массиве существуют. Если же он не был инициализирован, то в терминале отобразится сообщение, указанное в строке тринадцать.
А теперь начинается та часть, которая делает все очень интересным. Это происходит именно в строке 14, которая вызывает функцию со строки 27. Обратите внимание, что до этого момента массив ЕЩЕ НЕ ИНИЦИАЛИЗИРОВАН. Он будет инициализирован только в момент выполнения строки 31, поскольку именно эта строка отвечает за вызов процедуры, определенной в строке 20.
В строке 24 эта процедура инициализирует динамический массив, созданный в строке 6. Обратите внимание, что для этого мы используем статический массив типа ROM, который показан в строке 22. Я знаю, это кажется очень запутанным и трудным для понимания, но если вы посмотрите на функцию ArrayCopy из стандартной библиотеки, вы поймете, почему это работает. По сути, эта функция копирует один массив в другой. И существует огромное количество ситуаций в реальных приложениях, где это можно применить.
Сразу после этого, мы переходим к циклу в строке 33, где выполняем вычисления, чтобы получить результат и вернуться к строке 14.
Но прежде, чем вернуться к строке 14, мы используем строку 36 для добавления нового элемента в массив, объявленный в строке 6 и инициализированный в строке 24, что и является самой интересной частью. Если не инициализировать массив, к примеру, из-за удаления из кода строки 31, функция Possibilities вернет значение, определенное в строке 29, поскольку цикл в строке 33 не будет выполнен.
Однако, если посмотреть на содержимое массива, объявленного в строке шесть, то можно увидеть, что в массиве есть один элемент, и этот элемент будет иметь значение, указанное в строке 37.
На самом деле, чтобы понять все это, необходимо попробовать на практике. Поэтому, как я уже говорил, практикуйтесь и используйте материалы приложения, чтобы лучше понять, как все работает.
Заключительные соображения
В этой статье все стало гораздо более увлекательным, чем в предыдущих статьях, потому что здесь мы начали исследовать новые возможности использования концепций, которые объяснялись с самого первого материала. Я понимаю, что для тех, кто дошел до этого места, не читая других статей, многое из показанного может показаться чрезвычайно сложным и запутанным. Однако те, кто практиковал и изучал каждый материал, отлично понимают, что содержание этой статьи весьма интересно. Оно открывает нам множество возможностей, включая ту, которая довольно поверхностно уже обсуждалась в более ранней статье.
Тем не менее, в следующем материале серии мы сделаем так, чтобы то, что вы уже увидели, стало настолько обыденным и привычным, что вы даже не заметите, как станете отличным программистом. Опираясь на верные концепции и зная, как их применять, вы освободитесь от ограничений, в которых многие застревают именно потому, что не понимают идей, связанных с программированием, а просто копируют и вставляют код. Так что, приятного просмотра файлов в приложении, и увидимся в следующей статье.
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15473





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования