Español Português
preview
От начального до среднего уровня: Struct (II)

От начального до среднего уровня: Struct (II)

MetaTrader 5Примеры |
47 0
CODE X
CODE X

В предыдущей статье "От начального до среднего уровня: Struct (I)", мы начали разбираться с темой, которую, на мой взгляд, очень важно понять, поскольку это позволяет упростить выполнение многих вещей. Однако по-настоящему важно понять все детали, связанные с темой структур, потому что они находятся на полпути между объектно-ориентированным программированием и обычным программированием.

Но поскольку мы только начинаем данную тему, нам еще предстоит многое обсудить, прежде чем мы сможем сказать: "Да, я знаю, как работать со структурами".


Как использовать структуры в функциях и процедурах

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

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

Но, несмотря на то, что это возможно, НЕ БУДУ объяснять, как это сделать, поскольку это очень сложно и чревато ошибками. Поэтому давайте научимся делать всё правильно. Прежде всего, вы должны понимать, что не всегда необходимо передавать структуру между различными подпрограммами. То есть функция или процедура не должна знать, работает ли она со структурой данных или с дискретными значениями.

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     MqlDateTime dt;
07. 
08.     TimeToStruct(TimeLocal(), dt);
09. 
10.     Print(DateToString(dt.day, dt.mon, dt.year));
11.     Print(DateToString(3, 15, 2024));
12. }
13. //+------------------------------------------------------------------+
14. string DateToString(int day, int month, int year)
15. {
16.     if ((day < 1) || (day > 31) || (month < 1) || (month > 12))
17.         return "ERROR...";
18. 
19.     return StringFormat("%02d/%02d/%d", day, month, year);
20. }
21. //+------------------------------------------------------------------+

Код 01

При выполнении кода 01 получим следующий результат:

Изображение 01

Прошу заметить, что в коде 01 мы не используем ничего сложного или трудного для понимания. Однако, результат, полученный при запуске кода, конечно же, будет отличаться от того, что показан на изображении 01. Причина проста: использование функции TimeLocal. Данная функция стандартной библиотеки MQL5 предназначена для захвата текущего значения часов и возврата его в формате времени. Этот формат, который занимает восемь байт, содержит информацию о текущем времени и дате в месте, где запущен MetaTrader 5, и он исчисляется в секундах. Но для нас это не имеет значения, или не настолько, чтобы вдаваться в подробности. Однако то же самое значение, которое возвращает TimeLocal, используется функцией TimeToStruct для заполнения структуры MqlDateTime. И именно к этому мы хотим прийти. Структура MqlDateTime объявляется таким образом:

struct MqlDateTime 
  { 
   int year;
   int mon;
   int day;
   int hour;
   int min;
   int sec;
   int day_of_week;
   int day_of_year;
  };

Объявление MqlDateTime

Теперь обратите, пожалуйста, внимание на то, что будет объяснено, поскольку это важно для понимания того, что мы делаем в коде 01. Функция TimeToStruct разбивает 64-битное значение (известное как datetime) на значения, чтобы присвоить соответствующее значение каждой из переменных в структуре, показанной в приведенном выше объявлении. Есть и другие способы сделать это. Но в данном случае мы рассматриваем не этот вопрос. Важно то, что после присвоения значений мы можем их использовать так, как показано в строке 10 кода 01. Однако, прошу заметить, что это возможно только потому, что в функции в строке 14 мы используем примитивные аргументы для получения данных. И это позволяет нам отказаться от ненужных значений и использовать только те, которые нам нужны.

Но этот же подход позволяет нам использовать то, что показано в строке 11. Однако из-за теста в строке 16 мы в итоге заставим процедуру в строке 14 вернуть ошибку, так как НЕ СУЩЕСТВУЕТ возможности использовать год с пятнадцатью месяцами.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     MqlDateTime dt;
07. 
08.     TimeToStruct(TimeLocal(), dt);
09. 
10.     Print(DateToString(dt));
11.     dt.day  = 3;
12.     dt.mon  = 15;
13.     dt.year = 2024;
14.     Print(DateToString(dt));
15. }
16. //+------------------------------------------------------------------+
17. string DateToString(MqlDateTime &arg)
18. {
19.     if ((arg.day < 1) || (arg.day > 31) || (arg.mon < 1) || (arg.mon > 12))
20.         return "ERROR...";
21. 
22.     return StringFormat("%02d/%02d/%d", arg.day, arg.mon, arg.year);
23. }
24. //+------------------------------------------------------------------+

Код 02

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

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

Хорошо, но эти два кода, которые мы только что рассмотрели, относятся к определенному виду деятельности. Но нам может понадобиться создавать собственные структуры, хотя MQL5 уже предоставляет двенадцать различных типов предопределенных структур. О каждой из этих структур мы поговорим в другой раз, хотя об одной из них мы уже немного говорили: MqlDateTime. Однако, это была лишь краткая презентация того, что мы действительно можем и будем делать в дальнейшем.

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


Работа с пользовательскими структурами

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

Но прежде давайте разберем одну вещь: 

Структура - это особый тип данных, который может содержать любой вид информации.

Это очень важно, и вы должны правильно понять это. Когда вы поймете, что такое структура, то вам будет легче понять, как её реализовать и использовать в своем коде. Итак, вы знаете, что структура ничем не отличается от uchar, double или любого другого типа. Точно так же, как эти типы позволяют нам размещать содержащиеся в них значения и работать с ними, мы можем делать то же самое со структурами. Это аналогично тому, что было показано с объединениями, которые в данном случае будут не особым типом данных, а набором данных, разделяющих общую область памяти.

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

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     string      Name,
07.                 Surname,
08.                 Address,
09.                 Position;
10.     double      Remuneration;
11.     datetime    Birth,
12.                 Hiring;
13. 
                   .
                   .
                   .
            Registration routines
                   .
                   .
                   .

Код 03

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

Хорошо, как можно справиться с этим? На практике чаще всего приходится создавать массивы. Поскольку мы уже говорили о массивах, вам, вероятно, придет в голову создать нечто подобное тому, что показано ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06. #define def_Number_of_Records  10
07. 
08.     string      Name[def_Number_of_Records],
09.                 Surname[def_Number_of_Records],
10.                 Address[def_Number_of_Records],
11.                 Position[def_Number_of_Records];
12.     double      Remuneration[def_Number_of_Records];
13.     datetime    Birth[def_Number_of_Records],
14.                 Hiring[def_Number_of_Records];
15. 
                   .
                   .
                   .
            Registration routines
                   .
                   .
                   .

Код 04

Как видите, мы теперь можем работать с несколькими записями одновременно. Однако, хотя данная техника и работает, она довольно примитивна. Дело в том, что если нам нужно добавить поле или удалить его, то для простого редактирования придется проделать много работы. И я говорю только о применении, а не об использовании. Однако, глядя на код 04 и принимая во внимание то, что было ранее говорилось о структурах, можно подумать: "А не могли бы мы включить в структуру всё, что показано в коде 04? И именно в этот момент всё начинает приобретать смысл, потому что все сложности, которые мы видели в других кодах, сводятся к тому, что показано ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06. #define def_Number_of_Records  10
07. 
08.     struct st_Register
09.     {
10.         string      Name,
11.                     Surname,
12.                     Address,
13.                     Position;
14.         double      Remuneration;
15.         datetime    Birth,
16.                     Hiring;
17.     };
18.     
19.     st_Register Record[def_Number_of_Records];
20. 
                   .
                   .
                   .
            Registration routines
                   .
                   .
                   .

Код 05

Посмотрите, всё получилось естественным образом. Ведь для того, чтобы идея обрела форму и смысл, не обязательно было создавать серию приложений. Однако - и это самое интересное - структура, объявленная в строке 08 кода 05, будет доступна только в процедуре OnStart или в любом другом месте, где она была объявлена. В данном случае следует отметить, что структура st_Register не может быть использована для передачи чего-либо в функцию или процедуру, как это было сделано в коде 02, поэтому необходимо использовать подход, аналогичный коду 01, чтобы иметь возможность использовать любую информацию из структуры, объявленной в строке 08 кода 05.

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

Если вы хотя бы поверхностно изучали такие языки, как Python или JavaScript, то знаете, что в них очень часто используется что-то вроде этого:

cmd_entry.pack(fill='x', side='left' , expand=1)

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

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

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

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

Интересно, не так ли? Ведь не существует идеального способа сделать что-либо. Однако есть способы, которые могут быть более или менее интересными, в зависимости от каждого конкретного случая.

Давайте теперь сделаем следующее: предположим, что в коде 05 используется несколько функций и процедур, и в каждую из них нужно передать больше или меньше аргументов. Мы могли бы использовать подход кода 01, чтобы иметь контроль над каждым аргументом, но нам придется использовать перегрузку в некоторые моменты, и это неудобно. Однако можно использовать и подход с кодом 02, хотя в этом случае недостатком будет риск того, что какое-то значение будет изменено без нашего ведома. Но есть решение, которое во многих случаях действительно будет принято. Оно заключается в том, чтобы использовать функцию, когда мы хотим что-то изменить, и процедуру, когда мы хотим только что-то узнать.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. struct st_Register
05. {
06.     string      Name,
07.                 Surname,
08.                 Address,
09.                 Position;
10.     double      Remuneration;
11.     datetime    Birth,
12.                 Hiring;
13. };
14. //+------------------------------------------------------------------+
15. void OnStart(void)
16. {   
17.     st_Register Record[10];
18.     uchar       Counter = 0,
19.                 Index;
20. 
21.     Index = Counter;
22.     Counter += (NewRecord(Record[Counter], "Daniel", "Jose", "Brasil", "1971/03/30") ? 1 : 0);
23.     Record[Index] = UpdatePosition(Record[Index], "Chief Programmer");
24.     Index = Counter;
25.     Counter += (NewRecord(Record[Counter], "Edimarcos", "Alcantra", "Brasil", "1974/12/07") ? 1 : 0);
26.     Record[Index] = UpdatePosition(Record[Index], "Programmer");
27.     Index = Counter;
28.     Counter += (NewRecord(Record[Counter], "Carlos", "Almeida", "Brasil", "1985/11/15") ? 1 : 0);
29.     Record[Index] = UpdatePosition(Record[Index], "Junior Programmer");
30.     Index = Counter;
31.     Counter += (NewRecord(Record[Counter], "Yara", "Alves", "Brasil", "1978/07/25") ? 1 : 0);
32.     Record[Index] = UpdatePosition(Record[Index], "Accounting");
33. 
34.     Print("Number of records: ", Counter);
35.     ViewRecord(Record[3]);
36. }
37. //+------------------------------------------------------------------+
38. bool NewRecord(st_Register &arg, const string Name, const string Surname, const string Address, const string Birth)
39. {
40.     arg.Name = Name;
41.     arg.Surname = Surname;
42.     arg.Address = Address;
43.     arg.Birth = StringToTime(Birth);
44.     arg.Hiring = TimeLocal();
45. 
46.     return true;
47. }
48. //+------------------------------------------------------------------+
49. st_Register UpdatePosition(const st_Register &arg, const string NewPosition)
50. {
51.     st_Register info = arg;
52. 
53.     info.Position = NewPosition;
54. 
55.     return info;
56. }
57. //+------------------------------------------------------------------+
58. void ViewRecord(const st_Register &arg)
59. {
60.     PrintFormat("Collaborator: %s, %s\nPosition: %s\nBirth in: %s",
61.                     arg.Surname,
62.                     arg.Name,
63.                     arg.Position,
64.                     TimeToString(arg.Birth, TIME_DATE));
65. }
66. //+------------------------------------------------------------------+

Код 06

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

Изображение 03

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

В строке 04 мы объявляем нашу структуру данных. Обратите внимание, что она объявлена на глобальном уровне. По этой причине она может использоваться в нескольких точках за пределами основного блока кода, то есть в процедуре OnStart. Теперь, внутри основного блока, у нас есть объявление строки 17, целью которой является создание списка информации. То есть наша структура будет рассматриваться как особый вид переменной. Далее, между строками 21 и 32, мы можем добавить записи в наш список информации. Помните, что каждое значение в массиве будет выглядеть так, как будто это совершенно отдельный элемент. Однако, поскольку мы сорганизовали всё в одном блоке (структура, объявленная в строке 04), создается впечатление, что каждый элемент будет физически связан с остальными, создавая тем самым журнальный лист.

В каждом вызове между строками 21 и 32 мы пытаемся добавить новую запись в наш массив. Мы делаем это с помощью вызова строки 38. Прошу заметить, что мы предоставляем функции в строке 38 минимально необходимые привилегии. То есть мы предоставляем только некоторые данные, в то время как другие данные могут быть назначены непосредственно из самой подпрограммы, как это происходит в строке 44, где мы получаем текущую дату и используем ее для сообщения о времени заключения контракта. На практике это часто происходит, поскольку оформление нового сотрудника означает, что его принимают на работу. Поэтому мы можем разрешить функции в строке 38 присваивать значение возвращаемой структуре - значение, которое не передается в качестве аргумента в самом вызове.

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

Итак, хотя каждой записи нужны другие данные, которые не передаются напрямую в качестве аргументов функции NewRecord, присутствующей в строке 38, мы используем строку 49, чтобы обновить или присвоить какое-то конкретное значение уже созданной записи. В реальном коде здесь выполнится ряд тестов.

Однако цель функции в строке 49 - показать, как можно вернуть значение, которое в данном случае будет структурой, созданной в самой функции. Данная практика позволяет нам лучше контролировать, когда и где мы изменяем значения структуры.

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

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

Можно ли создать тот же код 06 таким образом, чтобы работа выполнялась немного по-другому? Да, и именно это мы видим в коде ниже:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. struct st_Register
005. {
006.     uchar       ID;
007.     string      Name,
008.                 Surname,
009.                 Address,
010.                 Position;
011.     double      Remuneration;
012.     datetime    Birth,
013.                 Hiring;
014. }gl_Record[10];
015. //+------------------------------------------------------------------+
016. void OnStart(void)
017. {
018.     uchar Index;
019. 
020.     ZeroMemory(gl_Record);
021. 
022.     NewCollaborator("Daniel", "Jose", "Brasil", "1971/03/30", "Chief Programmer");
023.     NewCollaborator("Edimarcos", "Alcantra", "Brasil", "1974/12/07", "Programmer");
024.     NewCollaborator("Carlos", "Almeida", "Brasil", "1985/11/15", "Junior Programmer");
025. 
026.     Index = GetNumberRecord();
027. 
028.     if (NewRecord(gl_Record[Index], "Yara", "Alves", "Brasil", "1978/07/25"))
029.     {
030.         gl_Record[Index].ID = Index + 1;
031.         gl_Record[Index] = UpdatePosition(gl_Record[Index], "Accounting");
032.     }
033. 
034.     Print("Number of records: ", GetNumberRecord());
035.     Print("--------------------");
036. 
037.     for(uchar c = 1; ViewRecord(c); c++)
038.         Print("********************");
039. }
040. //+------------------------------------------------------------------+
041. bool NewCollaborator(const string Name, const string Surname, const string Address, const string Birth, const string Position)
042. {
043.     st_Register info;
044.     uchar       Index = 0;
045. 
046.     if (!NewRecord(info, Name, Surname, Address, Birth))
047.         return false;
048. 
049.     info = UpdatePosition(info, Position);
050. 
051.     while (gl_Record[Index].ID)
052.     {
053.         if (Index >= gl_Record.Size()) return false;
054.         Index++;
055.     }
056. 
057.     info.ID = Index + 1;
058.     gl_Record[Index] = info;
059. 
060.     return true;
061. }
062. //+------------------------------------------------------------------+
063. bool NewRecord(st_Register &arg, const string Name, const string Surname, const string Address, const string Birth)
064. {
065.     arg.Name = Name;
066.     arg.Surname = Surname;
067.     arg.Address = Address;
068.     arg.Birth = StringToTime(Birth);
069.     arg.Hiring = TimeLocal();
070. 
071.     return true;
072. }
073. //+------------------------------------------------------------------+
074. st_Register UpdatePosition(const st_Register &arg, const string NewPosition)
075. {
076.     st_Register info = arg;
077. 
078.     info.Position = NewPosition;
079. 
080.     return info;
081. }
082. //+------------------------------------------------------------------+
083. uchar GetNumberRecord(void)
084. {
085.     uchar counter = 0;
086. 
087.     for (uchar c = 0; c < gl_Record.Size(); counter += (gl_Record[c].ID ? 1 : 0), c++);
088. 
089.     return counter;
090. }
091. //+------------------------------------------------------------------+
092. bool ViewRecord(const uchar ID)
093. {
094.     st_Register info;
095. 
096.     ZeroMemory(info);
097. 
098.     for (uchar c = 0; (c < gl_Record.Size()) && (!info.ID); c++)
099.         info = (gl_Record[c].ID == ID ? gl_Record[c] : info);
100. 
101.     if (info.ID)
102.         PrintFormat("Collaborator: %s, %s\nPosition: %s\nBirth in: %s",
103.                         info.Surname,
104.                         info.Name,
105.                         info.Position,
106.                         TimeToString(info.Birth, TIME_DATE));
107.     else
108.         Print("Record ID [", ID ,"] not found.");
109. 
110.     return (bool) info.ID;
111. }
112. //+------------------------------------------------------------------+

Код 07

«Теперь у нас на руках нечто действительно сложное. Код 07 безусловно сложнее, потому что я не понимаю ничего из того, что здесь делается». (СМЕХ). Ну что ж, в самом деле, код 07 очень прост. Если быть до конца честным, то, на мой взгляд, код 07 проще, чем код 06, хотя они, по сути, выполняют одно и то же. Однако я могу понять, что кому-то может показаться, что код 07 сложнее.

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

Изображение 04

Обратите внимание, что все записи, которые мы делаем в главном блоке, появляются на этом изображении, и это прекрасно, поскольку свидетельствует о том, что код 07 работает правильно. Но как это было сделали, если теперь появляются разные вещи? Что ж, мой дорогой читатель, как уже говорилось в предыдущей статье, структуры - это промежуточный шаг между обычным программированием и ООП. Но мы всё еще находимся в самом начале пути к тому, что можно сделать с этими структурами.

Однако прошу заметить, что теперь мы используем строки с 22 по 32 для включения новых записей в систему. Но то, как создаются записи между строками 22 и 24, поначалу легче понять, чем то, что происходит между строками 26 и 32. Здесь мы выполняем ту же работу, что и функция строки 41, только полностью вручную. Простое выполнение данного условия делает код более рискованным. Ведь если допустить ошибку, то можно потратить несколько часов на решение проблемы.

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

Однако, пожалуй, самой сложной частью данного кода является функция в строке 92, которую мы собираемся проанализировать. Во-первых, в строке 94 мы объявляем временную структуру. В строке 96 мы очищаем область памяти, в которой будет размещена структура. Таким образом, мы можем использовать тесты внутри цикла, показанного в строке 98, чтобы узнать, когда искомый идентификатор найдется.

Итак, в строке 101 мы можем проверить, есть ли что-нибудь, что нужно вывести в терминале MetaTrader 5. Если идентификатор найден, мы выведем содержимое структуры записи. В противном случае выведется сообщение в строке 108.

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


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

Сегодня мы рассмотрели, как использовать структуры в довольно простом коде. Хотя всё здесь направлено на обучение, нам удалось создать нечто, очень похожее на базу данных. Очевидно, что реальная база данных должна включать несколько тестов, чтобы избежать сбоев. Я считаю, что главная цель была достигнута - показать, как можно представить концепцию структур.

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

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

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

Прикрепленные файлы |
Anexo.zip (2.8 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Добавляем пользовательскую LLM в торгового робота (Часть 5): Разработка и тестирование торговой стратегии с помощью LLM(IV) — Тестирование торговой стратегии Добавляем пользовательскую LLM в торгового робота (Часть 5): Разработка и тестирование торговой стратегии с помощью LLM(IV) — Тестирование торговой стратегии
Языковые модели (LLM) являются важной частью быстро развивающегося искусственного интеллекта, поэтому нам следует подумать о том, как интегрировать мощные LLM в нашу алгоритмическую торговлю. Большинству людей сложно настроить эти модели в соответствии со своими потребностями, развернуть их локально, а затем применить к алгоритмической торговле. В этой серии статей будет рассмотрен пошаговый подход к достижению этой цели.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Нейросети в трейдинге: От трансформеров к спайковым нейронам (Окончание) Нейросети в трейдинге: От трансформеров к спайковым нейронам (Окончание)
Нейросети уже меняют подход к анализу рынков, а новые архитектуры открывают ещё больше возможностей. В статье мы завершаем работу с фреймворком SpikingBrain, который отрывает перед нами новые перспективы.