preview
Do básico ao intermediário: Sobrecarga de operadores (VI)

Do básico ao intermediário: Sobrecarga de operadores (VI)

MetaTrader 5Exemplos |
16 0
CODE X
CODE X

Introdução

No artigo anterior Do básico ao intermediário: Sobrecarga de operadores (V), que ao meu ver foi um dos artigos que muitos iniciantes precisarão estudar com bastante calma e atenção. Foi feita o que seria uma primeira parte de uma implementação de uma lista duplamente encadeada, utilizando a Sobrecarga de operadores de forma bastante pura. E tudo isto tendo sido feito dentro do que o MQL5 nos permite explorar. Porém, toda via e, entretanto, aquilo que foi visto no artigo anterior, não finaliza o que precisa ser de fato implementado.

Mas como durante a escrita e explicação que estava sendo dada foi notado que algo precisava ser sacrificado para que o artigo não ficasse muito confuso, ou muito extenso. Fiz a escolha de dividir o conteúdo a ser explicado em dois artigos. Assim sendo, caso você não tenha visto, ou entendido de maneira adequada o que foi explicado no artigo anterior. Peço que por favor, retorne a ele antes de dar início a leitura deste artigo de agora. Isto por que, sem entender o que foi visto lá, entender o que será visto aqui, se torna uma tarefa praticamente impossível. Dado o tipo de coisa que iremos implementar aqui.

Grande parte das técnicas e conceitos que serão vistos neste artigo, vem da minha experiência em programação C e C++. Sendo assim, procure estudar este artigo com bastante calma e atenção. Pois aqui iremos ver algo que considero material cujo programador em nível intermediário deve dominar. Mas como temos muito o que falar, vamos direto ao tópico principal deste artigo.


Uma lista encadeada repleta de diversão (II)

Dentro do que foi feito no artigo anterior, já temos meios de adicionar e ler elementos em uma lista encadeada. Porém aquilo que se encontra implementado lá, NÃO é o suficiente para termos de fato uma lista perfeitamente funcional. Primeiro pelo simples fato de que ainda não temos um mecanismo para excluir nenhum elemento. Segundo, quando vamos fazer a leitura da lista, a mesma é destruída, algo que precisamos corrigir. Terceiro, a leitura não pode ser feita de maneira aleatória. Já que tal mecanismo não foi ainda implementado.

Com relação a este terceiro ponto, no artigo Do básico ao intermediário: Sobrecarga de operadores (IV), foi explicado um pequeno detalhe que precisamos levar em conta. Porém, toda via e, entretanto, existe uma forma de contornarmos aquele pequeno inconveniente. Portanto, vamos fazer uso de uma outra técnica. Mas que não trabalha da forma como você pode estar imaginando. Lembre-se de que aquilo que foi explicado no artigo acima mencionado, de fato continua valendo. Mas podemos manipular o código de uma certa maneira a fim de tornar possível implementar este terceiro ponto.

Ok, vamos começar então, do mais simples para o mais complicado. Como o segundo e terceiro ponto, praticamente se comunicam, podendo de certa maneira serem considerados um único desafio. Não que seja mesmo um desafio, e você logo irá entender o porquê. Ele é algo que será mais simples de entender do que a parte referente a excluir dados da lista, via Sobrecarga de operadores.

Sendo assim, vamos reaver o que já se encontra implementado. Tendo assim o que será o nosso ponto de partida.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Include\C_Demo_01.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     C_Demo <char> demo;
09. 
10.     demo << 10;
11.     demo << 84;
12.     demo << -6;
13.     demo[2] <<= 35;
14.     demo[0] <<= 47;
15.     demo[4] <<= 110;
16.     demo.Debug(__FUNCTION__, __LINE__);
17. 
18.     for (char info; demo >> info;)
19.         Print(info);
20. };
21. //+------------------------------------------------------------------+

Código 01

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T> class C_Demo
05. {
06.     private:
07. //+----------------+
08.         T           m_info;
09.         C_Demo <T>  *m_prev,
10.                     *m_next;
11. //+----------------+
12. #define def_Adjust(x) (m_prev = (m_prev == NULL ? x : ((*m_prev).m_prev != NULL ? (*m_prev).m_prev : m_prev)))
13. //+----------------+
14.         void Debug_Private(string fn, uint line)
15.         {
16.             Print("===== DEBUG [", fn, " :: ", line, "]=====");
17.             for (C_Demo <T> *loc = m_prev; (loc != NULL); loc = (*loc).m_next)
18.                 PrintFormat("0x%06X ->> 0x%06X <<- 0x%06X = [%d]", (*loc).m_prev, loc, (*loc).m_next, (*loc).m_info);
19.             Print("=================");
20.         }
21. //+----------------+
22.     public:
23. //+----------------+
24.         C_Demo() : m_next(NULL), m_prev(NULL) {}
25. //+----------------+
26.         void operator<<(const T arg)
27.         {
28.             C_Demo <T>  *loc = new C_Demo <T>;
29.                         
30.             (*loc).m_info = arg;
31.             (*loc).m_prev = m_next;
32.             if (m_next != NULL) (*m_next).m_next = loc;
33.             m_next = loc;
34.             m_prev = def_Adjust(loc);
35.         }
36. //+----------------+
37.         bool operator>>(T &arg)
38.         {
39.             C_Demo <T>  *loc = def_Adjust(m_prev);
40.             if (loc == NULL) return false;
41.             arg = (*loc).m_info;
42.             if ((m_prev = (*loc).m_next) != NULL) (*m_prev).m_prev = NULL;
43.             delete loc;
44. 
45.             return true;
46.         }
47. //+----------------+
48.         C_Demo <T> *operator[](const uint arg)
49.         {
50.             C_Demo <T> *loc = def_Adjust(m_prev);
51.             for (uint c = 0; (loc != NULL) && (c < arg); loc = (*loc).m_next, c++);
52.             return loc;
53.         }
54. //+----------------+
55.         void operator=(const T arg)
56.         {
57.             m_info = arg;
58.         }
59. //+----------------+
60.         void operator<<=(const T arg)
61.         {
62.             C_Demo <T>  *loc = new C_Demo <T>;
63.             
64.             (*loc).m_info = arg;
65.             (*loc).m_next = GetPointer(this);
66.             (*loc).m_prev = m_prev;
67.             if (m_prev != NULL) (*m_prev).m_next = loc;
68.             m_prev = loc;
69.         }
70. //+----------------+
71.         void Debug(string fn, uint line)
72.         {
73.             m_prev = def_Adjust(m_prev);
74.             Debug_Private(fn, line);
75.         }
76. //+----------------+
77. #undef def_Adjust
78. //+----------------+
79. };
80. //+------------------------------------------------------------------+

Código 02

Muito bem, quando este código 01 é executado, teremos como resultado o que é visto logo abaixo.

Imagem 01

Legal. Agora podemos começar. No artigo Do básico ao intermediário: Classes (III), foi explicado alguns detalhes sobre o tempo de vida de uma classe. Agora pense no seguinte detalhe: Como em muitos dos casos, não iremos dentro de nosso código principal, ler toda a lista, a fim de a destruir e devolver a memória para o sistema. Precisamos implementar um destructor dentro da classe vista no código 02. Isto irá garantir que não teremos disparos de alertas do MetaTrader 5, quando a variável que esteja suportando a lista seja destruída sem que a memória alocada tenha sido liberada. Fazer isto é algo muito simples e direto. Tudo que precisamos fazer é adicionar o fragmento visto logo abaixo ao código 02.

                   .
                   .
                   .
25. //+----------------+
26.         ~C_Demo()
27.         {
28.             for (T loc; this >> loc;);
29.         }
30. //+----------------+
                   .
                   .
                   .

Fragmento 01

Com isto, agora não precisamos nos preocupar com erros por falta de liberação da memória alocada. Muito bem, agora podemos focar na parte da leitura randómica dos elementos dentro da lista. Já que a leitura sequencial poderá remover os elementos, conforme eles vão sendo lidos. Mas a leitura randomizada não irá destruir a lista. Nos permitindo assim um maior controle do que estaremos fazendo na lista. E isto fará com que ele venha a ficar muito mais funcional.

Como quero que você venha a experimentar cada mudança que será feita aqui, a fim de entender o passo a passo. Irei na média do possível fazer as coisas em arquivos diferentes, de forma que no anexo, você terá acesso a todos os códigos que eu por ventura achar necessário colocar ali. De qualquer forma, procure experimentar o que será visto no artigo, a fim de ter uma melhor compreensão do que cada coisa irá produzir no resultado final.

Muito bem, então vamos a primeira experiência. Esta é feita usando o código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Include\C_Demo_02.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     C_Demo <char> demo;
09. 
10.     demo << 10;
11.     demo << 84;
12.     demo << -6;
13.     demo[2] <<= 35;
14.     demo[0] <<= 47;
15.     demo[4] <<= 110;
16.     demo.Debug(__FUNCTION__, __LINE__);
17.     demo[3] = 18;
18.     demo.Debug(__FUNCTION__, __LINE__);
19. };
20. //+------------------------------------------------------------------+

Código 03

Quando executado este código irá gerar o que é visto logo abaixo.

Imagem 02

Ou seja, com relação a mudar um valor dentro da lista encadeada, não temos nenhum problema. Assim como também, podemos notar que o destructor está funcionando perfeitamente bem. Agora caso venhamos a desejar ler o conteúdo presente em um dado elemento, precisaríamos implementar no arquivo de cabeçalho isto que está sendo mostrado no fragmento logo abaixo.

                   .
                   .
                   .
75. //+----------------+
76.         void GetElement(T &arg)
77.         {
78.             arg = m_info;
79.         }
80. //+----------------+
                   .
                   .
                   .

Fragmento 02

A parte chata do que este fragmento 02 representa, é que, agora nosso código não irá mais fazer uso apenas da sobrecarga de operadores. Porém, agora poderemos ler qualquer elemento, no momento em que desejarmos fazer isto. E para que isto ocorra, precisaremos utilizar algo que é visto no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Include\C_Demo_02.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     char info;
09.     C_Demo <char> demo;
10. 
11.     demo << 10;
12.     demo << 84;
13.     demo << -6;
14.     demo[2] <<= 35;
15.     demo[0] <<= 47;
16.     demo[4] <<= 110;
17.     demo[3] = 18;
18.     demo.Debug(__FUNCTION__, __LINE__);
19.     for (uint pos = 1; pos < 3; pos++)
20.     {
21.         demo[pos].GetElement(info);
22.         Print("The content of position ", pos, " is ", info);
23.     }
24. };
25. //+------------------------------------------------------------------+

Código 04

Observe o que está acontecendo no laço da linha 19 deste código 04. Aqui, exatamente na linha 21, é que capturamos o valor da posição requirida. Ao fazermos isto desta maneira, NÃO REMOVEMOS O ELEMENTO DA LISTA. Apenas fazemos a leitura e o elemento continua ali, disponível para ser utilizado novamente.

Ok, acredito que nenhuma outra parte vista até aqui, é algo assim tão complicado. Sendo até mesmo bastante simples de se entender. De qualquer forma, o resultado da execução deste código 04 é visto logo abaixo.

Imagem 03

Agora pense um pouco, no que vimos até este momento, meu caro leitor. Isto por que, podemos fazer uma modificação no código, a fim de tornar a linha 21 do código 04, uma adepta da sobrecarga de operadores. E para fazer isto, precisamos apenas e tão somente modificar o fragmento 02, para este que podemos visualizar logo abaixo.

                   .
                   .
                   .
75. //+----------------+
76.         void operator>>=(T &arg)
77.         {
78.             arg = m_info;
79.         }
80. //+----------------+
                   .
                   .
                   .

Fragmento 03

Com isto, agora podemos modificar o laço do código 04, para este que podemos ver logo abaixo.

                   .
                   .
                   .
19.     for (uint pos = 1; pos < 3; pos++)
20.     {
21.         demo[pos] >>= info;
22.         Print("The content of position ", pos, " is ", info);
23.     }
                  .
                  .
                  .

Fragmento 04

Veja que coisa interessante. Bem lá no fundo, o código continua funcionando da mesma maneira. Porém, ao olhar esta linha 21, no fragmento 04. Muitos podem achar que se trata de um código completamente diferente.

Viu como não é de fato difícil transformar algo de modo a utilizarmos a sobrecarga de operadores? Tudo que precisamos fazer, é tomar alguns cuidados a fim de não estragar o código. E tal pouco, tornar ele completamente ilegível.

Certo, mas esta linha 21 parece ser bem estranha. Na verdade até que nem tanto, meu caro leitor. Se você olhar com calma o código irá notar que ela seria algo equivalente ao que podemos ver nas linhas 14 até a linha 16. Podendo inclusive a sobrecarga ser feita de forma a utilizar este código de maneiras bem mais exóticas. Como por exemplo o que é visto logo abaixo.

                   .
                   .
                   .
75. //+----------------+
76.         T operator>>=(T &arg)
77.         {
78.             return (arg = m_info);
79.         }
80. //+----------------+
                   .
                   .
                   .

Fragmento 05

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Include\C_Demo_02.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     char info;
09.     C_Demo <char> demo;
10. 
11.     demo << 10;
12.     demo << 84;
13.     demo << -6;
14.     demo[2] <<= 35;
15.     demo[0] <<= 47;
16.     demo[4] <<= 110;
17.     demo[3] = 18;
18.     demo.Debug(__FUNCTION__, __LINE__);
19.     for (uint pos = 1; pos < 3; pos++)
20.         Print("The content of position ", pos, " is ", demo[pos] >>= info);
21. };
22. //+------------------------------------------------------------------+

Código 05

Apesar destas modificações aparentemente gerarem um novo código. Elas nos permitem fazer algumas mudanças na própria estrutura do código principal, como você pode observa ao analisar o laço da linha 19, neste código 05.

Ok, chega de ficar falando sobre esta parte. Acredito que todos aqui, já devem ter conseguido notar o que podemos ou não fazer. Mas agora chegou a hora em que as coisas irão tomar um outro rumo. Pois iremos implementar a exclusão de elementos de dentro da lista.

Como esta parte muitas das vezes costuma gerar muito mais confusão do que qualquer outra coisa, que foi vista até aqui. Vamos simplificar ainda mais a nossa lista. Isto para que a didática seja a mais adequada, ao mesmo tempo que será mais simples de explicar o que estará acontecendo.

O problema em se implementar a exclusão, com o código que você viu até este ponto é justamente o destructor da classe. Já que sem ele teremos disparos de recursos não liberados, vindos do MetaTrader 5. Porém, com ele, teremos o código quebrando toda a vez que tentarmos remover algum elemento, que não esteja na base da lista. Até dá para corrigir isto, mantendo o código como você o viu até este ponto. No entanto, é muito mais simples, efetuar algumas mudanças no código presente no arquivo de cabeçalho, do que implementar a solução de outra maneira.

Assim sendo, vamos modificar o arquivo de cabeçalho, para o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T> class C_NODE
05. {
06.     private :
07. //+----------------+
08.         T           m_info;
09.         C_NODE <T>  *m_next,
10.                     *m_prev;
11. //+----------------+
12.     public  :
13. //+----------------+
14.         C_NODE(const T arg1, C_NODE <T> *arg2, C_NODE <T> *arg3) : m_info(arg1), m_prev(arg2), m_next(arg3) {}
15. //+----------------+
16.         const T info(void) { return m_info; }
17. //+----------------+
18.         C_NODE <T> *prev(void) { return m_prev; }
19. //+----------------+
20.         C_NODE <T> *next(void) { return m_next; }
21. //+----------------+
22.         void setPrev(C_NODE <T> *arg) { m_prev = arg; }
23. //+----------------+
24.         void setNext(C_NODE <T> *arg) { m_next = arg; }
25. //+----------------+
26.         void operator=(const T arg) { m_info = arg; }
27. //+----------------+
28.         T operator>>=(T &arg) const { return (arg = m_info); }
29. //+----------------+
30.         void operator<<=(const T arg)
31.         {
32.             C_NODE <T>  *loc = new C_NODE<T>(arg, m_prev, GetPointer(this));            
33.             if (m_prev != NULL) (*m_prev).m_next = loc;
34.             m_prev = loc;
35.         }
36. //+----------------+
37. };
38. //+------------------------------------------------------------------+
39. template <typename T> class C_Demo
40. {
41.     private :
42. //+----------------+
43.         C_NODE <T>  *m_Base,
44.                     *m_Top;
45. //+----------------+
46.         C_NODE<T> *AdjustBase(C_NODE <T> *arg) { return m_Base = (m_Base == NULL ? arg : ((*m_Base).prev() == NULL ? m_Base : (*m_Base).prev())); }
47. //+----------------+
48.     public  :
49. //+----------------+
50.         C_Demo(): m_Base(NULL), m_Top(NULL) {}
51. //+----------------+
52.         ~C_Demo()
53.         {
54.             for(C_NODE <T> *ptr = (*m_Top).prev(); m_Top != NULL; m_Top = ptr, ptr = (ptr != NULL ? (*m_Top).prev() : NULL))
55.                 delete m_Top;
56.         }
57. //+----------------+
58.         void operator<<(const T arg)
59.         {
60.             C_NODE <T> *tmp = m_Top;
61. 
62.             m_Top = new C_NODE <T> (arg, m_Top, NULL);
63.             if (tmp != NULL) tmp.setNext(m_Top);
64.             AdjustBase(m_Top);
65.         }
66. //+----------------+
67.         bool operator>>(T &arg)
68.         {
69.             C_NODE <T> *tmp;
70. 
71.             if ((tmp = AdjustBase(m_Base)) == NULL) return false;
72.             arg = (*m_Base).info();
73.             if ((m_Base = (*m_Base).next()) != NULL) (*m_Base).setPrev(NULL);
74.             delete tmp;
75.             return true;
76.         }
77. //+----------------+
78.         C_NODE <T> *operator[](const uint arg)
79.         {
80.             C_NODE <T> *loc = AdjustBase(m_Base);
81.             for (uint c = 0; (loc != NULL) && (c < arg); loc = (*loc).next(), c++);
82.             return loc;
83.         }
84. //+----------------+
85.         void Debug(string fn, uint line)
86.         {
87.             Print("===== DEBUG [", fn, " :: ", line, "]=====");
88.             for (C_NODE<T>*loc = AdjustBase(m_Base); (loc != NULL); loc = (*loc).next())
89.                 PrintFormat("0x%06X ->> 0x%06X <<- 0x%06X = [%d]", (*loc).prev(), loc, (*loc).next(), (*loc).info());
90.             Print("=================");
91.         }
92. //+----------------+
93. };
94. //+------------------------------------------------------------------+

Código 06

Este código 06, é consideravelmente muito mais simples do que todos os demais visto aqui. No entanto, ele tem as mesmas capacidades, dos códigos mostrados até aqui. Portanto, considero completamente desnecessário explicar o mesmo. Já que tudo que ele faz, foi visto até este presente momento. E tenho absoluta certeza de que qualquer um de vocês, que estejam acompanhando está sequencial, conseguirão entender de maneira muito fácil como este código trabalha.

Mas então por que você não nos apresentou ele antes? Por que somente agora você vem com este código? Pois bem meu caro leitor, existe de fato um bom motivo para isto. Já que se este código 06, fosse mostrado antes, você dificilmente iria conseguir entender como ele funciona. Já que aqui a classe C_Demo mantem a lista. E a classe C_NODE irá criar.

Para que você perceba isto, basta olhar para a linha 62 deste código 06. Esta linha cria um novo elemento de maneira, quase que totalmente automática. Já será necessário apenas e tão somente, depois de o elemento ter sido criado, é o ajuste da posição anterior e a posição posterior. E isto é feito nas linhas 63 e 64. Veja como é muito mais simples do que todos aqueles ajustes que eram feitos anteriormente.

Mas porque é importante, que tenhamos chegado a este código 06? Bem, o motivo é que sem este código 06, seria muito mais complicado implementar a exclusão da forma como quero mostrar que podemos implementar a mesma. Até daria para ser feito, mas o nível de complexidade envolvido, acabaria por destruir completamente a didática e o entendimento do conceito que será adotado.

Uma vez entendido isto, podemos enfim começar a implementar o código de exclusão. Porém vamos fazer isto com calma. Já que iremos usar algo que para você, que apenas e somente conhece uma ou outra linguagem, não irá fazer sentido algum. Já que o mecanismo que iremos utilizar no final, deriva justamente da linguagem C e C++. O conceito até onde sei e pude ver em códigos MQL5, jamais foi utilizado. Pelo menos não encontrei nenhuma referência sobre ele.

Mas chega de perder tempo. E vamos ao que interessa. Para implementar a rotina de exclusão, com base neste código 06, visto a pouco. Precisamos fazer muito pouco. Tamanho o nível de simplicidade que este código nos permite obter. Basicamente precisamos apenas acrescentar o fragmento mostrado logo abaixo ao código.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. template <typename T> class C_NODE
005. {
                   .
                   .
                   .
015. //+----------------+
016.         ~C_NODE()
017.         {
018.             if (m_prev != NULL) (*m_prev).m_next = m_next;
019.             if (m_next != NULL) (*m_next).m_prev = m_prev;
020.         }
021. //+----------------+
                   .
                   .
                   .
043. };
044. //+------------------------------------------------------------------+
045. template <typename T> class C_Demo
046. {
                   .
                   .
                   .
090. //+----------------+
091.         void Exclude(const uint arg)
092.         {
093.             C_NODE <T> *loc = AdjustBase(m_Base);
094.             for (uint c = 0; (loc != NULL) && (c < arg); loc = (*loc).next(), c++);
095.             delete loc;
096.         }
097. //+----------------+
                   .
                   .
                   .
106. };
107. //+------------------------------------------------------------------+

Fragmento 06

O que? É somente isto que precisamos adicionar ao código? Não acredito. Você só pode estar de brincadeira. Bem, meu caro leitor, é sério, precisamos adicionar somente isto. Porém tem um pequeno problema. Mas iremos ver isto depois. Primeiro vamos ver se de fato este código modificado funciona. Para verificar isto, passamos a utilizar o código visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Include\C_Demo_04.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     C_Demo <char> demo;
09. 
10.     demo << 10;
11.     demo << 84;
12.     demo << -6;
13.     demo[2] <<= 35;
14.     demo[0] <<= 47;
15.     demo[4] <<= 110;
16.     demo[3] = 18;
17.     demo.Debug(__FUNCTION__, __LINE__);
18.     demo.Exclude(4);
19.     demo.Debug(__FUNCTION__, __LINE__);
20. };
21. //+------------------------------------------------------------------+

Código 07

Ok, agora vamos executar este código 07. Lembrando que na linha 18, estamos pedindo para que o elemento de índice quarto seja removido da lista. E não se esqueça, a contagem se inicia em zero, justamente por conta da linha 94 do fragmento 06. Assim, quando executarmos este código o resultado é o que podemos ver na imagem logo abaixo.

Imagem 04

Perfeito, funcionou maravilhosamente bem. Porém, toda via e, entretanto, existe um pequeno problema aqui. Que é o fato de este fragmento 06, permitir que apenas os elementos centrais sejam excluídos. Na verdade, os elementos presentes nos extremos também podem ser excluídos. Mas irá ocorrer uma falha devido a um desarranjo nos ponteiros. Para tornar isto mais claro, vamos tentar eliminar o elemento cujo valor é 47. Já que ele está no índice zero. Tudo que precisamos fazer é mudar conteúdo da linha 18, neste código 07, para o que é visto logo abaixo.

demo.Exclude(0);

Agora quando o código voltar a ser executado, o que será apresentado é visto na imagem abaixo.

Imagem 05

Ok, isto foi um pouco estranho. Então vamos tentar agora eliminar o elemento de valor -6. O index dele é cinco. Assim precisamos mudar a mesma linha mostrada acima para a que é vista logo abaixo.

demo.Exclude(5);

O resultado da execução é o que podemos observar na imagem logo na sequência.

Imagem 06

De fato, isto está muito estranho mesmo. Claramente podemos observar nesta imagem 06 que o ponteiro foi corrigido dentro do node. Isto graças ao destructor que podemos ver no fragmento 06. No entanto, devido ao fato de que os ponteiros não foram reajustados de maneira adequada. O código simplesmente falha. Esta é uma das situações que podemos utilizar diversas abordagens para corrigir o problema. Cada uma com suas vantagens e desvantagens. Porém, aqui neste artigo, justamente por conta da didática, irei propor a você meu caro leitor, a seguinte solução vista logo abaixo.

                   .
                   .
                   .
090. //+----------------+
091.         void Exclude(const uint arg)
092.         {
093.             C_NODE <T>  *loc = AdjustBase(m_Base),
094.                         *ptr = NULL,
095.                         *tmp;
096.             for (uint c = 0; (loc != NULL) && (c < arg); ptr = loc, loc = (*loc).next(), c++);
097.             tmp = (*loc).next();
098.             delete loc;
099.             if (ptr != NULL) m_Top = ((*ptr).next() == NULL ? ptr : m_Top);
100.                 else m_Base = tmp;
101.         }
102. //+----------------+
                   .
                   .
                   .

Fragmento 07

Agora usando o que é visto neste fragmento 07, podemos finalmente resolver o problema de excluir os valores entre os extremos. Como você poderá constatar ao executar novamente o código 07, com as modificações vistas acima. Mas só de curiosidade, vamos ver como seria o resultado quando eliminamos o extremo superior. Este é mostrado na imagem logo abaixo.

Imagem 07

Simplesmente perfeito. Veja que resolver a questão de excluir valores foi muito mais fácil do que poderia ser imaginado antes. Mas não é bem isto que eu quero mostrar aqui. Aqui neste ponto, quero mostrar como podemos fazer isto via sobrecarga de operadores. Então neste momento peço a você, uma atenção ainda maior ao que será explicado. Pois a coisa aqui vai realmente ficar bem confusa no começo. Para começar a explicar o que precisamos fazer, vamos recortar uma parte do código. Este é visto logo abaixo.

                   .
                   .
                   .
083. //+----------------+
084.         C_NODE <T> *operator[](const uint arg)
085.         {
086.             C_NODE <T> *loc = AdjustBase(m_Base);
087.             for (uint c = 0; (loc != NULL) && (c < arg); loc = (*loc).next(), c++);
088.             return loc;
089.         }
090. //+----------------+
091.         void Exclude(const uint arg)
092.         {
093.             C_NODE <T>  *loc = AdjustBase(m_Base),
094.                         *ptr = NULL,
095.                         *tmp;
096.             for (uint c = 0; (loc != NULL) && (c < arg); ptr = loc, loc = (*loc).next(), c++);
097.             tmp = (*loc).next();
098.             delete loc;
099.             if (ptr != NULL) m_Top = ((*ptr).next() == NULL ? ptr : m_Top);
100.                 else m_Base = tmp;
101.         }
102. //+----------------+
                   .
                   .
                   .

Fragmento 08

Estas duas rotinas, vistas neste fragmento 08, não estão juntas por acaso. Elas foram propositalmente colocadas juntas, para que você venha a entender o que iremos fazer. Observe que a linha 86 é igual a linha 93. Assim como a linha 87 é igual a linha 96. Sendo que a única diferença entre elas é justamente a linha 88. Onde a linha 98, faz algo equivalente. Só que com um objetivo diferente. E por que estou lhe chamando a atenção para estas linhas, meu caro leitor? Bem, o motivo é que iremos utilizar justamente a sobrecarga do operador subscrito, para remover um elemento da lista.

Mas espere um pouco aí. Como você pretende fazer isto? Bem, esta é justamente a parte confusa. Mas vamos com calma, pois quero que você compreenda muito bem o conceito a ser adotado aqui.

Se você observar com atenção irá notar que em diversos momentos utilizamos a constante NULL para verificar se um valor é ou não valido, quando o assunto, são ponteiros. E quando o operador delete destrói um elemento, como é feito na linha 98 do fragmento 08. Ele além de fazer isto, atribui a constante NULL para aquela posição. Isto a fim de a marcar como inválida. Não podendo ser utilizada novamente dentro do código. Se você tentar acessar esta posição irá ver seu código quebrando e mostrando avisos de erros, como os vistos nas imagens 05 e 06. Entender isto é muito importante para entender o que iremos fazer a seguir.

Pois bem, se você olhar na documentação do MQL5, irá encontrar qual é o valor de NULL. Isto pode ser visto em Outras constantes. Porém, isto é apenas parte do que precisamos fazer, sendo está a parte fácil. Ou seja, se você atribuir o valor NULL, a um determinado elemento. O código deverá conseguir interpretar isto, como um pedido para que o elemento seja removido da memória. Fazendo assim com que a lista encadeada seja modificada. Bem, a ideia básica é esta. Porém para conseguir fazer isto, precisamos fazer as coisas de uma forma que muitos não irão entender inicialmente.

Para tornar claro o que quero mostrar veja como ficaria o código 07, usando este princípio que iremos implementar. Ele pode ser visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Include\C_Demo_05.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     C_Demo <char> demo;
09. 
10.     demo << 10;
11.     demo << 84;
12.     demo << -6;
13.     demo[2] <<= 35;
14.     demo[0] <<= 47;
15.     demo[4] <<= 110;
16.     demo[3] = 18;
17.     demo.Debug(__FUNCTION__, __LINE__);
18.     demo[4] = NULL;
19.     demo.Debug(__FUNCTION__, __LINE__);
20. };
21. //+------------------------------------------------------------------+

Código 08

Agora preste atenção, quando a linha 18 deste código 08 for executada, estamos pedindo para que o elemento presente no índice quatro seja removido da lista. No entanto, quanto executamos o código o resultado é o que podemos ver logo abaixo.

Imagem 08

Note que o elemento não foi removido. Mas apenas seu valor foi modificado para zero. Agora vem a parte confusa. Existe duas maneiras básicas de eliminar o elemento nestes casos. A primeira é a de sempre que houver a tentativa de lançar o valor zero na lista, o elemento será excluído da mesma. Isto caso ele exista. Se não existir ele não será criado e colocado na lista. Esta é uma forma de se fazer isto. Porém não é adequada em muitos dos casos. Já que dependendo do objetivo da lista, podemos de fato vir a ter elementos com valor igual a zero aparecendo diversas vezes na mesma. Sendo assim precisamos utilizar uma outra abordagem.

E para conseguir esta abordagem, preciso que você veja na documentação do MQL5, o seguinte tema Tipo void e constante NULL. E por favor, não me julguem pelo que irei dizer aqui. Mas isto que está na documentação do MQL5, é apenas parte de um todo. Acredito que colocaram desta forma, justamente para não gerar confusão na mente de programadores iniciantes. Mas se você, caso algum dia venha a se interessar, em estudar C e C++. Irá se deparar com algo realmente assustador. Que é justamente variáveis do tipo void.

Mas como isto pode vir a ser assustador?  Não entendo onde você está querendo chegar. O problema meu caro leitor é que um valor quando declarado, sendo do tipo void, pode assumir qualquer comprimento de dado. Podendo mudar de cara, pelo simples fato de você olhar para ele. É algo realmente muito confuso. Aqui no MQL5, os desenvolvedores da linguagem, limitaram ao máximo este tipo void, o impedindo de ser utilizado normalmente como um tipo de dado. Porém, e é importante que você entenda muito bem isto. Existem situações, em que podemos de fato, declarar uma variável como sendo do tipo void. E assim passar ela para dentro de uma função ou procedimento.

Ao fazermos isto, em muitas das situações acabamos gerando um tipo de problema, extremamente difícil e complicado de ser corrigido. Mas, se for muito bem pensado e feita da forma correta. Podemos abrir diversas portas, pelo simples fato de utilizar uma variável do tipo void em nossos códigos. E para grande parte de vocês, que seja por curiosidade, ou mesmo na tentativa de quem sabe no futuro, vir a se tornar um profissional. Se deparar com uma variável do tipo void é algo tal confuso, que chega a dar um nó no cérebro.

Certo, agora quero que você entenda que o que será visto aqui, não deve ser feito sem o devido cuidado. Pois se for feito de qualquer maneira, irá lhe causar grandes transtornos. A primeira coisa que precisamos fazer, para que a linha 18 vista no código 08, venha a dizer ao código para remover um dado elemento da lista é efetivamente criar a sobrecarga da sobrecarga do operador de atribuição.

Esta questão da sobrecarga, foi vista no artigo Do básico ao intermediário: Sobrecarga. Então mudando o código do arquivo de cabeçalho, passaremos a ter o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T> class C_NODE
05. {
                   .
                   .
                   .
31. //+----------------+
32.         void operator=(const T arg) { m_info = arg; }
33. //+----------------+
34.         void operator=(void *arg)
35.         {
36.             Print(__FUNCTION__, " :: ", __LINE__, " Recognized void type variable...");
37.         }
38. //+----------------+
                   .
                   .
                   .

Fragmento 09

Agora para que possamos ver este fragmento 09 em ação, precisamos mudar o código para o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Include\C_Demo_05.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     C_Demo <char> demo;
09. 
10.     demo << 10;
11.     demo << 84;
12.     demo << -6;
13.     demo[2] <<= 35;
14.     demo[0] <<= 47;
15.     demo[4] <<= 110;
16.     demo[3] = 18;
17.     demo.Debug(__FUNCTION__, __LINE__);
18.     demo[4] = (void *)NULL;
19.     demo.Debug(__FUNCTION__, __LINE__);
20. };
21. //+------------------------------------------------------------------+

Código 09

Node que a linha 18 no código 09 mudou, mais uma vez. Porém, esta é a última vez que a iremos modificar. Todo o restante que será feito, será implementado dentro do arquivo de cabeçalho. Muito bem, executando este código 09, você irá ver o que é mostrado na imagem logo abaixo.

Imagem 09

Pelo amor de DEUS. Misericórdia, que coisa mais maluca e sem noção é esta que estou vendo nesta imagem 09? Isto não é coisa de DEUS. Cara que coisa mais sem noção. Calma, meu caro leitor, vamos manter a calma. Eu avisei que a coisa era bem confusa. E olha que estou pegando leve aqui. Agora que você viu que funciona. Podemos dar o que seria o próximo passo. Que seria justamente pegar aquela rotina de exclusão e a transportar para dentro deste ponto do código.

Porém temos um outro problema antes de fazer isto. Pois, fazer isto, não é como um simples cópia e cola. Já que o código de exclusão implementado para ser usados dentro da classe C_Demo. No entanto, não podemos utilizar aquele mesmo código aqui na classe C_NODE. Já que ele não seria compreendido pelo compilador. Sendo assim não seria gerado o tipo de resultado que queremos. Precisamos assim, adaptar aquele código Exclude, presente na classe C_Demo, para que ele possa ser usado, aqui na classe C_NODE. Programação é ou não é algo extremamente divertido? ( RISOS ).

O que iremos fazer agora é corrigir um problema e gerar outro. E vamos depois corrigir os problemas que serão gerados, até conseguir concluir a implementação que desejamos fazer. Então para começar, preciso que você entenda e veja uma coisa primeiro. Isto é feito, modificando o fragmento 09, para o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T> class C_NODE
05. {
                   .
                   .
                   .
31. //+----------------+
32.         void operator=(const T arg) { m_info = arg; }
33. //+----------------+
34.         void operator=(void *arg)
35.         {
36.             PrintFormat("0x%08X ->> 0x%08X:[%d] <<- 0x%08X", m_prev, GetPointer(this), m_info, m_next);
37.         }
38. //+----------------+
                   .
                   .
                   .

Fragmento 10

Agora executando novamente o código 09, iremos ver o que está sendo mostrado na imagem 10 logo abaixo.

Imagem 10

Note que estamos vendo nesta região demarcada exatamente o elemento que queremos eliminar da lista. Assim sendo, vamos primeiro resolver a questão de eliminar, os elementos que estão no meio da lista, depois nos preocupamos com os elementos que estão nos extremos. Tendo então definido o foco inicial, mudaremos este fragmento 10, para o que é visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. template <typename T> class C_NODE
05. {
                   .
                   .
                   .
31. //+----------------+
32.         void operator=(const T arg) { m_info = arg; }
33. //+----------------+
34.         void operator=(void *arg)
35.         {
36.             delete GetPointer(this);
37.         }
38. //+----------------+
                   .
                   .
                   .

Fragmento 11

Novamente compilamos e executamos o código 09. Agora o resultado visto é mostrado logo abaixo.

Imagem 11

Ebá. Que coisa mais linda. Está funcionando. É realmente incrível. Calma, meu caro leitor, vamos manter o foco. Pois ainda falta resolver a questão dos extremos lembra? O problema agora é um pouco mais complicado do que era antes. Isto por que, antes tínhamos o apoio do próprio procedimento para nos ajudar a ajustar os extremos. Agora não contamos mais com este apoio. Precisamos encontrar uma maneira de contornar isto. Sendo assim, modificamos o código 09, de maneira que possamos focar nossa atenção na eliminação do que seria o extremo superior. Para fazer isto, precisamos mudar a linha 18, para o que é visto logo abaixo.

demo[5] = (void *)NULL;

Ok, agora executando novamente o código 09, temos como resultado o que é visto na imagem abaixo.

Imagem 12

Perceba uma coisa aqui. Nesta região demarcada, podemos ver que a lista foi corrigida de forma a eliminar o extremo superior. No entanto, voltamos a ter o mesmo tipo de problema que havia antes. E que foi visto lá na imagem 06. Assim se tentarmos remover o extremo inferior na lista, com toda a certeza teremos algo que irá se parecer com a imagem 05. Você pode experimentar isto depois de desejar. Mas vamos ver agora, como iremos corrigir isto.

E para fazer isto é preciso entender a sequência lógica de execução do código. Uma vez que você consiga entender esta sequência lógica, irá acabar percebendo que o melhor local para corrigir os extremos é modificando a função AdjustBase. Mas a modificando de que maneira? Bem, para entender melhor, precisamos recorrer ao fragmento 08. Ali podemos claramente notar que durante a exclusão de algum elemento, temos um código muito parecido com o código do operador subscrito. Tanto que ambos estão sendo mostrados no mesmo fragmento. Lembra que expliquei por que os coloquei juntos?

Pois bem, nitidamente percebemos que precisamos de dois ponteiros auxiliares, para que as coisas funcionam a fim de podermos ajustar os ponteiros depois. Assim para promover a devida correção no código a ponto de podemos trabalhar tanto nos extremos quanto no meio da lista. Eliminando sem problema qualquer elemento dentro dela. Precisaremos utilizar o que é visto no código logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. template <typename T> class C_NODE
005. {
006.     private :
007. //+----------------+
008.         T           m_info;
009.         C_NODE <T>  *m_next,
010.                     *m_prev;
011. //+----------------+
012.     public  :
013. //+----------------+
014.         C_NODE(const T arg1, C_NODE <T> *arg2, C_NODE <T> *arg3) : m_info(arg1), m_prev(arg2), m_next(arg3) {}
015. //+----------------+
016.         ~C_NODE()
017.         {
018.             if (m_prev != NULL) (*m_prev).m_next = m_next;
019.             if (m_next != NULL) (*m_next).m_prev = m_prev;
020.         }
021. //+----------------+
022.         const T info(void) { return m_info; }
023. //+----------------+
024.         C_NODE <T> *prev(void) { return m_prev; }
025. //+----------------+
026.         C_NODE <T> *next(void) { return m_next; }
027. //+----------------+
028.         void setPrev(C_NODE <T> *arg) { m_prev = arg; }
029. //+----------------+
030.         void setNext(C_NODE <T> *arg) { m_next = arg; }
031. //+----------------+
032.         void operator=(const T arg) { m_info = arg; }
033. //+----------------+
034.         void operator=(void *arg)
035.         {
036.             delete GetPointer(this);
037.         }
038. //+----------------+
039.         T operator>>=(T &arg) const { return (arg = m_info); }
040. //+----------------+
041.         void operator<<=(const T arg)
042.         {
043.             C_NODE <T>  *loc = new C_NODE<T>(arg, m_prev, GetPointer(this));            
044.             if (m_prev != NULL) (*m_prev).m_next = loc;
045.             m_prev = loc;
046.         }
047. //+----------------+
048. };
049. //+------------------------------------------------------------------+
050. template <typename T> class C_Demo
051. {
052.     private :
053. //+----------------+
054.         C_NODE <T>  *m_Base,
055.                     *m_Top;
056. //+----------------+
057.         struct st01
058.         {
059.             C_NODE <T>  *prev,
060.                         *next;
061.         }m_aux;
062. //+----------------+
063.         C_NODE<T> *AdjustBase(C_NODE <T> *arg)
064.         {
065.             if (m_aux.prev != NULL) m_Top = ((*m_aux.prev).next() == NULL ? m_aux.prev : m_Top);
066.                 else m_Base = (m_aux.next != NULL ? ((*m_aux.next).prev() == NULL ? m_aux.next : m_Base) : m_Base);
067.             return m_Base = (m_Base == NULL ? arg : ((*m_Base).prev() == NULL ? m_Base : (*m_Base).prev()));
068.         }
069. //+----------------+
070.     public  :
071. //+----------------+
072.         C_Demo(): m_Base(NULL), m_Top(NULL) {}
073. //+----------------+
074.         ~C_Demo()
075.         {
076.             for(C_NODE <T> *ptr = (*m_Top).prev(); m_Top != NULL; m_Top = ptr, ptr = (ptr != NULL ? (*m_Top).prev() : NULL))
077.                 delete m_Top;
078.         }
079. //+----------------+
080.         void operator<<(const T arg)
081.         {
082.             C_NODE <T> *tmp = m_Top;
083. 
084.             m_Top = new C_NODE <T> (arg, m_Top, NULL);
085.             if (tmp != NULL) tmp.setNext(m_Top);
086.             AdjustBase(m_Top);
087.         }
088. //+----------------+
089.         bool operator>>(T &arg)
090.         {
091.             C_NODE <T> *tmp;
092. 
093.             if ((tmp = AdjustBase(m_Base)) == NULL) return false;
094.             arg = (*m_Base).info();
095.             if ((m_Base = (*m_Base).next()) != NULL) (*m_Base).setPrev(NULL);
096.             delete tmp;
097.             return true;
098.         }
099. //+----------------+
100.         C_NODE <T> *operator[](const uint arg)
101.         {
102.             C_NODE <T> *loc = AdjustBase(m_Base);
103.             m_aux.prev = NULL;
104.             for (uint c = 0; (loc != NULL) && (c < arg); m_aux.prev = loc, loc = (*loc).next(), c++);
105.             m_aux.next = (loc != NULL ? (*loc).next() : NULL);
106.             return loc;
107.         }
108. //+----------------+
109.         void Debug(string fn, uint line)
110.         {
111.             Print("===== DEBUG [", fn, " :: ", line, "]=====");
112.             for (C_NODE<T>*loc = AdjustBase(m_Base); (loc != NULL); loc = (*loc).next())
113.                 PrintFormat("0x%08X ->> 0x%08X <<- 0x%08X = [%d]", (*loc).prev(), loc, (*loc).next(), (*loc).info());
114.             Print("=================");
115.         }
116. //+----------------+
117. };
118. //+------------------------------------------------------------------+

Código 10

Olhe para este código 10 e diga. Ele é ou não um código lindo de ser visto? ( RISOS ) Agora note que para conseguir o efeito necessário, a fim de corrigir as falhas vista anteriormente. Criei na linha 57, uma pequena estrutura para manter os valores auxiliares que iremos utilizar, na correção dos ponteiros. Estes valores estão sendo ajustados, no operador sobrecarregado visto na linha 100. Mas os mesmos são utilizados na verdade nas linhas 65 e 66. Fazendo um trabalho parecido com o que foi visto no fragmento 08.

Agora você poderá excluir qualquer elemento presente na lista, assim, quando executar o código 09, a fim de eliminar o que seria o primeiro elemento da lista. O resultado será o que podemos ver na imagem logo abaixo.

Imagem 13

Agora sim podemos finalmente comemorar. E parabéns meu caro leitor, você acaba de aprender um monte de coisas novas. Que antes você poderia estar considerando praticamente impossíveis de serem feitas.


Considerações finais

Este com toda a certeza será um artigo no qual muitos de vocês irão estudar por um longo período. Isto a fim de conseguir assimilar tudo o que foi mostrado aqui. Mas quero lembrar a você, que o que vimos aqui, foi apenas uma pequena e insignificante parte de tudo aquilo que é chamado programação. Quando digo que AMO PROGRAMAR, não estou me referindo a criar coisas banais e sem graça. A maior parte das coisas, podem ser resolvidas de maneira muito simples e sem muita dificuldade.

Porém, nada e absolutamente nada, nos tira o prazer de criar algo, que muitos consideram ser impossível ou impraticável. Quando alguns reclamaram do conteúdo de alguns de meus artigos no passado, eu os simplesmente ignorei. Prefiro mostrar em forma de código, aquilo que eles tentam por meio de palavras. Então não se sinta diminuído quando alguém lhe disser que você é incapaz. Não precisa se rebaixar, apenas mostre o que você aprendeu ao logo dos anos e que você sabe como fazer e eles não. Então no próximo artigo iremos continuar está nossa aventura, em busca de mais saber. Usando para isto o MQL5.

Arquivo MQ5Descrição
Code 01 Demonstração básica
Code 02 Demonstração básica
Code 03 Demonstração básica
Code 04 Demonstração básica
Code 05  Demonstração básica
Arquivos anexados |
Anexo.zip (6.99 KB)
Rede neural na prática: O caso da porta XOR Rede neural na prática: O caso da porta XOR
Neste artigo tentarei mostrar a você, meu caro leitor, que nem tudo é como parece. Muitas das vezes somos levados a pensar que as coisas são de uma dada maneira, quando na verdade, podemos estar sendo levados a pensar algo que não necessariamente é verdade. Redes neurais, são de longe um dos assuntos mais interessantes em termos gerais. Tanto pelo ponto de vista matemático, eletrônico ou mesmo de software. Porém, diferente do que muitos acreditam ou pregam. Redes neurais não são nem de longe, a questão e solução definitiva. São apenas um ramo de pesquisa, no qual devemos sempre estar estudando e procurando nos informar sobre o que acontece nos bastidores.
Modelo matricial de previsão baseado em cadeia de Markov Modelo matricial de previsão baseado em cadeia de Markov
Criamos um modelo matricial de previsão baseado em uma cadeia de Markov. O que são cadeias de Markov e como uma cadeia de Markov pode ser usada para trading no Forex.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Visão computacional para trading (Parte 2): complexificando a arquitetura até a análise 2D de imagens RGB Visão computacional para trading (Parte 2): complexificando a arquitetura até a análise 2D de imagens RGB
Visão computacional para trading, como funciona e como é desenvolvida passo a passo. Criamos um algoritmo de reconhecimento de imagens RGB de gráficos de preços com um mecanismo de atenção e uma camada LSTM bidirecional. Como resultado, obtemos um modelo funcional de previsão do preço do euro-dólar com precisão de até 55% no conjunto de validação.