
От начального до среднего уровня: Массивы и строки (I)
Введение
Представленные здесь материалы предназначены только для обучения. Ни в коем случае нельзя рассматривать это приложение как окончательное, цели которого будут иные, кроме изучения представленных концепций.
В предыдущей статье, "От начального до среднего уровня: Приоритет операторов", мы немного поговорили о мерах предосторожности, которые следует соблюдать при использовании факторизации в наших кодах. Нередко можно встретить коды, которые кажутся правильными на первый взгляд, но в определенное время выдают странные результаты. Как правило, проблемы такого рода напрямую связаны с тем, как проводится факторизация. Заставить код выдавать разумные результаты кажется тривиальной задачей, но без соответствующих знаний (как показано в этой статье) шансы на то, что он катастрофически провалится, возрастают по мере внедрения произвольных факторизаций.
Поскольку теоретическая часть о правильном создании факторизации может быть утомительной и скучной, мы не будем на ней останавливаться. Мы будем действовать практично. Однако я советую вам по возможности экспериментировать, немного изменять предоставленные мною коды и пытаться создать различные факторизации или даже иное управление потоком выполнения, отличающееся от показанного в приложенных файлах.
Это очень важно, ведь с помощью практики, можно разными способами достичь один и тот же результат по самостоятельно разработанному пути. Выполняя такие упражнения (которые многие считают пустой тратой времени), вы будете отлично тренироваться, ведь в приложении у вас будет код, который работает и генерирует определенный тип результата. Ваша задача состоит в том, чтобы создать другой код, который даст тот же результат. Практикуясь, вы сможете создавать собственные решения, даже если поначалу они покажутся вам не слишком профессиональными.
После этого небольшого вступления, давайте перейдем к сегодняшней теме. Для этой статьи есть предварительное условие. Для его выполнения необходимо полностью изучить тему объявления и использования переменных и констант. Хотя сегодняшняя тема имеет гораздо больше отношения к операторам, чем к чему-либо еще, владение методами объявления, инициализации и использования переменных и констант необходимо для понимания того, что будет объяснено позже. Далее мы рассмотрим некоторые особые типы данных. В этом разделе мы не ограничимся теоретической частью. Этот вопрос, наряду с правилами приоритета, которые были рассмотрены в предыдущей статье, вероятно, является одним из самых сложных. Но на данном начальном этапе мы рассмотрим самые простые аспекты, которые только можно представить. ведь это действительно необходимо сделать, прежде чем приступать к решению еще более сложной проблемы. Давайте приступим к делу.
Массивы и строки
Несомненно, это один из вопросов, который больше вынуждает начинающих программистов отказаться от использования типизированных языков. И это происходит так, потому что нетипизированные языки, такие как Python и JavaScript, работают с подобными элементами почти прозрачно, поэтому программисту не нужно беспокоиться об их определении: он просто использует их.
С другой стороны такие языки, как C и C++, значительно усложняют данный вопрос, именно из-за того, как эти две сущности трактуются в данных языках. В этом случае они рассматриваются не как две отдельные сущности, а как одна, и в зависимости от того, как мы с ними работаем, в рамках этой же темы может появиться третья или даже четвертая сущность. Другими словами, это усложнение, которое делает работу начинающих программистов еще более сложной.
Однако в MQL5 мы находимся где-то между типизированными языками (C и C++) и нетипизированными языками (Python или JavaScript). Почему я говорю об этом? Потому что в отличие от C и C++, где можно игнорировать тип данных, присутствующий в массиве, а в некоторых случаях и в строке, невозможно сделать это в MQL5. По крайней мере, не без определенных манипуляций. И это делает процесс обучения немного проще. Однако без прочного и устоявшегося фундамента становится почти невозможным создать определенные типы манипуляций с данными на MQL5 таким же простым образом, как на C и C++. Но, несмотря на это, вам, как начинающим программистам, гораздо сложнее создать в MQL5 то, что выходит из-под вашего контроля. Это отличается от C и C++, где любая небольшая ошибка может превратиться в бомбу замедленного действия, готовую взорваться при малейшей неосторожности.
Между нами говоря, изучать MQL5 значительно проще, чем то же самое на C или C++. Поэтому я с удовольствием поделюсь своими знаниями в области C и C++, чтобы использовать их в MQL5. Возможно, я смогу научить некоторых читателей достигать хорошего уровня в программировании.
Давайте теперь приступим к нашей главной теме. Для этого сначала проясним один важный момент. В некотором смысле массивы и строки - это одно и то же. Это относится к данным, находящимся в памяти. Если быть точным, то строка - это не что иное, как массив, но специальный. Я называю его особым из-за одной характеристики, которая существует только в строках, но не в любом другом виде массивов. Эта деталь является маркером, который указывает, где заканчивается строка.
Чтобы проиллюстрировать это, мы проведем небольшую параллель с другими языками программирования, нечто простое, чтобы прояснить то, о чем мы говорим. Некоторые языки программирования, например, основанные на BASIC (да, старом, старом BASIC, появившемся в эпоху MS-DOS), используют в качестве первого символа строки данные, указывающие на количество символов или значений, присутствующих в строке. Такой подход позволяет использовать любой символ в строке, так как ее размер всегда задается в первом символе. Однако пользователь никогда не видит этот первый символ, который может занимать более одного байта. Он невидим и может быть обнаружен только кодом, что позволяет нам использовать любое символьное или числовое значение в строке. Однако у этой свободы есть предел, а именно разрядность первого символа, в котором хранится длина строки.
В языках на базе C и C++ используется другой метод. В этом случае для указания места окончания строки используется специальный символ или значение. На самом деле, тип string не существует в C и C++, обычно в качестве символа или значения используется ноль или null. Когда данное значение появляется в строке, оно указывает на конец строки. С одной стороны, это позволяет создать строку, которая занимает всю доступную память компьютера. С другой стороны, это ограничивает нас при использовании определенного значения в строке.
Поскольку он основан на языках C и C++, этот же подход использует MQL5. Однако, в отличие от последнего, в MQL5 есть тип под названием string. Поэтому мы не можем делать некоторые вещи, используя исключительно тип string в любом коде. В идеале следует использовать массив. Однако при этом возникают и другие проблемы. К счастью, библиотека MQL5 достаточно обширна, чтобы их обойти. В одних случаях - простым способом, в других - чуть более сложным.
А почему я говорю об этом? Потому что, если вы не понимаете этот тип моделирования, вы будете заперты в ограниченном количестве вариантов, поэтому не сможете выполнять определенные задачи. Особенно если они требуют более продвинутого уровня программирования. Нередко можно встретить людей, которые жалуются на то, что в MQL5 нельзя сделать то или иное. Но когда мы посмотрим на уровень знаний этих программистов, мы обнаружим, что они полностью зациклены на определенных концепциях и идеях, которые не позволяют им выйти за рамки того, что предлагает стандартная библиотека.
Дело не в том, чтобы использование стандартной библиотеки подразумевало недостаток знаний, всё именно наоборот. Но если вы не понимаете, как на самом деле связаны вещи, вы всегда будете жаловаться, что не можете сделать то, что хотели бы.
Итак, дорогие читатели, вы должны понимать следующее: массивы - это, так сказать, общая строка, в которую можно поместить что угодно. С другой стороны, строка - это конструкция, которая имеет лимиты, а во многих случаях ограничения. Если мы действительно хотим что-то сделать и не знаем ограничений массива или строки, у нас ничего не получится. Возможно, использование массивов усложняется тем, что строка - это массив типа uchar или ushort, в зависимости от используемого символьного кода. Однако чистый массив может быть любого типа, от класса до булевого. Это очень усложняет жизнь тем, кто только начинает изучать программирование. Но поскольку я хочу показать вам путь, по которому можно идти, поэтому мы начнем с самого основного. Другими словами, мы начнем со строк.
Данные типа string (Строки)
Когда мы создаем или объявляем что-то как строку, мы говорим компилятору, что хотим создать массив. Но не просто массив, а специальный массив, в котором будет символ или значение, указывающее, где заканчивается строка. В более глубоком смысле, используя переменную типа string, не нужно беспокоиться о выделении памяти. Данная работа выполняется кодом автоматически, по мере необходимости корректируется объем используемой памяти.
Это позволяет нам делать множество вещей без лишних усилий. С другой стороны, есть и некоторые ограничения, но сейчас мы не будем обращать на них внимание. Вместо этого мы сосредоточимся на том, что мы можем сделать. Помните, что для их освоения потребуется много практики, здесь мы коснемся лишь поверхности. Давайте начнем с первого кода, показанного ниже:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string sz0 = "Hello world !!!", 07. sz1 = "My first operation with strings...", 08. sz2; 09. 10. sz2 = sz0 + sz1; 11. 12. Print(sz2); 13. } 14. //+------------------------------------------------------------------+
Код 01
А теперь внимание. Здесь мы создаем три переменные типа string. Две из них объявляются и инициализируются в момент создания. Строка 03 объединяет две другие, образуя новую строку. В строке 12 мы просто выводим результат в терминал, как показано ниже:
Рисунок 01
Ключевым моментом здесь является то, что в строке 10 используется оператор сложения. Это один из немногих, если не единственный, оператор, который по умолчанию делает что-то определенное с типом string. В данном случае он соединяет одну строку с другой. Однако будьте очень осторожны с этим понятием, потому что операторы не всегда ведут себя так, как мы от них ожидаем. Позже рассмотрим этот вопрос более подробно. Пока просто будьте осторожны при работе с незнакомым кодом.
Хорошо, у нас есть предложение, отображаемое на терминале. Но что будет, если мы захотим разделить вещи на строки? Как мы можем это сделать? Один из способов - создать два вызова процедуры Print. Но если предпочитаете включать разрыв строки непосредственно в строку, можно сделать это с помощью одного из специальных маркеров.
Существует несколько специальных маркеров, которые могут появляться в строке. Все они, по крайней мере те, которые можно использовать в MQL5, являются производными от C и C++. Поэтому, если хотите добавить разрыв строки в каком-то месте, просто добавьте то, что показано ниже:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string sz0 = "Hello world !!!", 07. sz1 = "My first operation with strings...", 08. sz2; 09. 10. sz2 = sz0 + "\n" + sz1; 11. 12. Print(sz2); 13. } 14. //+------------------------------------------------------------------+
Код 02
Здесь мы можем увидеть один из таких маркеров в действии. Такие маркеры, например, "\n" называются escape-последовательностями. Если проводить исследование, то найдем множество таких маленьких кодов. Если включить этот тип маркера в строку, поведение кода изменится, и мы получим результат, как показано ниже:
Рисунок 02
Однако этот вид маркеров предназначен не только для этого. Например, мы можем опустить часть текста, если используем маркер NULL, как показано в следующем примере:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. string sz0 = "Hello world !!!", 07. sz1 = "My first operation \0with strings...", 08. sz2; 09. 10. sz2 = sz0 + "\n" + sz1; 11. 12. Print(sz2); 13. } 14. //+------------------------------------------------------------------+
Код 03
Выполнив код 03, мы получим в терминале MetaTrader 5 то, что показано на следующем рисунке:
Рисунок 03
Посмотрите, как это легко и просто. Но это не всё, мы также можем создавать строки с числовыми значениями. Для этого нам придется прибегать к функциям из стандартной библиотеки MQL5, что значительно упрощает процесс преобразования. Возможно, в будущем будет рассказано, как можно создать свою собственную версию для аналогичной цели. Но пока оставим это для другого раза. Используя функции преобразования, доступные в MQL5, мы можем конвертировать числа в строки и строки в числа.
Данная операция очень полезна и необходима во многих случаях, особенно при настройке систем анализа данных. Простой пример показан ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 65538; 07. double d_value = 0.25; 08. color cor = clrRed; 09. string sz0; 10. 11. sz0 = "Color: " + ColorToString(cor) + "\nInteger: " + IntegerToString(i_value) + "\nDouble: " + DoubleToString(d_value); 12. 13. Print(sz0); 14. } 15. //+------------------------------------------------------------------+
Код 04
Когда запускается код 04, будет видно нечто подобное этому:
Рисунок 04
Как видно, нам удалось создать строку с разнообразной информацией. Но в некоторых случаях формат создаваемой строки должен быть очень специфичным. Это может быть связано с типом отображаемой информации или необходимостью применения определенного форматирования к используемым данным. В данном случае мы должны действовать немного иначе, чем было показано до сих пор. Однако из-за характера информации далее, я думаю, будет лучше рассмотреть это в новом разделе. Таким образом, вам будет легче изучать и применять на практике представленные здесь знания.
Форматирование строк
Когда мы говорим о форматировании текста, многие вспоминают о текстовых редакторах. Однако, когда мы говорим об этом понятии в контексте программирования, мы имеем в виду то, что определенная информация должна соответствовать определенным критериям, прежде чем ее можно будет использовать или распечатать.
Данные критерии составляют так называемое форматирование строки. На первый взгляд, этот тип операций довольно простой и удобный в использовании, и позволяет нам создавать строки с очень специфической информацией довольно простым способом. Но есть некоторые аспекты, о которых вы должны знать. Среди них - использование и построение выходных параметров. При правильной настройке данные параметры значительно облегчают жизнь программисту, даже если он новичок, поскольку в значительной степени заменяют настройку, которую мы рассматривали в предыдущей теме. В то же время они допускают тонкую настройку и обеспечивают существенную гибкость для построения четко определенных строк.
Вы, вероятно, уже видели код, использующий процедуру библиотеки PrintFormat. Эта процедура позволяет отображать в терминале MetaTrader 5 некую форматированную информацию, обычно это строка. Однако не всегда хочется или желательно отправлять данную информацию или строку на терминал. Зачастую мы хотим или должны использовать эту информацию в другом контексте, например, сохранить ее в файле или запустить/использовать в объекте, присутствующем на графике. В этих случаях процедура PrintFormat не всегда помогает. К счастью, существует функция, которая как нельзя лучше подходит для этих целей: StringFormat.
Функция StringFormat использует тот же тип параметризации, что и PrintFormat. Но, в отличие от PrintFormat, ее вывод может быть направлен на строку. Это делает ее использование интересным в самых разных ситуациях.
Учитывая это, мы можем сгенерировать вывод, основанный на том, что мы видели в коде 04, но в уже отформатированном виде. Возможно, на этом этапе вы еще немного запутаетесь в некоторых параметрах. Поэтому я вам рекомендую внимательно изучить каждый из параметров. Пройдитесь по документации в разделе, посвященном процедуре PrintFormat. Там вы найдете множество моментов, которые объясняют, как создать формат строки, которую вы хотите вывести. Чтобы проиллюстрировать это, давайте изменим код 04 так, чтобы он был отформатирован. В результате мы получим такой код:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 65538; 07. double d_value = 0.25; 08. color cor = clrRed; 09. string sz0; 10. 11. sz0 = StringFormat("Color: %s\nInteger: %d\nDouble : %.2f", ColorToString(cor), i_value, d_value); 12. 13. Print(sz0); 14. } 15. //+------------------------------------------------------------------+
Код 05
Запустив код 05, мы увидим в терминале MetaTrader 5 результат, похожий на следующий рисунок:
Рисунок 05
Посмотрите на различия между рисунками 04 и 05. Хотя в обоих случаях используется нечто очень похожее, информация в рисунке 05 имеет формат, который был специально определен в нашем коде, в частности, при реализации строки 11. Можете возразить, что такая настройка здесь не нужна, но подумайте о сценарии, в котором нам нужно работать с шестнадцатеричными значениями. Многие программы используют этот тип значений в ситуациях, когда приходится работать с битами. Если мы хотим визуализировать подобные данные, чтобы убедиться, что всё происходит так, как ожидалось, как мы это сделаем?
Способы отображения могут быть разными, но в целом для отображения шестнадцатеричных значений можно использовать формат, применяемый в коде 05. Кто-то может даже предположить, что для данной задачи лучше создать специальный код. Но, на мой взгляд, это скорее вопрос личных предпочтений, чем реальной необходимости. В любом случае, каждый программист волен принимать собственные решения. Но поскольку вы только начинаете программировать, в идеале вам следует полагаться на функции и процедуры, предоставляемые стандартной библиотекой. В этом случае мы используем функцию StringFormat для создания репрезентации шестнадцатеричных значений, которые будут отображаться позже. Для этого мы продолжим использовать код 05, но изменим способ форматирования создаваемой строки. Это делается очень просто: замените строку 11 на строку, показанную ниже.
sz0 = StringFormat("Color: 0x%X\nInteger: 0x%X\nDouble : %.2f", cor, i_value, d_value);
Когда снова будет запущен код после внесения данного изменения, результат станет таким:
Рисунок 06
Интересно, не правда ли? Однако здесь есть небольшая проблема, и именно поэтому я упомянул, что вам следует обратить внимание на моменты, связанные с форматированием строк. Проблема в этом случае заключается в значении цвета. Если заметили, он показывается в шестнадцатеричном формате, но это значение на изображении 06 не обязательно указывает на то, что цвет красный. Данное значение может представлять собой другой цвет. Помните, что цветовой формат - это "RGB" и, в некоторых случаях, "ARGB". Поэтому, просто взглянув на это значение, невозможно точно определить, что оно из себя представляет. Однако с помощью небольшой корректировки мы можем изменить значение, сделав его по-настоящему репрезентативным. Для этого просто изменим код, как показано ниже:
sz0 = StringFormat("Color: 0x%06X\nInteger: 0x%X\nDouble : %.2f", cor, i_value, d_value);
Запустив код с этой модификацией, мы получим результат, похожий на следующий рисунок:
Рисунок 07
Подождите! Это точно не красный цвет. То, что появляется здесь - это синий цвет. Что здесь произошло? Может ли проблема заключаться в том, что мы добавляем несколько нулей перед отображаемым значением, чтобы создать нужный формат? Что-то похожее, но не совсем так, как мы могли себе представить вначале. Объяснить настоящие причины немного сложнее, если не показать другой пример, в котором применяется та же концепция форматирования значения цвета для представления его в шестнадцатеричном формате.
Чтобы проиллюстрировать произошедшее, изменим значение переменной ""i_value"" и попросим ее отформатировать так же, как мы отформатировали значение цвета. То есть, в зависимости от отображаемого значения, может быть несколько нулей спереди. Для наглядности давайте посмотрим на код ниже:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 0xF; 07. double d_value = 0.25; 08. color cor = clrRed; 09. string sz0; 10. 11. sz0 = StringFormat("Color :\t0x%06X\t=>\t%s\n" + 12. "Integer:\t0x%06X\t=>\t%d\n" + 13. "Double :\t%.2f", cor, ColorToString(cor), i_value, i_value, d_value); 14. 15. Print(sz0); 16. } 17. //+------------------------------------------------------------------+
Код 06
Когда мы запускаем код 06, мы увидим нечто похожее на это:
Рисунок 08
Здесь я разложил всё по полочкам, чтобы вы могли понять то, что я хочу показать. Обратите внимание, что в строках 11 и 12 кода мы выведем два значения. Мы сделали так для того, чтобы можно было видеть, почему шестнадцатеричное значение цвета отображается именно так. Видно, что значение i_value оказалось правильным. Затем мы заменим значение i_value на большее. Как показано в следующей строке, внесение данной модификации всё равно приводит к стабильному результату, как вы увидите на следующем изображении:
int i_value = 0xDA09F;
Рисунок 09
Другими словами, форматирование работает идеально. Но тогда почему не показывает шестнадцатеричное значение цвета правильно? Причина в том, что, хотя ожидаемый формат - RGB, внутри в самом деле используется BGR. Другими словами, значения отличаются от того, что мы себе представляем. Поэтому, хотя значение в шестнадцатеричном поле выглядит неверно, оно правильное. Можем ли мы изменить это и представить значения таким образом, чтобы они соответствовали нашим ожиданиям? Да, мы можем это сделать. Благодаря знаниям, полученным в предыдущих статьях, сделать это довольно просто. Ниже приводится возможный код, отвечающий этой задаче.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. int i_value = 0xDA09F; 07. double d_value = 0.25; 08. color cor = clrRoyalBlue; 09. string sz0; 10. 11. sz0 = StringFormat("Color :\t0x%06X\t=>\t%s\n" + 12. "Integer:\t0x%06X\t=>\t%d\n" + 13. "Double :\t%.2f", cor, ColorToString(cor), i_value, i_value, d_value); 14. 15. Print(sz0); 16. PrintFormat("Color value in hexadecimal (RGB format): %s", ColorToStrHex(cor)); 17. } 18. //+------------------------------------------------------------------+ 19. string ColorToStrHex(const color arg) 20. { 21. return StringFormat("0x%02X%02X%02X", (arg & 0xFF), ((arg >> 8) & 0xFF), (arg >> 16) & 0xFF); 22. } 23. //+------------------------------------------------------------------+
Код 07
Обратите внимание, что в строке 19 мы создаем небольшую функцию. Ее довольно просто понять, основываясь на концепциях, которые я уже объяснял в своих статьях. Запустив его, мы получим строку в шестнадцатеричном формате, но на этот раз значение будет отображаться так, как мы ожидали при анализе цвета в формате RGB. Когда запустим код 07, увидим в терминале нечто похожее на следующий рисунок:
Рисунок 10
Внимание, теперь будет интересно. В первой строке рисунка 10 выражено значение цвета. Однако шестнадцатеричное значение не соответствует тому, что мы ожидали найти. Но если мы посмотрим на последнюю строку этого же рисунка, то увидим, что значение, которое мы ожидали найти, интерпретируя цвет как RGB, действительно там появляется. То есть значения в первой строке представлены в обратном порядке в шестнадцатеричном формате, но мы можем переставить их так, чтобы они располагались в нужном порядке. Важный момент: значение, которое показано в последней строке рисунка 10, не соответствует цвету, указанному в восьмой строке кода 07. Это представление просто показывает ожидаемое значение, основанное на результатах, возвращаемых функцией ColorToString. Поэтому, не смущайтесь. Я изменил цвет, чтобы информация имела смысл и мы могли ее правильно объяснить.
Заключительные идеи
В данной статье показана первая часть чего-то гораздо более глубокого и продвинутого, чем может показаться на первый взгляд. Я понимаю, что многие могут быть в некотором замешательстве от сегодняшних объяснений. Но уверяю вас, что если вы начнете внимательно изучать этот материал и достаточно попрактикуетесь, то сможете разрабатывать интересные приложения с точки зрения общения с пользователем.
Хотя то, что мы здесь рассмотрели, - лишь краткий обзор того, что можно сделать, у вас уже есть много материала для изучения и практики. В следующей статье мы изучим чуть более сложный материал, который, несомненно, станет основополагающим при объяснении других тем, с которыми мы познакомимся позже. Надеюсь, вам будет приятно работать с файлами, включенными в приложение. До встречи в следующей статье!
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15441





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