
Do básico ao intermediário: Estruturas (VII)
Introdução
No artigo anterior Do básico ao intermediário: Estruturas (VI), vimos como poderíamos começar a criar o que seria uma implementação genérica de uma estrutura de dados simples. Apesar de parecer um tanto quanto pouco convencional. Aquele tipo de modelagem, é muito mais utilizada do que grande parte das pessoas pensam. Isto porque, grandes bancos de dados, utilizam um princípio muito parecido a fim de efetuar pesquisas e organizar os próprios dados.
Sei que este assunto, que está sendo abordado nestes artigos, são o que muitos podem considerar desnecessário. Porém, vale lembrar de que estou visando explicar as coisas, de forma que no futuro, não venha a ser preciso explicar certos detalhes, que ao meu ver, são triviais. No entanto, como grande parte de vocês, fazem destes artigos, meios de aprender com a experiência e conhecimento de outros programadores, autores dos artigos. Todo conhecimento e experiência, que eu puder vir a conseguir passar neste início, será de grande ajudar para você no futuro, meu caro leitor.
Isto porque, não adianta você apenas ver um código funcionando. É preciso entender por que ele funciona. E caso venha a ser necessário, saber como adequar a implementação feita por outro programador, ao seu nível de necessidade. E para saber fazer isto, é necessário conhecer conceitos e entender como eles podem ser aplicados.
Como no artigo anterior, começamos a implementar algo bastante interessante, visando entender como estruturas podem ser usadas dentro de outras estruturas. Acredito que você tenha ficado um tanto quanto intrigado com o resultado visto no último código. Isto porque, usamos uma estrutura criada para um objetivo, e acabamos no final tornando possível usar ela com um outro objetivo. No caso, inicialmente os dados eram do tipo double, e no final, podíamos usar dados de qualquer tipo.
Porém, aquilo que foi visto, não é onde realmente quero chegar. Para isto, ainda precisamos caminhar um pouco mais. No entanto, todo este esforço, irá realmente valer a pena e será fortemente recompensado futuramente. Quanto a isto, posso lhe garantir meu caro leitor. Muito bem, então vamos começar de onde paramos no artigo anterior.
Estruturas de estruturas, parte dois, o retorno
Para começar, no artigo anterior, terminamos com um código que é visto logo abaixo.
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. //+------------------------------------------------------------------+
Código 01
Este código 01, apesar de funcionar, é um tanto quanto chato. Isto pelo simples motivo, de que no momento em que viermos a pedir para compilar o mesmo, iremos receber como resposta algo parecido com o que é mostrado logo abaixo.
Imagem 01
Este tipo de coisa que podemos ver na imagem 01, é um tanto quanto incomodo. Isto porque, dependendo do tipo de coisa que estamos implementando. Ter mensagens de alerta do compilador, pode acabar gerando falhas futuras no código. Não por este código 01 está incorreto. Mas sim por conta de que, o compilador pode estar nos alertando, sobre uma possível falha critica. E por conta do fato de que estamos ignorando, mensagens de falhas, devido justamente a saber que algumas mensagens podem ser ignoradas. Mas ao fazermos isto, podemos acabar ignorando uma mensagem realmente importante.
De qualquer forma, quando este código 01 é executado. Teremos como resposta o que pode ser visto na imagem logo abaixo.
Imagem 02
Legal, não é mesmo? Mas antes de continuarmos, vamos fazer uma coisa. Que é justamente resolver esta questão mostrada na imagem 01. Existem algumas formas de se resolver isto. Porém todas acabam da mesma maneira. Ou seja, usando uma conversão explicita. Assim a forma de se chegar de fato ao ponto da conversão explicita não importa. O que importa é que ela precisa ser efetivada. Para conseguir isto, bastará mudar o código 01, para algo parecido com o que é visto logo abaixo.
. . . 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. //+----------------+ . . .
Código 02
Provavelmente você deve estar um tanto quanto decepcionado, ao ver que a solução para resolver o problema visto na imagem 01, é este fragmento que mostro no código 02. Muitos talvez estariam na expectativa que seria criado todo um conjunto de instruções e funções. Para no final, tudo que precisamos fazer é mostrado neste código 02. É faz parte. O tamanho da frustração é proporcional ao nível de expectativa que tínhamos com relação a algo. Brincadeiras à parte. Vamos voltar a questão do código a fim de entender como tornar ele de fato mais extensivo a ponto de conseguir lidar com dados diferentes sem grandes mudanças.
Talvez você esteja pensando que fazer isto seria algo muito complicado. Porém, antes de vermos como isto poderia ser feito. Preciso que você de fato tenha compreendido, a ideia de como e porque, ligar um elemento de um array a um outro elemento presente em outro array.
O conceito em si é muito simples. Pois estaríamos simplesmente criando uma tradução. Ou seja, dada uma chave que seria o elemento de um array, teríamos uma resposta que seria basicamente o elemento presente em um outro array. É somente isto que estamos fazendo, com este código 01. Mas então por que não usar um array multidimensional para isto? O motivo é que apesar do array multidimensional ser a melhor proposta neste caso. Ela de fato não é adequada, devido ao fato de que estamos usando esta construção para demonstrar algo. Ou melhor dizendo, demonstrar como trabalhar com um dado tipo de recurso da linguagem.
Beleza, agora que acredito, ter deixado bastante claro o que estamos fazendo. Quero que você olhe novamente para o código 01 e me diga: O que está atravancando o código, impedindo-o de poder ser usado não apenas na tradução de valores, mas também para outras coisas com um contexto parecido?
Pode pensar, não tenha pressa em tentar chegar a uma conclusão. Já que aqui, irei mostrar uma de tantas maneiras de liberar o código a fim de podermos colocar qualquer coisa dentro dele. Mas, no momento em que você tentar expandir as coisas, acabará notando que o problema é a estrutura declarada na linha dez. Este é o problema. Mas não da forma como você possa estar pensando.
O problema da estrutura da linha dez, é que ela é a responsável por travar, ou evitar um uso mais genérico da estrutura principal. Como sei que entender isto, é algo muito complicado e ao mesmo tempo, demanda tempo. Vou continuar o artigo, mas peço a você meu caro leitor, que leia e releia este artigo, e os anteriores a fim de compreender uma coisa.
Estruturas assim como outros tipos de construção, são apenas formas de representação de dados, mais ou menos complexos. A forma como você precisará lidar com um problema, depende de qual complexo o problema de fato é. Alguns problemas podem ser resolvidos, utilizando para isto, tipos discretos e simples, como representações em ponto flutuante ou em valores inteiros.
Quando o problema começa a ficar mais complexo, tipos discretos, podem não ser o suficiente. Neste caso, precisamos de tipos de dados mais complexos, como arrays e uniões. E se ainda assim o problema continua complexo, precisamos de um tipo igualmente complexo, que neste caso seria estruturas.
Assim sendo, a estrutura que estamos declarando na linha dez do código 01. Nada mais é do que um tipo complexo. E preciso que você entenda isto para conseguir entender o que iremos fazer.
Primeiro precisamos remover esta estrutura da linha dez para fora da estrutura definida na linha quatro. Isto olhando ainda o código 01. Porém ao fazermos isto, estaremos criando um emaranhado de informações que se você não entendeu, ou não conseguiu entender os artigos anteriores, irá ficar mais perdido que cachorro que caiu do caminhão de mudança. Isto por que, não tem como promover a mudança que será feita, aos poucos. Ela precisa ser feita em um número muito pequeno de passos. Sendo o primeiro passo, visto logo abaixo.
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. //+------------------------------------------------------------------+
Código 03
Preste atenção meu caro leitor, para tornar o código e o que iremos fazer, o mais simples e didático possível. Dei um passo para trás a fim de que o tipo usado aqui fosse, o tipo string. Note que agora não existe mais a sobrecarga de estrutura sendo criada. E o resultado da execução continua sendo o que é visto na imagem 02.
Você pode estar olhando este código 03 e dizendo: Está bem, você disse que era complicado, mas até agora está tudo como era antes. Ou seja, tudo muito simples. Cadê a tal complicação? Certo, então vamos começar. O segundo ponto a ser mudando é o fato de que tanto a função Set, quando a função Get, que você está vendo das linhas 19 e 34, respectivamente, NÃO SÃO FUNÇÕES DE st_Data, mas sim de st_Reg.
Agora pergunto: O que elas estão fazendo em st_Data? Resposta: Elas estão atrapalhando a estrutura de funcionar de maneira mais genérica. Podendo assim lidar com qualquer tipo de dado. Então precisamos remover estas funções da estrutura st_Data, vista no código 03. E para tornar as coisas simples, veja como ficaria o fragmento corrigido. Isto é mostrado logo abaixo.
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. //+------------------------------------------------------------------+ . . .
Código 04
Mas que doideira é esta? Calma meu caro leitor, a coisa só tá começando a ficar animada. Agora temos não uma, mas duas estruturas a fim de conseguir consolidar os dados dentro do que seria a nova estrutura st_Data. Mas antes de prosseguirmos para o código principal, ou seja, o código que estará em OnStart. Precisamos entender o que está acontecendo aqui.
Pois de fato, apesar deste fragmento mostrado no código 04, funcionar da mesma maneira que o código 03. Aqui temos diversas coisas que são completamente diferentes daquilo que você está acostumado a lidar. Mas ainda assim, ao meu ver isto ainda é um código de nível básico. Já que estamos lidando com coisas muito simples aqui.
Para começar, você deve perceber que st_Reg, agora é uma estrutura pensada para lidar e trabalhar com um código estrutural. Ou seja, não estaremos entrando diretamente em contato com as variáveis. Mas sim estaremos criando um conceito, ou contexto, no qual podemos lidar com as variáveis presentes na estrutura st_Reg.
Basicamente st_Reg, funciona como era antes de começarmos a falar de estruturas de estruturas. Agora vem a parte que complica a vida de muito iniciante. Ainda mais se ele não tem os conceitos corretos sobre o que seria uma variável e como lidar com tipos de dados. Então vamos entender o que está acontecendo nesta segunda estrutura, que é st_Data. Sendo st_Reg, um tipo complexo de dado. Podemos na linha 31 declarar uma variável, que no caso é um array, para conter este tipo de dado, que é st_Reg. Até aí, acredito que você não esteja vendo nenhum problema. Não é mesmo meu caro leitor?
Porém, no momento em que olhamos a linha 35, algo começa a não fazer muito sentido. Principalmente ao olharmos a linha 37. Misericórdia, que coisa esquisita é esta que está sendo feito aí? Nesta linha 37, estamos simplesmente tentando alocar espaço na memória meu caro leitor. Porém existe um pequeno detalhe, que tornar as coisas um pouco confusas. O tal detalhe é que no MQL5, NÃO PODEMOS RETORNAR PONTEIROS. E por conta disto, a função da linha 45 tem uma pequena falha, que tentamos resolver nesta linha 37.
Então preste atenção. Quando formos procurar um valor, que foi inserido na estrutura st_Data, porém este valor NÃO FOI ENCONTRADO. Temos que retornar um dado invalido. Em linguagens como C e C++, fazemos isto retornando, normalmente, um valor NULL. Porém aqui no MQL5, NÃO PODEMOS FAZER ISTO. Precisamos de um subterfúgio. Por conta disto no momento em que a linha 37 tentar alocar o primeiro bloco para adicionar um dado do tipo st_Reg. Iremos alocar duas posições de memória. A primeira estará vazia. Já a segunda estará com um valor que será atribuído por conta da linha 40.
Sendo este o primeiro elemento, teremos duas posições alocadas. A partir da próxima chamada, a linha 37, irá perceber que já temos, pelo menos, dois elementos alocados, e alocará apenas mais um. E assim acontecerá a toda nova chamada que seja feita para a função Set da estrutura st_Data. Observe que ela nada tem a ver como o procedimento Set da estrutura st_Reg. E isto tem motivo. Mas será visto depois.
Muito bem, como a função Get da estrutura st_Data, está diretamente relacionada a explicação da estrutura Set. Tudo que você precisa entender aqui, é que o valor recebido como argumento pela função Get, não estará de fato nos dando uma indicação de qual o índice procurado no array Values. Mas sim, qual o valor que esteja fisicamente gravado, naquele elemento específico. Por conta disto, precisamos de um laço, para procurar o valor específico dentro do que seria a estrutura st_Reg. Este daqui é um problema, do qual depois iremos falar mais a respeito dele. Mas basicamente é isto.
Agora podemos ver o código principal. Lembrando que este código visto no que é o fragmento mostrado no código 04. Pode muito bem ser colocado em um arquivo de cabeçalho. E o mesmo irá funcionar sem problemas. Mas antes de mostrar como fazer isto, de modo a conseguir generalizar o código. Precisamos ver o restante do mesmo. Isto é mostrado logo na sequência.
. . . 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. //+------------------------------------------------------------------+
Código 05
E aí está meu caro leitor, a parte mais divertida e animada até este momento. Como boa parte deste código, já deve ter sido compreendida por você. Já que ele foi visto antes. Vamos a parte que é novidade aqui. E esta é justamente o laço da linha 67 e as linhas 75 e 76.
Vamos começar pelo laço. Como cada elemento a ser colocado na estrutura st_Data, é um tipo st_Reg. Precisamos que a linha 71, crie a estrutura st_Reg. Para somente depois, usando a linha 72, podemos armazenar o elemento do tipo st_Reg, na estrutura st_Data. Agora pergunta: Por que não estou fazendo isto diretamente na estrutura st_Data? Resposta: Por que se a gravação dos elementos do tipo st_Reg fosse feito diretamente pela estrutura st_Data, ficaríamos impedidos de sobrecarregar a estrutura depois. Mas, tenha calma você logo irá entender melhor o que acabei de dizer.
Uma vez que todos os elementos tenham sido gravados na estrutura st_Data. Podemos usar as linhas 75 e 76 para testar a própria estrutura st_Data. Para isto usamos a linha 75, para procurar a string cujo índice seja igual a três. E olhando nas linhas 60 e 61, podemos perceber que valor tem este índice. Mas já a linha 76, irá procurar um elemento cujo índice é igual a treze. Como tal índice não existe sendo definido na linha 61, o valor retornado será aquele que estiver definido na linha 51 do código 04.
Tendo como base este conhecimento prévio do que irá acontecer. Você já pode executar o código. E ao fazer isto, irá ver que o resultado é o que podemos observar logo na imagem abaixo.
Imagem 03
Ou seja, funciona perfeitamente bem. Então agora podemos generalizar boa parte do código. Mas para fazer isto, vamos a um novo tópico. Assim, você poderá estudar com calma o que foi feito até aqui, para somente depois tentar entender o que será feito a fim de generalizar as coisas.
Estruturas de estruturas, parte três, a vingança
Se você achou confuso o que foi feito no tópico anterior. Então se prepare, pois agora a coisa irá ficar ainda mais animada. Aqui é onde o filho chora e a mãe não vê. Então preste atenção o seguinte fato. No código 04, visto no tópico anterior, a estrutura st_Data, só pode receber um tipo de dado, ou seja o tipo st_Reg. Mas isto não torna a coisa de fato genérica. Apenas aprisiona a estrutura st_Data ao que esteja sendo definido dentro da estrutura st_Reg. Mesmo que uma nada tenha a ver com a outra.
No entanto, se mudarmos levemente o código da estrutura st_Data, conseguiremos fazer com que ela se liberte a ponto de poder começar a trabalhar com tipos de dados diversos. Preste atenção ao que acabei de falar. Quando a estrutura st_Data, se tornar um template, ela não ficará totalmente livre da estrutura st_Reg. Isto por conta da função Get, que ainda irá estar ligada ao tipo st_Reg. Mas mesmo assim, vale o esforço.
Então, conforme foi dito, tudo que precisamos e transformar a estrutura st_Data em um template. E ao mesmo tempo corrigir o código principal para que ele se adeque a este template, que precisará ser criado pelo compilador. Isto é feito usando o código visto logo na sequência.
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. //+------------------------------------------------------------------+
Código 06
Na teoria, este código 06, pode comportar qualquer tipo de estrutura de dados. Ou qualquer tipo de dado dentro da estrutura st_Data. Isto na teoria. E o motivo é justamente a função Get presente na linha 46 do código 06. Observe que para tornar o código mais genérico, tudo que precisamos fazer, foi adicionar a linha 26 e logo depois, trocar as referências a st_Reg, nas linhas 32, 36 e 46, pelo tipo T. Uma vez feito isto, precisamos mudar a declaração da linha 64 para que o compilador consiga entender e gerar a estrutura adequada para nós.
O resultado da execução deste código 06 é o mesmo da execução do código 05. Ou seja, a imagem 03. Porém, estamos ainda de alguma forma ligados a estrutura st_Reg. Mas esta ligação, que ao mesmo tempo é um problema, também pode ser de algum benefício. Tudo depende da necessidade, e do nível de criatividade e conhecimento sobre certos conceitos, que você tenha, meu caro leitor. Observe o seguinte: A ligação que ainda existe da estrutura st_Data com a estrutura st_Reg é a chamada Get_K presente na linha 49. Este é o único ponto de ligação que ainda persiste ali. Pois bem, como a estrutura st_Reg também pode ser sobrecarregada, podemos utilizar tipos diferentes ali também.
Mas como este tipo de coisa gera alguns detalhes que podem vir a ser complicado de serem entendido neste finalzinho de artigo. Não irei falar sobre isto neste exato momento.
Porém, toda via e, entretanto, podemos fazer uma outra coisa, que ao meu ver é igualmente interessante, e ao mesmo tempo pode ser explicada no que resta de espaço para este artigo. O detalhe é justamente esta linha 49 no código 06. Agora pense no seguinte fato meu caro leitor, no artigo Do básico ao intermediário: Sobrecarga, falamos de uma maneira bem simples e interessante de utilizar a sobrecarga de funções e procedimentos. Em outros artigos dentro desta série, também mostrei que podemos fazer isto, usando nomes idênticos aos encontrados na biblioteca padrão.
Agora vem a parte interessante. E se, neste código 06, construíssemos uma forma de sobrecarregar esta função Get_K, vista na linha 49. Que tipo de portas isto nos abriria? Como eu disse a pouco, é aqui onde a criatividade e a aplicação de conceitos realmente tornar as coisas muito interessantes. Isto por que, se você observar, irá notar que a estrutura st_Reg, vista no código 06, tem somente duas variáveis. Porém podemos sobrecarregar, se bem que este não seria o termo correto, esta estrutura st_Reg. Isto a fim de poder ter uma outra estrutura com mais variáveis, ou mesmo tipos bem diferentes de dados na estrutura.
No entanto, ao fazermos isto, não necessariamente precisaríamos destruir a estrutura st_Reg original. E tão pouco modificar a estrutura st_Data, já criada no código 06. Como não tenho certeza, de que você, meu amigo leitor, esteja entendendo onde quero chegar. Vamos ver um código onde podemos ver o que pretendo mostrar. Este código pode ser visualizado logo abaixo.
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. //+------------------------------------------------------------------+
Código 07
Quando este código 07 for executado, você irá ver no terminal algo muito parecido com o mostrado na imagem logo na sequência.
Imagem 05
Misericórdia. Mas que coisa mais insana e maluca é esta que você criou? Calma meu caro leitor, o que nos interessa aqui é justamente as informações que estão sendo destacadas nesta imagem 05. E estas informações foram criadas pelo procedimento da linha 89. Que é o ponto onde irei dar uma maior atenção na explicação. Já que grande parte do código é simples de entender. Porém entender este procedimento da linha 89 é extremamente importante para entender algo que será visto no próximo artigo.
Mas antes de explicar o que está acontecendo neste procedimento da linha 89. Precisamos entender outros detalhes aqui. Começando pelo fato de que, na linha 26 temos uma nova estrutura sendo criada. Observe que ela contém uma base parecida com a estrutura st_Reg. Mas as coisas ficam somente neste ponto. Já que esta estrutura st_Bio, tem mais variáveis e um conjunto diferente de operações dentro do seu contexto. Porém o importante para nós é justamente a função da linha 44. Isto por que, ela se parece com a função da linha 20. Tendo o mesmo propósito e objetivo. E isto é importante para o que faremos logo a seguir.
Note também o fato de que a estrutura st_Data, vista neste código 07 é exatamente a mesma vista no código 06. Assim como também a estrutura st_Reg. A única real mudança aqui é a presença desta estrutura st_Bio. Tendo observado isto, podemos partir para o procedimento primário, que é OnStart. Ou o ponto de entrada do nosso código. Perceba que aqui, incluímos algumas coisas extras, que não existiam no código 06. Tais coisas visam justamente mostrar como a estrutura st_Data, pode ser tornar extremamente flexível, se a mesma for bem pensada, durante a fase de implementação do código.
Basicamente adicionamos as linhas 107 e 111. Agora preste muita, mas muita atenção, meu caro leitor. Note que a diferença entre as linhas 110 e 111, é justamente o tipo de dado que estaremos colocando na estrutura st_Data. Por conta disto, dentro do laço da linha 116, adicionamos uma nova variável, isto na linha 119. Está variável irá receber valores na linha 122. E na linha 125, atribuímos, ou somamos um novo dado a estrutura. Só que aqui o tipo será outro, diferente do que acontece na linha 124. Até neste momento, tudo ocorreu conforme era previsto e feito no código 06.
Porém, e é aqui onde a coisa começa a complicar, e ao mesmo tempo se torna mais divertida, temos a linha 130 e esta irá fazer com que o procedimento da linha 89 seja executado. Agora vamos a parte divertida. Observe o seguinte: Apesar de estarmos dentro de um laço na linha 94. A parte que realmente nos interessa é a linha 97. E quero que você compare esta linha 97 com a linha 128. O que existe de diferente em ambas as linhas? Praticamente quase nada. Exceto o fato de que, por conta que a linha 76 irá retornar um tipo de estrutura referente ao tipo de dado, presente na variável declarada na linha 62. Não temos nada de diferente entre ambas as linhas.
No artigo Do básico ao intermediário: Variáveis (III), expliquei que uma função é um tipo de variável especial. Aquele conceito, explicado ali, se aplica aqui. E de maneira bem interessante. Já que por conta do retorno que estamos tendo da função da linha 76. Podemos estar apontando para um ou outro tipo de dado. No caso estrutura. Sem fazer uso de uma programação estrutural, seria quase que impossível, se não extremamente difícil criar o que está sendo feito aqui. Isto por que, na linha 97 estaremos apontando para a estrutura st_Bio. Já na linha 128 estaremos apontando para a estrutura st_Reg. E como ambas estão dentro, por assim dizer, da estrutura st_Data. Conseguimos lidar da mesma forma, com diferentes tipos de informações, ou conjuntos de dados.
Você pode e deve praticar e experimentar isto na prática para entender o que de fato está acontecendo aqui. Não é algo que apenas olhando para o código, e tendo pouca experiência em programação, você irá conseguir entender. E lembre-se: Aqui ainda estamos lidando com material que ao meu ver é de nível básico.
Considerações finais
Neste artigo, foi mostrado como podemos lidar com problemas de forma a estruturar as coisas a fim de criar uma solução mais fácil e atrativa. Apesar do conteúdo visto aqui, ser voltado para a didática, não representando assim um código real. Os conceitos e conhecimento vistos aqui, precisam de fato ser muito bem assimilados. Isto para que no futuro, você meu caro e estimado leitor, consiga acompanhar os códigos que iremos mostrar. Já que conforme as coisas vão sendo mostradas, fica cada vez mais simples, para mim, mostrar detalhes mais profundos e interessantes sobre programação. Coisa que não seria possível de ser feita, se fosse necessário explicar em detalhes cada linha de código criado.
Então aproveite e estude bastante o conteúdo presente no anexo, e não tenha pressa. Estude e pratique estes ensinamentos, pois no próximo artigo iremos continuar de onde este daqui parou. Já que ainda não terminamos de falar sobre programação estruturada e como podemos tirar proveito dela antes de iniciarmos com a programação orientada em objetos.





- 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