
Do básico ao intermediário: Struct (II)
No artigo anterior Do básico ao intermediário: Struct (I), iniciamos um dos assuntos, que ao meu ver, é muito importante que venha a ser compreendido da melhor maneira possível. Já que o mesmo nos permite fazer muitas coisas, de maneira bem mais simples. No entanto, a verdadeira importância em se entender, todos os detalhes envolvidos, quando o assunto é estruturas. É justamente pelo fato, de que estruturas esteja no meio do caminho, entre o que seria, uma programação orientada a objetos e programação convencional.
Mas como este assunto está apenas no começo. Ainda temos muito o que falar antes de realmente podermos dizer: Sim, já sei como trabalhar com estruturas.
Usando estruturas em funções e procedimentos
Uma das coisas que faz muito iniciante ficar pedido, ao lidar com estruturas, é a questão de não saber, se deve ou não passar valores usando estruturas. Pois bem. De fato esta é uma questão bastante intrigante e que em alguns momentos, acaba gerando mais dúvidas do que qualquer outra coisa. E o motivo, para aqueles que acham ser algo simples de resolver é justamente o fato de que, podemos passar variáveis por referência. Seja para uma função, seja para um procedimento. E quando isto é feito, precisamos tomar cuidado, ao trabalharmos com estruturas, neste tipo de cenário.
Eu que sou das antigas, vivi uma época em que na linguagem C, não podíamos transferir dados via estrutura. Isto de maneira direta. Hoje já é possível fazer isto, mas houve uma época, em que precisamos utilizar outros tipos de mecanismos para efetivar tais transferências. Neste tipo de cenário, as possibilidades de erro, crescem a medida em que as coisas vão ficando com cada vez mais variáveis dentro da estrutura. Mas isto é passado. Hoje temos mecanismos mais seguros para efetuar o mesmo tipo de transferência. Porém isto não lhe impede de usar antigas técnicas de implementação de código. Onde a segurança não é o foco principal. Mas sim a velocidade de processamento das informações.
No entanto, apenas de ser possível fazer tais coisas, NÃO IREI mostrar como as implementar. Visto que a dificuldade e o risco de erros são muito grandes. Portanto, vamos aprender como fazer as coisas da forma correta. Primeiro, você meu caro leitor, precisa compreender, que nem sempre uma estrutura, precisa de fato ser passada entre rotinas diferentes. Ou seja, uma função ou procedimento, não necessariamente precisa saber que está lidando com uma estrutura de dados, ou se está trabalhando com valores discretos.
De certa maneira, quando usamos os mecanismos já mostrados e explicados em outros artigos. Podemos deixar o código muito mais flexível e com uma usabilidade muito maior. Visto que no momento em que passamos a utilizar estruturas, podemos acabar limitando certas atividades de uma determinada rotina.
Para entender isto, vamos ver um primeiro código bem simples e que é mostrado logo abaixo.
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. //+------------------------------------------------------------------+
Código 01
Quando executado este código 01, irá produzir o resultado que pode ser observado logo na sequência.
Imagem 01
Observe que aqui no código 01, praticamente não estamos utilizando coisas muito complexas e difíceis de se entender. Mas o resultado que será visto, quando você executar este código 01. Irá com toda a certeza ser diferente do que é mostrado na imagem 01. E o motivo para isto é simples. O uso da chamada TimeLocal. Esta função da biblioteca padrão do MQL5, tem como objetivo, capturar o valor atual do relógio e o retornar isto em formato datetime. Este formato, que seria um tipo contendo oito bytes, tem informações sobre a hora e o dia atual, do local onde o MetaTrader 5 está sendo executado. Claro que este tempo é contabilizado em termos de segundos. Mas isto não nos importa. Não a ponto de precisamos entrar em mais detalhes sobre isto. Porém, este mesmo valor, que TimeLocal, está retornando, é utilizado pela função TimetoStruct, para preenchimento da estrutura MqlDateTime. E é aqui onde realmente queremos chegar. A estrutura MqlDateTime, é declarada como mostrado logo abaixo.
struct MqlDateTime { int year; int mon; int day; int hour; int min; int sec; int day_of_week; int day_of_year; };
Declaração de MqlDateTime
Agora preste atenção ao que irei explicar, pois isto é importante que entender o que estamos fazendo no código 01. A função TimetoStruct, irá decompor o valor de 64 bits, que conhecemos como sendo o tipo datetime. Em valores, isto a fim de atribuir, a cada uma das variáveis presentes na estrutura mostrada na declaração acima, um valor adequado. Existem outras formas de se fazer isto. Porém não é esta a questão a ser tratada aqui. A questão é que, uma vez atribuídos os valores, podemos utilizar eles como mostrado na linha dez do código 01. Porém note que isto somente foi possível devido justamente ao fato de que, na função da linha 14, estamos utilizando argumentos do tipo primitivo, para receber os dados. Isto nos permite, abrir mão de valores desnecessários, e apenas utilizar valores que realmente precisamos.
No entanto, esta mesma abordagem, também nos permite fazer uso do que é visto na linha 11. Mas, devido ao teste na linha 16, acabamos fazendo a rotina da linha 14, retornar um erro. Já que NÃO TEMOS a possibilidade de um ano com quinze meses.
É neste tipo de coisa, que eu estava querendo chegar. Já que esta mesma abordagem que está sendo mostrada aqui, no código 01. Poderia ser implementada de diversas maneiras diferentes. Como por exemplo, o que pode ser observado no código 02 logo abaixo.
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. //+------------------------------------------------------------------+
Código 02
Neste caso, teríamos o mesmo tipo de resultado que é visto na imagem 01. Porém, seria muito mais fácil entender o motivo do erro. Já que olhando a linha doze neste código 02. Claramente percebemos o que está errado. Porém, perceba o fato de que, na linha 17, foi necessário passar a estrutura por meio de referência. Isto pode ser um problema, mas também é facilmente contornado. Já que apenas precisamos fazer com que o argumento recebido, seja visto como sendo uma constante. Como fazer isto, já foi explicado em outros artigos. De qualquer forma, existe sim o fardo de que a função, agora está tendo muito mais privilégios do que havia antes. Já que ela pode visualizar, mais dados do que o necessário para seu funcionamento. E é por isto, que não existe uma resposta totalmente adequada de como podemos e devemos de fato utilizar estruturas em funções ou procedimentos. Já que você pode estar passando muito mais informações do que realmente seria de fato necessário repassar.
Este tipo de conceito, que é o do mínimo conhecimento de que uma função ou procedimento, deve ter sobre outros dados. É muito mais utilizado e mencionado, quando estivermos falando sobre classes e objetos. Isto por que ali, na programação orientada em objetos. Este tipo de conceito se faz muito mais presente do que você pode estar imaginando, em caro leitor. Mas isto é assunto para o futuro. Por hora, você precisa entender, que quanto menos uma rotina, seja ela um procedimento ou função, precisar saber. Melhor. Já que precisamos declarar e repassar menos informações, o que agiliza a implementação e futuras correções e melhorias no código.
Ok, mas estes dois códigos, que acabamos de ver, estão relacionados a um determinado tipo de atividade. Porém, não é raro, você de fato precisar criar estruturas personalizadas. Mesmo que aqui, o MQL5, já venha a nos fornecer doze tipos diferentes de estruturas, previamente definidas. Em outro momento futuro, iremos abordar cada uma destas estruturas. Apesar de já termos falado um pouco a respeito de uma delas. Que no caso foi a MqlDateTime. Mas esta foi apenas uma breve apresentação do que realmente podemos e iremos fazer futuramente.
Muito bem, mas agora pode ser que você, meu caro leitor, esteja na dúvida sobre uma coisa: Como faço para implementar algo, que venha a utilizar uma estrutura, que eu tenha criado? Bem, para responder isto de maneira adequada, vamos criar um novo tópico. Já que não quero misturar um assunto com outro. Apesar de que, a resposta tem alguma relação com este tópico atual. Não quero misturar as coisas.
Trabalhando com estruturas personalizadas
Apesar da resposta, sobre o que foi questionado no final do tópico anterior, ter muito a ver com aquele tópico. Quero de fato separar as coisas, a fim de tornar o material o mais didático possível. Isto por conta de que, quando usamos uma das doze estruturas previamente definidas no MQL5. Podemos implementar as coisas de uma dada maneira. No entanto, quando iremos implementar algo, fazendo uso de alguma estrutura que tenhamos criado. Precisamos implementar as coisas, muitas vezes de uma outra maneira. Isto devido a questões que foram explicadas no artigo anterior, e no tópico anterior. Mas existe uma outra, que iremos abordar em breve.
Mas antes disto, vamos entender uma coisa:
Uma estrutura, é um tipo especial de dado. Que pode conter qualquer informação.
Esta frase, dita acima, é muito importante ser corretamente compreendida. Isto porque, de fato, quando você compreende o que seria realmente uma estrutura, fica mais simples entender como implementar e a utilizar em seus códigos. Então, meu caro leitor, entenda que uma estrutura, NÃO É diferente de um tipo uchar, double ou outro qualquer. Assim como estes tipos permitem que coloquemos e venhamos a trabalhar com valores contidos neles. Podemos fazer o mesmo tipo de coisa, fazendo uso de estruturas. Mais ou menos como foi mostrado para as uniões, que neste caso, não seriam de fato um tipo especial de dado. Mas sim um conjunto de dados, compartilhando uma região comum de memória.
Para tornar a ideia de que uma estrutura, seria de fato um tipo especial de dado. Vamos pensar em registros cadastrais. Em um registro cadastral, podemos ter diversas informações presentes. Como por exemplo: Nome, sobrenome, endereço, profissão, contatos, entre outros. Enfim, todos estes dados poderiam ser organizados de maneira, a terem o mesmo formato base em termos de registro cadastral. Tornando assim possível manipular os registros de maneira muito simples e prática.
Basicamente existem duas maneiras de se fazer isto. A primeira é criar uma série de variáveis para conter as informações que queremos e precisamos manter em registro. E estas mesmas variáveis estariam dispersas por todo o código. Seria mais ou menos como podemos ver logo abaixo. Claro que isto é apenas um exemplo hipotético.
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 . . .
Código 03
A não ser que você esteja criando uma aplicação cujo proposito seria o de lidar com apenas e somente um único registro. Dificilmente um código real teria uma aparência como a vista em código 03. Porém, pode ser que você venha de fato a ter um código com a aparência vista no código 03. Desde que os registros estejam sendo armazenados em disco. Mas neste ponto surge alguns problemas, entre eles a ordem em que as variáveis precisam ser armazenadas. Porém este problema não é o que de fato nos interessa neste momento. E sim outro, que seria o de lidar com cruzamento ou relações entre dados de diferentes registros.
Ok, como você poderia lidar com isto? Bem, na prática, e sendo a coisa mais comum, seria criar arrays. Como já falamos sobre arrays, você logo iria pensar em criar algo do tipo mostrado logo abaixo.
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 . . .
Código 04
Observe que agora temos a possibilidade de trabalhar com mais de um registro ao mesmo tempo. Porém, apesar desta técnica funcionar, ela é um tanto quanto desajeitada. Isto pelo motivo de que se por ventura, precisarmos adicionar mais algum campo, ou remover o mesmo, teremos muito trabalho para uma simples edição. E olha que estou falando apenas em termos de aplicação, não em termos de uso da aplicação em si. Porém olhando este código 04, e tendo em vista o que foi mostrado até este momento, sobre estruturas. Você pensa: Será que não podemos colocar tudo isto, visto no código 04, em uma estrutura? E é justamente neste ponto em que a coisa começa a de fato fazer sentido. Isto porque, toda aquela complicação vista nos demais códigos, passa a ser algo como visto logo abaixo.
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 . . .
Código 05
Note como naturalmente a coisa foi surgindo. Não precisamos de fato criar toda uma série de aplicações para que a ideia tomasse corpo e que viesse de fato a fazer sentido. Porém, e é agora que a diversão realmente começa, a estrutura declarada na linha oito deste código 05 somente será acessível dentro do procedimento OnStart. Ou onde ela estiver sido declarada. Neste caso você deverá entender que a estrutura st_Register, de fato não pode ser utilizada para passar algo para uma função ou procedimento. Assim como era feito no código 02. Sendo necessário adotar uma abordagem muito similar à vista no código 01, para de fato poder utilizar qualquer informação presente na estrutura declarada na linha oito do código 05.
Isto em alguns momentos é um tanto quanto problemático. Porém em outros, pode de fato ser o que precisamos. De qualquer forma, no geral, estruturas, diferente das uniões, são declaradas com um âmbito global. Isto justamente para nos permitir transferir dados de maneira mais agradável e prática.
Quem já estudou, mesmo que de maneira bem superficial, linguagens como Python ou Java Script, sabem que é muito comum usarmos algo como visto logo abaixo.
cmd_entry.pack(fill='x', side='left' , expand=1)
Nesta linha de código visto acima, que tanto pode ser em Python, como em Java Script, temos algo que para quem utiliza MQL5, C ou C++, não faz o menor sentido. Isto porque, quando você observa algum valor sendo atribuído a uma variável, em uma chamada, você de fato, estará atribuindo um valor a uma variável presente no código. Seja uma variável global, seja uma variável local. Porém, de qualquer forma, se você procurar está variável ela de fato irá ser encontrada em algum destes âmbitos. Isto falando em termos de MQL5, C ou C++.
No entanto, se este mesmo trecho é visto em um código Python ou Java Script, não estamos de fato atribuindo um valor a uma variável. Sei que isto parece completamente ilógico e sem sentido. Mas estamos de fato, atribuindo valor a algo semelhante a uma estrutura. Devido justamente a este fato, é que podemos atribuir valores a qualquer elemento, sem uma ordem previamente estabelecida. Coisa que seria obrigatória em MQL5 por exemplo.
Porém, toda via e, entretanto, se olharmos este mesmo código, que poderia estar escrito em Python, ou em Java Script, como algo semelhante a uma estrutura escrita em MQL5. Tudo passaria a fazer sentido. O que acabaria tornando a passagem de valores entre funções e procedimentos algo muito mais simples e agradável. Ainda mais se forem muitos argumentos a serem repassados.
Agora preste atenção, pois isto pode fazer diferença, na forma como você irá abordar diferentes problemas. Em alguns artigos anteriores, falamos sobre o fato de podemos criar sobrecarga de funções e procedimentos. Isto tanto para que pudéssemos trabalhar com tipos diferentes, quanto também, quantidades diferentes de argumentos em uma função ou procedimento. Mas, se em alguns destes momentos utilizássemos estruturas para passar tais valores, alguns casos onde a sobrecarga precisaria ser feita, já não mais precisaria. Isto porque, podemos passar um número arbitrário de argumentos, apenas pelo simples fato de estarem em uma estrutura.
Ficou curioso, não é mesmo? Pois de fato, não existe uma forma perfeita, e derradeira de se fazer algo. Porém existem maneiras que podem ser mais ou menos interessante. Dependendo claro de cada caso específico.
Então vamos supor o seguinte: Suponhamos que o código 05, de fato, venha a utilizar diversas funções e procedimentos. E em que cada um venhamos a precisar, de mais ou menos argumentos sendo repassados. Você poderia utilizar a abordagem vista no código 01, e ter um controle sobre cada um dos argumentos. Mas ao custo de que pode vir a necessitar utilizar a sobrecarga em diversos momento. Mas também poderia adotar a abordagem vista no código 02. Mas neste caso o custo seria o risco de ter algum valor sendo modificado sem que realmente você note isto acontecendo. No entanto, existe uma solução que em muitos casos seria de fato adotada. Esta solução envolve justamente o fato de usarmos uma função, quando queremos modificar algo. E utilizar um procedimento quanto NÃO queremos modificar algo, mas apenas pesquisar ali.
De qualquer maneira, a estrutura declarada na linha oito do código 05, precisaria sair do âmbito local e passar a ser adotada no âmbito global. Assim como pode ser visto, no código hipotético logo abaixo.
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. //+------------------------------------------------------------------+
Código 06
Sei que este código 06, é algo bem subjetivo. Sendo algo que muitos acham pouco provável que de fato venha a ser criado. Mas apesar de ser apenas um exemplo hipotético. Ele contém muitos elementos e questões de um código que você irá ver no mundo real. Quando executado, ele irá gerar o que é visto na imagem logo abaixo.
Imagem 03
Agora observe que aqui temos diversas coisas sendo feitas. E mesmo que para alguns, este código possa parecer algo muito complicado e difícil de entender. Para quem vem estudando e praticando o que vem sendo mostrado nos artigos, este código é até bastante simples, claro e direto. Mas como aqui temos alguns elementos que podem ser um tanto quanto estranhos. Como por exemplo, as datas de nascimento. Vou dar uma rápida explicação sobre o que está acontecendo aqui.
Pois bem, na linha quatro, declaramos a nossa estrutura de dados. Note que ela está sendo declarada em âmbito global. Por conta disto, poderá ser utilizada em diversos pontos fora do que seria o bloco de código principal. Ou seja, o procedimento OnStart. Ok, agora, dentro deste bloco principal, temos a declaração na linha 17, cujo objetivo é o de criar uma lista de informações. Ou seja, a nossa estrutura será de fato tratada como se fosse um tipo de variável especial. Feito isto, podemos entre as linhas 21 e 32 adicionar alguns registros a nossa lista de informações. Lembre-se de que cada valor, dentro do array, seria como se fosse um elemento completamente distinto. Mas mesmo assim, devido ao fato de que estamos estruturando tudo em um único bloco, que é a estrutura declarada na linha quatro. Temos a impressão de que cada elemento estaria fisicamente ligado a outros. Criando assim o que seria uma folha de registro.
Então a cada chamada entre as linhas 21 e 32, primeiro tentamos adicionar um novo registro ao nosso array. Isto através da chamada da linha 38. Observe que, estamos dando a função da linha 38, o mínimo de privilégios necessários. Ou seja, estamos apenas informando a ela, algumas informações, enquanto outras podem ser atribuídas diretamente pela própria rotina. Como é o caso da linha 44. Onde pegamos a data atual e usamos ela para informar o momento da contratação. Isto na realidade de fato, muitas vezes é feito. Já que no momento em que um novo registro de colaborador acontece, é o momento em que ele de fato está sendo contrato. Por conta disto, podemos permitir que a função da linha 38, atribuía um valor a estrutura de retorno. Valor este que não está sendo repassado como sendo um argumento da própria chamada.
Naturalmente, existe em um código real, uma serie e testes a fim de garantir que não venha a ocorrer erros durante a fase de registro. Mas como este código é apenas didático. Tais testes se tornam completamente desnecessários.
Muito bem, como cada registro, necessita de outros dados, que não estão sendo postos diretamente como argumentos para a função NewRecord presente na linha 38. Usamos a linha 49, para atualizar, ou atribuir algum valor especifico ao registro já efetuado. Novamente, em um código real, haveria vários testes sendo feitos aqui.
Porém, o objetivo nesta função da linha 49, é justamente mostrar, como poderíamos retornar um valor, que neste caso seria a própria estrutura gerada dentro da função. Tal prática, nos permite ter um controle mais refinado, sobre quando e onde iremos mudar valores presentes em uma estrutura.
Pois observe que diferente do que acontecia na linha 38, onde a estrutura de entrada era modificada de maneira permanente, aqui na linha 49. Modificamos a estrutura e retornamos isto para o chamador. Depende do chamador, encontrar o local adequado para utilizar o que foi retornado pela função. E sim meu caro leitor, você pode utilizar este retorno de diversas maneiras como será visto depois.
De qualquer maneira, no final, temos um pequeno procedimento para fins de visualizar algum registro. Com isto temos o que podemos visualizar na imagem 03.
Agora, será que poderíamos criar o mesmo código 06, só que de maneira a termos o trabalho sendo feito de uma forma um pouco diferente? Sim. E é justamente isto que podemos ver no código logo abaixo.
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. //+------------------------------------------------------------------+
Código 07
Ok, agora temos realmente algo muito complicado nas mãos. Isto por que este código 07 é bastante mais complexo. Já que estou entendendo muito pouco do que está sendo feito aqui. ( RISOS ). Bem meu caro leitor, lá no fundo e bem lá no fundo, este código 07 é muito simples. Para falar e ser verdadeiramente honesto, ao meu ver este código 07 é mais simples do que o código 06. Apesar de fazerem basicamente a mesma coisa. Porém, entendo o fato de que um, ou outro, possam estar imaginando que este código 07 é mais complicado.
Primeiro pelo fato de que agora temos uma declaração de variável global sendo feita na linha 14. E o simples fato de isto estar acontecendo já torna qualquer coisa bem mais complicada do que seria naturalmente exigido. Mas antes de falarmos um pouco mais sobre o que este código 07 está fazendo. Vamos ver o resultado da execução do mesmo. Isto pode ser observado na imagem logo na sequência.
Imagem 04
Note que de fato, todos os registros que estamos fazendo dentro do bloco principal, aparecem nesta imagem. O que é perfeito. Já que isto indica que de fato o código 07 está cumprindo o seu papel. Mas como isto foi feito. Já que agora temos coisas diferentes aparecendo ali? Bem, meu caro leitor, como foi dito no artigo anterior, estruturas é um passo que fica entre uma programação convencional e uma programação orientada em objetos. Porém, ainda estamos bem no começo do que realmente podemos fazer em temos de estruturas.
No entanto, observe que agora, estamos usando as linhas 22 até 32 para incluir novos registros no sistema. Mas, e este talvez seja o ponto chave. A forma como os registros estão sendo feitos entre as linhas 22 e 24 são mais simples de entender a princípio. Diferente do que está acontecendo entre as linhas 26 até a linha 32. Pois ali estamos fazendo o mesmo trabalho que é feito pela função da linha 41, só que de maneira completamente manual. O simples fato de estarmos fazendo assim, tornar o código um tanto quanto mais arriscado. Isto pelo fato de que se você cometer algum deslise. Pronto. Lá se foram horas tentando resolver o problema.
Mas quero que você procure estudar este código 07 a fim de que consiga compreender isto de maneira prática. E compare o nível de dificuldade em se gerar algum resultado usando o código 06 com o a dificuldade de gerar o mesmo tipo de resultado usando o código 07. Entender isto, irá tornar mais simples entender o que será visto no próximo artigo.
Porém, e talvez a parte mais complexa deste código 07, seja justamente a função da linha 92. Vamos entender com ela funciona. Primeiro, declaramos na linha 94 uma estrutura temporária. Já na linha 96, limpamos a região da memória onde a estrutura irá ser colocada. Com isto, podemos utilizar os testes no laço visto na linha 98, a fim de saber, quando a ID procurada foi encontrada.
Finalizando assim o laço, de modo que na linha 101 podemos checar se existe ou não algo a ser impresso no terminal do MetaTrader 5. Caso a ID tenha sido encontrada, iremos imprimir o conteúdo presente na estrutura de registro. Caso contrário, iremos imprimir a mensagem vista na linha 108.
Contudo, como o retorno desta função precisa ser do tipo booleano, e a ID é um valor numérico. Dizemos ao compilador, que estamos cientes deste fato. Assim na linha 110, usamos uma conversão explicita de tipo para retornar ao chamador, se tivemos sucesso ou não na busca pela ID requerida. Este retorno é importante para que o laço da linha 37, saiba quanto é hora de parar.
Considerações finais
Neste artigo vimos como utilizar estruturas em códigos bastante simples. Apesar de que tudo aqui, visa basicamente a didática. Pudemos construir, algo que se assemelha imensamente ao que seria um banco de dados. É bem verdade que um banco de dados real, precisaria incluir diversos testes a fim de evitar falhas no mesmo. Porém, acredito que o objetivo principal foi alcançado. Que seria justamente mostrar como você deve pensar o conceito de estruturas.
Ou seja, uma estrutura é de fato uma variável do tipo especial. Então, meu caro leitor, procure estudar e praticar com os códigos que estarão no anexo. Apesar de não ter sido feita uma explicação minuciosa sobre cada linha de código visto aqui. Imagino que isto de fato não seria necessário. Já que todo o conteúdo presente aqui é fácil de ser entendido por quem vem estudando e praticando o que está sendo mostrado nos artigos.
No próximo artigo iremos ver como as estruturas podem ser utilizadas de uma maneira ainda mais prática e interessante. Tornando o trabalho de codificação ainda mais simples.
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso