От начального до среднего уровня: Struct (VII)
Введение
В предыдущей статье, От базового до среднего уровня: Struct (VI) мы рассмотрели, как начать создавать универсальную реализацию простой структуры данных. Несмотря на его необыкновенность, данный тип моделирования используется гораздо чаще, чем думает большинство людей. Это объясняется тем, что в больших базах данных используется очень похожий принцип для организации и поиска собственных данных.
Я понимаю, что эта тема, которая затрагивается в наших статьях, может показаться многим излишней. Однако стоит помнить, что наша цель — объяснять вещи таким образом, чтобы в будущем не было необходимости разъяснять некоторые детали, которые, на мой взгляд, являются тривиальными. Но, поскольку большинство из вас используют эти статьи, чтобы учиться на опыте и знаниях других программистов, всё, что я могу передать вам в начале, окажет вам большую помощь в будущем, дорогие читатели.
Недостаточно просто увидеть работающий код, нужно понимать, почему он работает, и, при необходимости, знать, как адаптировать реализацию, сделанную другим программистом, к нашим потребностям. Для этого необходимо знать эти концепции и понимать, как их можно применять.
Как и в предыдущей статье, мы начали реализовывать нечто довольно интересное, чтобы лучше понять, как структуры могут использоваться внутри других структур, и, возможно, вас заинтриговал результат работы над последним кодом. Это произошло так, потому что мы использовали структуру, созданную для одной цели, а в итоге смогли применить её для другой. В этом случае данные изначально имели тип double, но в итоге мы смогли использовать данные любого типа.
Однако то, что мы показали, — это не то, чего мы хотели добиться. Для этого нам нужно ещё немного продвинуться вперед. Но все эти усилия окупятся и будут вознаграждены в будущем. Даю слово. Итак, давайте продолжим с того момента, где остановились в предыдущей статье.
Структуры структур, часть вторая, возвращение
Предыдущую статью мы завершили кодом, который показан ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. template <typename T> 05. struct st_Data 06. { 07. //+----------------+ 08. private: 09. //+----------------+ 10. struct st_Reg 11. { 12. T h_value; 13. uint k_value; 14. }Values[]; 15. //+----------------+ 16. string ConvertToString(T arg) 17. { 18. if ((typename(T) == "double") || (typename(T) == "float")) return DoubleToString(arg, 2); 19. if (typename(T) == "string") return arg; 20. 21. return IntegerToString(arg); 22. } 23. //+----------------+ 24. public: 25. //+----------------+ 26. bool Set(const uint &arg1[], const T &arg2[]) 27. { 28. if (arg1.Size() != arg2.Size()) 29. return false; 30. 31. ArrayResize(Values, arg1.Size()); 32. for (uint c = 0; c < arg1.Size(); c++) 33. { 34. Values[c].k_value = arg1[c]; 35. Values[c].h_value = arg2[c]; 36. } 37. 38. return true; 39. } 40. //+----------------+ 41. string Get(const uint index) 42. { 43. for (uint c = 0; c < Values.Size(); c++) 44. if (Values[c].k_value == index) 45. return ConvertToString(Values[c].h_value); 46. 47. return "-nan"; 48. } 49. //+----------------+ 50. }; 51. //+------------------------------------------------------------------+ 52. #define PrintX(X) Print(#X, " => ", X) 53. //+------------------------------------------------------------------+ 54. void OnStart(void) 55. { 56. const string T = "possible loss of data due to type conversion"; 57. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 58. 59. st_Data <string> info; 60. string H[]; 61. 62. StringSplit(T, ' ', H); 63. info.Set(K, H); 64. PrintX(info.Get(3)); 65. } 66. //+------------------------------------------------------------------+
Код 01
Хотя код 01 работает, он несколько раздражает. Это связано с тем, что при компиляции мы получаем ответ, похожий на тот, который показан далее.

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

Изображение 02
Интересно, не так ли? Но прежде, чем продолжить, давайте решим проблему, показанную на изображении 01. Существует несколько способов для этого. Однако все они в итоге приводят к одному и тому же результату: к явному преобразованию. Поэтому, способ достижения точки явного преобразования не имеет значения. Важно, чтобы это имело место. Для этого просто изменим код 01 на что-то похожее на это:
. . . 15. //+----------------+ 16. string ConvertToString(T arg) 17. { 18. if (typename(T) == "double") return DoubleToString((double)arg, 2); 19. if (typename(T) == "float") return DoubleToString((float)arg, 2); 20. if (typename(T) == "string") return (string)arg; 21. return IntegerToString((long)arg); 22. } 23. //+----------------+ 24. public: 25. //+----------------+ . . .
Код 02
Увидев, что решение проблемы на изображении 01 — это фрагмент кода 02, вы, наверняка, немного разочаровались. Многие, вероятно, ожидали, что будет создан полный набор инструкций и функций, и в конечном итоге нам нужно сделать только то, что показано в этом коде 02. Это часть процесса. Степень разочарования пропорциональна уровню наших ожиданий по отношению к чему-либо. Шутки в сторону, давайте вернемся к коду и поймем, как сделать его более расширяемым, чтобы он мог обрабатывать различные данные без существенных изменений.
Возможно, вы подумаете, что это будет сложно. Однако, прежде чем объяснить, как это сделать, мне нужно, чтобы вы действительно поняли как и зачем связывать элемент одного массива с другим элементом другого массива.
Сама концепция очень проста. Речь идёт всего лишь о создании перевода. Иными словами, если нам известен элемент массива, который будет ключом, то ответ будет, по сути, элементом другого массива. Именно это мы и сделаем с кодом 01. А почему бы для этого не использовать многомерный массив? Причина в том, что, хотя многомерный массив является наилучшим вариантом в данном случае, он не подходит, поскольку мы используем его как доказательство чего-либо. Иными словами, речь идёт о демонстрации работы с определённым типом языковых ресурсов.
Хорошо, предполагаю, что было достаточно ясно объяснено то, что мы делаем, и я хочу, чтобы вы ещё раз посмотрели на код 01 и сказали мне, что мешает его использованию не только в переводе значений, но и в других подобных контекстах.
Не спешите с выводами, сначала подумайте. Теперь мы рассмотрим один из многих способов освободить код, чтобы мы могли поместить в него что угодно. Однако, если попытаться развернуть код, то заметим, что проблема заключается в структуре, объявленной в строке 10. В этом и проблема, но не в том смысле, в каком могли бы подумать.
Проблема структуры строки 10 заключается в том, что она препятствует более универсальному использованию основной структуры. Поскольку я понимаю, что это сложно и требует времени, мы продолжим статью. Но я прошу вас прочитать и перечитать данную статью и предыдущие, чтобы понять одну вещь.
Структуры, как и другие виды конструкций, представляют собой формы отображения более или менее сложных данных. Способ решения проблемы зависит от её реальной сложности. Некоторые задачи можно решить с помощью дискретных и простых видов данных, таких как представления с плавающей точкой или целочисленные значения.
Когда задача становится более сложной, дискретных типов может быть недостаточно. В этом случае нам потребуются более сложные виды данных, такие как массивы и объединения. Если проблема по-прежнему сложна, нам нужен такой же сложный тип: структуры.
Таким образом, структура, объявленная нами в строке 10 кода 01, представляет собой не что иное, как комплексный тип. Это необходимо понять, чтобы разбираться в том, что мы собираемся делать.
Во-первых, мы должны взять данную структуру из строки 10 структуры, определенной в строке 04, сохранив при этом код 01. Однако, поступая таким образом, мы создадим путаницу в информации, которая запутает вас ещё сильнее, если вы не поняли или не смогли понять предыдущие статьи. Это связано с тем, что нет способа запускать изменения, которые будут происходить постепенно. И это необходимо сделать за очень небольшое количество шагов. Первый шаг показан чуть ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. struct st_Reg 05. { 06. string h_value; 07. uint k_value; 08. }; 09. //+----------------+ 10. struct st_Data 11. { 12. //+----------------+ 13. private: 14. //+----------------+ 15. st_Reg Values[]; 16. //+----------------+ 17. public: 18. //+----------------+ 19. bool Set(const uint &arg1[], const string &arg2[]) 20. { 21. if (arg1.Size() != arg2.Size()) 22. return false; 23. 24. ArrayResize(Values, arg1.Size()); 25. for (uint c = 0; c < arg1.Size(); c++) 26. { 27. Values[c].k_value = arg1[c]; 28. Values[c].h_value = arg2[c]; 29. } 30. 31. return true; 32. } 33. //+----------------+ 34. string Get(const uint index) 35. { 36. for (uint c = 0; c < Values.Size(); c++) 37. if (Values[c].k_value == index) 38. return Values[c].h_value; 39. 40. return NULL; 41. } 42. //+----------------+ 43. }; 44. //+------------------------------------------------------------------+ 45. #define PrintX(X) Print(#X, " => ", X) 46. //+------------------------------------------------------------------+ 47. void OnStart(void) 48. { 49. const string T = "possible loss of data due to type conversion"; 50. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 51. 52. st_Data info; 53. string H[]; 54. 55. StringSplit(T, ' ', H); 56. info.Set(K, H); 57. PrintX(info.Get(3)); 58. } 59. //+------------------------------------------------------------------+
Код 03
Внимание: чтобы сделать код (и то, что мы собираемся делать) максимально простым и наглядным, мы сделаем шаг назад и используем тип string. Прошу заметить, что создаваемая перегрузка структуры больше не существует, и результат выполнения по-прежнему соответствует изображению 02.
Возможно, если вы посмотрите на код 03 то подумаете: «Хорошо, вы сказали, что это сложно, но пока всё по-прежнему просто, в чём же сложность?» Хорошо, тогда давайте приступим. Второй момент, который необходимо изменить, заключается в том, что функции Set и Get (которые вы видите в строках 19 и 34 соответственно) НЕ ЯВЛЯЮТСЯ ФУНКЦИЯМИ из st_Data, а являются функциями st_Reg.
Теперь я спрашиваю: что они делают в st_Data? Ответ: Они затрудняют функционирование структуры в более универсальном режиме, что препятствует обработке данных любого типа. Следовательно, нам необходимо удалить данные функции из структуры st_Data, которая появляется в коде 03. Для большей простоты давайте посмотрим, как будет выглядеть исправленный фрагмент. Он показан ниже.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. struct st_Reg 05. { 06. //+----------------+ 07. private: 08. //+----------------+ 09. string h_value; 10. uint k_value; 11. //+----------------+ 12. public: 13. //+----------------+ 14. void Set(const uint arg1, const string arg2) 15. { 16. k_value = arg1; 17. h_value = arg2; 18. } 19. //+----------------+ 20. uint Get_K(void) { return k_value; } 21. //+----------------+ 22. string Get_H(void) { return h_value; } 23. //+----------------+ 24. }; 25. //+----------------+ 26. struct st_Data 27. { 28. //+----------------+ 29. private: 30. //+----------------+ 31. st_Reg Values[]; 32. //+----------------+ 33. public: 34. //+----------------+ 35. bool Set(const st_Reg &arg) 36. { 37. if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE) 38. return false; 39. 40. Values[Values.Size() - 1] = arg; 41. 42. return true; 43. } 44. //+----------------+ 45. st_Reg Get(const uint index) 46. { 47. for (uint c = 0; c < Values.Size(); c++) 48. if (Values[c].Get_K() == index) 49. return Values[c]; 50. 51. return Values[0]; 52. } 53. //+----------------+ 54. }; 55. //+------------------------------------------------------------------+ . . .
Код 04
«Что за безумие?» Успокойтесь, это только начало. Теперь у нас есть не одна, а две структуры для объединения данных в новой структуре st_Data. Но прежде чем перейти к основному коду (который будет находиться в OnStart), нам нужно понять происходящее здесь.
На самом деле, хотя данный фрагмент кода работает так же, как и код 03, здесь есть несколько моментов, которые совершенно отличаются от того, к чему мы привыкли. Тем не менее, на мой взгляд, это всё ещё код базового уровня, поскольку мы имеем дело с очень простыми вещами.
Для начала следует помнить, что st_Reg — это структура, предназначенная для работы со структурным кодом. Иными словами, мы не будем напрямую контактировать с переменными, но создадим контекст, в котором сможем ими управлять.
По сути, st_Reg работает так же, как и до начала темы о вложенных структурах. Теперь начинается та часть, которая усложняет жизнь многим новичкам, особенно если им неясны основные понятия о том, что такое переменная и как работать с типами данных. Давайте разберемся, что происходит во второй структуре, которая называется st_Data. Поскольку st_Reg — это сложный тип данных, в строке 31 мы можем объявить переменную, которая в данном случае является массивом, для хранения данных данного типа, то есть st_Reg. Пока что, наверное, вы не видите никаких проблем, не так ли?
Однако, если мы посмотрим на строку 35, то увидим, что где-то вещи перестают иметь смысл, особенно если обратить внимание на строку 37. Боже мой, что там происходит?! В данной строке мы просто пытаемся выделить место в памяти, дорогие читатели. Однако есть один небольшой момент, который немного запутывает ситуацию. Дело в том, что в MQL5 МЫ НЕ МОЖЕМ ВЕРНУТЬ УКАЗАТЕЛИ. Поэтому в функции, указанной в строке 45, есть небольшой недостаток, который мы пытаемся исправить в строке 37.
Поэтому будьте внимательны. Если мы пытаемся найти значение, вставленное в структуру st_Data, и НЕ НАХОДИМ ЕГО, нам приходится возвращать недопустимые данные. В таких языках, как C и C++, мы делаем это, обычно возвращая нулевое значение. Однако здесь, в MQL5, МЫ НЕ МОЖЕМ ЭТОГО СДЕЛАТЬ. Нам нужна небольшая хитрость. Поэтому, когда в строке 37 будет предпринята попытка выделить первый блок для добавления данных типа st_Reg, мы выделим две ячейки памяти. Первая будет пуста. Однако второй будет присвоено значение, соответствующее строке 40.
Поскольку это первый элемент, ему будут присвоены две позиции. Начиная со следующего вызова, строка 37 будет определять, что нам уже назначено как минимум два элемента, и назначит только ещё один. Таким образом, это будет происходить при каждом новом вызове функции Set структуры st_Data. Прошу заметить, что это никак не связано с процедурой Set структуры st_Reg, и этому есть причина, которая будет рассмотрена позже.
Итак, поскольку функция Get структуры st_Data напрямую связана с объяснением структуры Set, нам нужно понять лишь то, что значение, полученное в качестве аргумента функцией Get, не указывает, что именно ищет индекс в массиве Values, а скорее, какое значение физически хранится в этом конкретном элементе. Поэтому нам нужен цикл для поиска конкретного значения в структуре st_Reg. Данную проблему мы обсудим позже. В целом, это всё.
Теперь мы можем просмотреть основной код. Если помните, данный код, который мы видели во фрагменте из кода 04, можно было бы прекрасно разместить в заголовочном файле, и он бы работал без проблем. Но прежде, чем показать, как это сделать, чтобы можно было обобщить код, нам нужно взглянуть на остальную его часть. Он показан ниже.
. . . 55. //+------------------------------------------------------------------+ 56. #define PrintX(X) Print(#X, " => [", X, "]") 57. //+------------------------------------------------------------------+ 58. void OnStart(void) 59. { 60. const string T = "possible loss of data due to type conversion"; 61. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 62. 63. st_Data info; 64. string H[]; 65. 66. StringSplit(T, ' ', H); 67. for (uint c = 0; c < H.Size(); c++) 68. { 69. st_Reg reg; 70. 71. reg.Set(K[c], H[c]); 72. info.Set(reg); 73. } 74. 75. PrintX(info.Get(3).Get_H()); 76. PrintX(info.Get(13).Get_H()); 77. } 78. //+------------------------------------------------------------------+
Код 05
И вот начинается самая веселая и захватывающая часть. Поскольку вы, вероятно, уже поняли большую часть этого кода, поскольку он был показан ранее, давайте перейдем к новой части. Это относится конкретно к циклу строки 67, а также строк 75 и 76.
Начнём с цикла. Поскольку каждый элемент, который должен быть помещен в структуру st_Data, имеет тип st_Reg, нам нужна строка 71 для создания структуры st_Reg, чтобы затем можно было сохранить элемент типа st_Reg в структуре st_Data, используя строку 72. Теперь вопрос: почему мы не делаем это непосредственно в структуре st_Data? Ответ: потому что, если бы запись элементов типа st_Reg производилась непосредственно в структуре st_Data, мы не смогли бы перегрузить эту структуру позже. Но не волнуйтесь, скоро вы всё поймёте.
После того, как все элементы будут записаны в структуре st_Data, мы можем использовать строки 75 и 76 для проверки этой структуры. Для этого в строке 75 мы ищем строку, индекс которой равен трём. А, взглянув на строки 60 и 61, можно увидеть, какое значение имеет этот индекс. В строке 76, в свою очередь будет выполнен поиск элемента, индекс которого равен 13. Поскольку данный индекс не определен в строке 61, возвращаемое значение будет тем, которое определено в строке 51 из кода 04.
Обладая этими предварительными знаниями, можно запустить код. Таким образом, мы увидим следующий результат:

Изображение 03
Другими словами, он работает идеально. Теперь мы можем обобщить значительную часть кода, но для этого мы перейдём к новой теме. Таким образом, можно будет спокойно изучить то, что уже сделали, а затем попытаться понять, что ещё предстоит сделать, чтобы сделать обобщения.
Структуры структур, часть третья, месть
Если вам показалось, что информация в предыдущей теме была непонятной, приготовьтесь, потому что дальше будет ещё интереснее. Здесь всё по настоящему усложняется. Поэтому обратите внимание на следующий факт. В коде 04 из предыдущей темы, структура st_Data может принимать только один тип данных, а именно тип st_Reg, но это не делает её универсальной. Это ограничивает структуру st_Data только тем, что определено в структуре st_Reg, даже если они никак не связаны друг с другом.
Однако, если немного изменить код структуры st_Data, мы сможем освободить её ресурсы, чтобы она могла работать с различными типами данных. Обратите внимание на то, что я только что сказал. Когда структура st_Data становится шаблоном, она не будет полностью свободна от структуры st_Reg, что связано с функцией Get, которая останется связанной с типом st_Reg. Тем не менее, это стоит своих усилий.
Как указано выше, всё, что нам нужно сделать, это преобразовать структуру st_Data в шаблон и одновременно исправить основной код, чтобы он соответствовал этому шаблону, который должен будет создать компилятор. Это делается с помощью следующего кода.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. struct st_Reg 05. { 06. //+----------------+ 07. private: 08. //+----------------+ 09. string h_value; 10. uint k_value; 11. //+----------------+ 12. public: 13. //+----------------+ 14. void Set(const uint arg1, const string arg2) 15. { 16. k_value = arg1; 17. h_value = arg2; 18. } 19. //+----------------+ 20. uint Get_K(void) { return k_value; } 21. //+----------------+ 22. string Get_H(void) { return h_value; } 23. //+----------------+ 24. }; 25. //+----------------+ 26. template <typename T> 27. struct st_Data 28. { 29. //+----------------+ 30. private: 31. //+----------------+ 32. T Values[]; 33. //+----------------+ 34. public: 35. //+----------------+ 36. bool Set(const T &arg) 37. { 38. if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE) 39. return false; 40. 41. Values[Values.Size() - 1] = arg; 42. 43. return true; 44. } 45. //+----------------+ 46. T Get(const uint index) 47. { 48. for (uint c = 0; c < Values.Size(); c++) 49. if (Values[c].Get_K() == index) 50. return Values[c]; 51. 52. return Values[0]; 53. } 54. //+----------------+ 55. }; 56. //+------------------------------------------------------------------+ 57. #define PrintX(X) Print(#X, " => [", X, "]") 58. //+------------------------------------------------------------------+ 59. void OnStart(void) 60. { 61. const string T = "possible loss of data due to type conversion"; 62. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 63. 64. st_Data <st_Reg> Info_1; 65. string H[]; 66. 67. StringSplit(T, ' ', H); 68. for (uint c = 0; c < H.Size(); c++) 69. { 70. st_Reg reg; 71. 72. reg.Set(K[c], H[c]); 73. Info_1.Set(reg); 74. } 75. 76. PrintX(Info_1.Get(3).Get_H()); 77. PrintX(Info_1.Get(13).Get_H()); 78. } 79. //+------------------------------------------------------------------+
Код 06
Теоретически, код 06 может содержать любой тип структуры данных или любой тип данных внутри структуры st_Data. В теории это так. Причина кроется именно в функции Get, расположенной в строке 46 из кода 06. Чтобы сделать код более универсальным, нам нужно было всего лишь добавить строку 26 и заменить ссылки на st_Reg в строках 32, 36 и 46 на тип T. После этого нам потребуется изменить объявление в строке 64, чтобы компилятор мог сгенерировать соответствующую структуру.
Результат выполнения кода 06 будет таким же, как и при выполнении кода 05, то есть изображение 03. Однако мы по-прежнему, в некотором смысле, привязаны к структуре st_Reg, хотя эта привязка (которая одновременно является проблемой) может также принести некоторую пользу. Всё зависит от потребностей, уровня креативности и уровня знаний каждого читателя по каким-то темам. Обратите внимание на следующее. Единственная оставшаяся связь между структурами st_Data и st_Reg — это так называемая функция Get_K, которая располагается в строке 49. Это единственная связь, которая ещё сохранилась. Поскольку структура st_Reg также может быть перегружена, в этом случае мы можем использовать разные типы.
Но поскольку подобные вещи могут породить сложные детали, которые трудно понять в заключительной части статьи, мы не будем говорить об этом сейчас.
Однако, даже так мы можем сделать нечто, как мне кажется, интересное и это также можно объяснить в оставшейся части статьи. Речь идет именно о 49-й строке из кода 06. А теперь обратите внимание, дорогие читатели: в статье От начального до среднего уровня: Перегрузка, мы поговорили об очень простом и интересном способе использования перегрузки функций и процедур. В других статьях этой серии мы также показали, что это можно сделать с использованием имен, идентичных тем, что содержатся в стандартной библиотеке.
А теперь самое интересное: что если в коде 06 мы создадим способ перегрузки функции Get_K, показанной в строке 49? Какие возможности это откроет? Как мы уже сказали, именно здесь творчество и применение концепций делают вещи очень интересными. Это объясняется тем, что, если присмотреться, вы заметите, что структура st_Reg (показанная в коде 06) содержит всего две переменные. Но мы можем перегрузить структуру st_Reg, чтобы получить другую структуру с большим количеством переменных или даже с совершенно другими типами данных внутри структуры.
Однако, сделав это, нам не обязательно придется удалить исходную структуру st_Reg или изменять структуру st_Data, уже созданную в коде 06. Поскольку я не уверен, что вы понимаете то, что я имею в виду, давайте посмотрим на фрагмент кода, где мы сможем увидеть, что я пытаюсь показать. Данный код находится ниже.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. struct st_Reg 005. { 006. //+----------------+ 007. private: 008. //+----------------+ 009. string h_value; 010. uint k_value; 011. //+----------------+ 012. public: 013. //+----------------+ 014. void Set(const uint arg1, const string arg2) 015. { 016. k_value = arg1; 017. h_value = arg2; 018. } 019. //+----------------+ 020. uint Get_K(void) { return k_value; } 021. //+----------------+ 022. string Get_H(void) { return h_value; } 023. //+----------------+ 024. }; 025. //+------------------------------------------------------------------+ 026. struct st_Bio 027. { 028. //+----------------+ 029. private: 030. //+----------------+ 031. string h_value; 032. string b_value; 033. uint k_value; 034. //+----------------+ 035. public: 036. //+----------------+ 037. void Set(const uint arg1, const string arg2, const string arg3) 038. { 039. k_value = arg1; 040. h_value = arg2; 041. b_value = arg3; 042. } 043. //+----------------+ 044. uint Get_K(void) { return k_value; } 045. //+----------------+ 046. bool Get_Bio(string &arg1, string &arg2) 047. { 048. arg1 = h_value; 049. arg2 = b_value; 050. 051. return true; 052. } 053. //+----------------+ 054. }; 055. //+------------------------------------------------------------------+ 056. template <typename T> 057. struct st_Data 058. { 059. //+----------------+ 060. private: 061. //+----------------+ 062. T Values[]; 063. //+----------------+ 064. public: 065. //+----------------+ 066. bool Set(const T &arg) 067. { 068. if (ArrayResize(Values, Values.Size() + (Values.Size() == 0 ? 2 : 1)) == INVALID_HANDLE) 069. return false; 070. 071. Values[Values.Size() - 1] = arg; 072. 073. return true; 074. } 075. //+----------------+ 076. T Get(const uint index) 077. { 078. for (uint c = 0; c < Values.Size(); c++) 079. if (Values[c].Get_K() == index) 080. return Values[c]; 081. 082. return Values[0]; 083. } 084. //+----------------+ 085. }; 086. //+------------------------------------------------------------------+ 087. #define PrintX(X) Print(#X, " => [", X, "]") 088. //+------------------------------------------------------------------+ 089. void CheckBio(st_Data <st_Bio> &arg) 090. { 091. string sz[2]; 092. 093. Print("Checking data in the structure..."); 094. for (uint i = 7; i < 11; i += 3) 095. { 096. Print("Index: ", i, " Result: "); 097. if (arg.Get(i).Get_Bio(sz[0], sz[1])) 098. ArrayPrint(sz); 099. else 100. Print("Failed."); 101. } 102. } 103. //+------------------------------------------------------------------+ 104. void OnStart(void) 105. { 106. const string T = "possible loss of data due to type conversion"; 107. const string M[] = {"2", "cool", "4", "zero", "mad", "five", "what", "xoxo"}; 108. const uint K[] = {2, 1, 4, 0, 7, 5, 3, 6}; 109. 110. st_Data <st_Reg> Info_1; 111. st_Data <st_Bio> Info_2; 112. 113. string H[]; 114. 115. StringSplit(T, ' ', H); 116. for (uint c = 0; c < H.Size(); c++) 117. { 118. st_Reg reg; 119. st_Bio bio; 120. 121. reg.Set(K[c], H[c]); 122. bio.Set(K[c], M[c], H[c]); 123. 124. Info_1.Set(reg); 125. Info_2.Set(bio); 126. } 127. 128. PrintX(Info_1.Get(3).Get_H()); 129. PrintX(Info_1.Get(13).Get_H()); 130. CheckBio(Info_2); 131. } 132. //+------------------------------------------------------------------+
Код 07
При выполнении кода 07 в терминале появится изображение, очень похожее на это.

Изображение 05
Боже мой! Какое же безумное и сумасшедшее творение мы создали! Успокойтесь, мои дорогие читатели, здесь нас интересует именно информация, выделенная на изображении 05, которая создалась с помощью процедуры в строке 89, на которой мы сосредоточимся в объяснении, поскольку большая часть кода легка для понимания. Понять данную процедуру в строке 89 крайне важно, чтобы разбираться в том, что будет рассмотрено в следующей статье.
Но перед тем, как объяснить происходящее в процедуре в строке 89, нам нужно разобраться в нескольких дополнительных моментах. Начнём с того, что в строке 26 создаётся новая структура. Следует отметить, что она имеет схожую основу со структурой st_Reg, но на этом сходство заканчивается, так как структура st_Bio содержит больше переменных и другой набор операций в своем контексте. Для нас важна именно функция из строки 44. Это объясняется тем, что она выполняет ту же функцию, что и строка 20, поскольку имеет те же цели и задачи. И это важно для того, что мы будем делать дальше.
Прошу также заметить то, что структура st_Data (из кода 07) в точности совпадает со структурой st_Reg в коде 06. Единственное реальное изменение — это наличие структуры st_Bio. После этого мы можем перейти к основной процедуре OnStart, которая является точкой входа в наш с вами код. Обратите внимание, что мы добавили сюда некоторые дополнительные элементы, которых не было в коде 06. Данные примеры как раз и призваны показать, насколько гибкой может стать структура st_Data, если тщательно продумать этап реализации кода.
По сути, мы добавили строки 107 и 111. А теперь, прошу сконцентрироваться. Обратите внимание, что разница между строками 110 и 111 заключается именно в типе данных, которые будут помещены в структуру st_Data. Поэтому внутри цикла в строке 116 мы добавляем новую переменную в строке 119, которая будет получать значения в строке 122, а в строке 125 мы присваиваем или добавляем новый фрагмент данных в структуру. В этом случае тип данных будет отличаться от типа данных в строке 124. Пока всё происходило так, как и предсказывалось в коде 06.
Однако именно здесь начинаются сложности, и в то же время, более интересные моменты: у нас есть строка 130, которая приведет к выполнению процедуры из строки 89. А теперь перейдём к самому интересному. Прошу заметить, что мы находимся внутри цикла в строке 94, и нас больше всего интересует строка 97, которую следует сравнить со строкой 128. В чем разница между ними? Практически никакой, за исключением того, что, поскольку строка 76 возвращает структурный тип, ссылающийся на тип данных переменной, объявленной в строке 62, между двумя строками нет никаких различий.
В статье «От начального до среднего уровня: Переменные (III)» мы объяснили, что функция — это особый тип переменной. Здесь данная концепция применяется очень интересным образом, поскольку, исходя из ответа, который мы получаем от функции в строке 76, мы можем указывать на тот или иной тип данных, в данном случае, на структуру. Без использования структурного программирования было бы практически невозможно (или чрезвычайно сложно) создать то, что делается здесь. Дело в том, что в строке 97 используется ссылка на структуру st_Bio. В строке 128 мы указываем на структуру st_Reg, и поскольку обе структуры находятся внутри структуры st_Data, мы можем обрабатывать различные типы информации или наборы данных одинаковым образом.
Чтобы понять, что здесь на самом деле происходит, вам нужно практиковаться и экспериментировать. Вы не сможете понять, просто взглянув на код и имея небольшой опыт в программировании. И помните: мы по-прежнему работаем с материалом, который, на мой взгляд, находится на базовом уровне.
Заключительные идеи
В сегодняшней статье было показано, как можно подходить к решению проблем, структурируя их и создавая более простое и привлекательное решение. Хотя представленный здесь материал носит образовательный характер и, следовательно, не является реальным кодом, необходимо тщательно усвоить изложенные здесь концепции и знания. Таким образом, в будущем вы сможете следить за кодом, который мы будем показывать, поскольку по мере продвижения мы сможем раскрывать более глубокие и интересные детали программирования, что было бы невозможно, если бы пришлось объяснять каждую строку кода.
Поэтому воспользуйтесь данной возможностью и внимательно изучите материал из индекса, без спешки. Изучайте и применяйте эти знания на практике, поскольку в следующей статье мы продолжим с того места, где остановились сегодня. Мы ещё не закончили разговор о структурном программировании и о том, как мы можем использовать его преимущества, прежде чем перейти к объектно-ориентированному программированию.
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/15912
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Автоматизация индикатора настроений рынка (индикатора сентимента)
Пользовательские инструменты отладки и профилирования для разработки на MQL5 (Часть I): Расширенное логирование
Искусство ведения логов (Часть 7): Как отображать логи на графике
Трейдинг с экономическим календарем MQL5 (Часть 9): Расширение интерактивности с новостями через динамический скроллбар и улучшенное отображение
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования