Русский Español
preview
Do básico ao intermediário: Definições (I)

Do básico ao intermediário: Definições (I)

MetaTrader 5Exemplos | 28 novembro 2024, 08:55
154 0
CODE X
CODE X

Introdução

O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como uma aplicação final, onde o objetivo não seja o estudo dos conceitos aqui mostrados.

No artigo anterior Do básico ao intermediário: Recursividade, foi explicado o que seria e como poderíamos utilizar uma técnica de programação muito útil em diversos tipos de cenários. Que é a recursividade. Esta por sua vez nos permite criar mecanismos e implementações de maneira fácil e simples. Isto desde que não venhamos a nos preocupar com a possibilidade de que o código possa em alguns momentos, vir a ser executado em um tempo maior. Necessitando assim que tenhamos paciência em alguns momentos bem específicos.

Mas de uma forma geral, a recursividade ajuda e facilita bastante a nossa vida. Devendo assim, ser um objeto de estudo por parte daqueles que desejam se capacitar, ou no mínimo ter um bom nível de entendimento sobre coisas relacionadas a programação em geral.

Mas aqui neste artigo, vamos tratar de um tema, que já poderia ter sido tratado ante. Que é o uso de definições. Tanto a fim de construção de macros, como para declarações e controle mais fino de certas partes que podem vir a ser implementadas por você, ou outro programador. Cujo código você possa ter se interessado, mas gostaria de modificar o mesmo de maneira controlada. Isto para que se possa alcançar e cobrir certos objetivos particulares.


O que seria definições

Definições em geral podem assumir diversas facetas dentro de um mesmo código. Assim como pode sofrer ligeiras mudanças relacionadas a conceitos e usos a qual pode estar sendo destinada. Isto dependendo da linguagem e o objetivo a ser alcançado, por cada programador em particular. No geral, uma definição nos ajuda a controlar e modificar o código de uma maneira simples, rápida e segura. Seja por meios de criação de macros. Seja por meio de diretivas de compilação. Seja por declarar constantes especiais. De qualquer forma, definições são algo que você irá gostar e apreciar bastante. Desde que você procure entender como elas funcionam e pratique sempre que possível, a sua utilização. Mas em grande parte, você de fato irá gostar bastante de utilizar definições, caso goste de criar pequenas mudanças nos códigos, a fim de verificar qual o resultado que tais mudanças proporcionam. Isto por que, as definições permitem fazer isto de maneira muito simples e prática.

Aqui iremos tratar de definições voltadas ao uso de diretivas de compilação. Existem outros tipos de definições, que são feitas quando utilizamos programação externa. E precisamos importar códigos criados em outros ambientes para serem utilizados em conjunto com o MQL5. Porém, estas definições em particular, exigem que você, meu caro leitor, tenha uma boa experiência em programar em ambientes integrados. Mesmo que você venha a utilizar somente o MQL5 como linguagem principal.

Por exemplo, existem forma de importar códigos e funcionalidades, criadas por outros programadores, normalmente em linguagem C ou C++, para dentro do MQL5. Isto para que consigamos adicionar funcionalidades, ou coisas de interesse pessoal, ao próprio MetaTrader 5. Como por exemplo um player de vídeo. Ou mesmo, desenvolver um utilitário de criação de gráficos científicos avançados, com possibilidades de utilizar uma linguagem parecida com a LaTeX. Que para quem não sabe, é uma linguagem, que permite formatação de expressões matemáticas. Algo bem interessante diga-se de passagem.

Muito bem, como aqui iremos focar nas diretivas de compilação, como forma de criar definições. A coisa será bem mais simples e agradável de ser apresentada. sendo um assunto que tenho certeza, que você conseguirá entender de maneira muito rápida. Já que o que será visto aqui, já foi de alguma maneira apresentado em outros artigos anteriores. Então vamos começar com a própria diretiva #define.

Esta diretiva de compilação é muito direta do que ela faz. Isto por que, basicamente ao utilizar ela, você poderá criar uma destas coisas: Uma constante nomeada, ou uma macro. Basicamente é isto que podemos fazer com esta diretiva. Assim como a diretiva #include, que já foi explicada em outro artigo, no caso Do básico ao intermediário: Diretiva Include. Esta diretiva #define, junto com outras diretivas de compilação. Nos possibilita gerar uma série de pequenos ajustes fáceis de serem ajustados e modificados. Como por exemplo. No artigo anterior, vimos um código que originalmente é mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     const uchar who = 6;
07. 
08.     Print("Result: ", Fibonacci_Recursive(who));
09.     Print("Result: ", Fibonacci_Interactive(who));
10. }
11. //+------------------------------------------------------------------+
12. uint Fibonacci_Recursive(uint arg)
13. {
14.     if (arg <= 1)
15.         return arg;
16. 
17.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
18. }
19. //+------------------------------------------------------------------+
20. uint Fibonacci_Interactive(uint arg)
21. {
22.     uint v, i, c;
23. 
24.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
25.         v += i;
26. 
27.     return (c == arg ? i : v);
28. }
29. //+------------------------------------------------------------------+

Código 01

Agora, note que na linha seis, temos uma constante do tipo uchar, sendo definida. Porém perceba que as funções que irão receber o valor, esperam receber um valor do tipo uint. Como você pode observar nas linhas 12 e 20. Mas você não quer ficar a todo momento ajustando isto. Então podemos fazer o uso de uma diretiva de compilação apenas para tornar este tipo de coisa mais agradável. Ao mesmo tempo, e em alguns casos, pode tornar o código levemente mais rápido. E logo você vai entender o porquê que usar uma diretiva de compilação no lugar de uma constante pode tornar o código, levemente mais rápido. Além de outras questões que também ajudam a melhorar a performance geral.

Ok, então, supondo que gostaríamos de usar uma diretiva de compilação aqui. Este mesmo código 01, poderia ser reescrito como mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element   6
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive(def_Fibonacci_Element));
09.     Print("Result: ", Fibonacci_Interactive(def_Fibonacci_Element));
10. }
11. //+------------------------------------------------------------------+
                   .
                   .
                   .

Código 02

Claro que não irei repetir todo o código, por não haver necessidade para isto. Porém note como a coisa ficou sendo implementada. Parece não fazer nenhuma diferença. Bem, mas de fato existe sim uma diferença aqui, meu caro leitor. Para você que está lendo o código, até que pode parecer tudo igual. Mas para o compilador. O código 01 é diferente do código 02. Justamente por estarmos utilizando uma diretiva #define, neste código 02.

Então para o compilador, o código que realmente será visto é o mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. void OnStart(void)
05. {
06.     Print("Result: ", Fibonacci_Recursive(6));
07.     Print("Result: ", Fibonacci_Interactive(6));
08. }
09. //+------------------------------------------------------------------+
                   .
                   .
                   .

Código 03

Mas espere um pouco. Isto seria o equivalente ao código 01. Então não faz muito sentido usar esta diretiva. Na verdade, faz muito sentido, meu caro leitor. Só que você precisa lembrar que aqui estamos interessados em mostrar as coisas de maneira didática. Pense no fato de que você pode ter uma quantidade enorme de posições em seu código, que deverá usar um determinado valor. Se você usar uma constante, mesmo que sendo uma constante global. Em algum momento, poderá vir a ter problemas, justamente por conta desta constante existir.

Porém, se você utilizar uma definição, como a vista no código 02. Você poderá ter um controle muito maior sobre o código. Além disto, uma constante, a não ser que seja uma constante global, irá ficar retida, ou melhor dizendo, visível, apenas na rotina onde ela esteja sendo declarada. Já uma diretiva, NÃO, a partir do momento em que você a declare, pode a utilizar em qualquer ponto do código, sem nenhum problema. Desde que ela ainda exista. Digo isto, pois diferente de uma constante global, uma diretiva pode ser destruída a qualquer momento. Podendo ter seu valor modificado, ou mesmo, tendo uma função completamente diferente de uma outra diretiva com o mesmo nome.

Então vamos entender isto por partes. Pois cada ponto mencionado aqui, é importante, e você pode vir a precisar em algum momento. Então vamos ver primeiro a questão da visibilidade. Isto em um caso bastante simples. Como você pode observar no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   6
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09.     Print("Result: ", Fibonacci_Interactive());
10. }
11. //+------------------------------------------------------------------+
12. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
13. {
14.     if (arg <= 1)
15.         return arg;
16. 
17.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
18. }
19. //+------------------------------------------------------------------+
20. uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default)
21. {
22.     uint v, i, c;
23. 
24.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
25.         v += i;
26. 
27.     return (c == arg ? i : v);
28. }
29. //+------------------------------------------------------------------+

Código 04

Note o seguinte meu caro leitor. Na linha quatro, estamos declarando uma diretiva de compilação do tipo constante nomeada. Ela é visível em todo o corpo do código, tendo o mesmo tipo e resultado esperado, quando definimos um valor default para um argumento em uma função ou procedimento. Porém, esta mesma diretiva de compilação, poderia ser substituída por uma constante global. Mas, e é aqui onde as coisas ficam interessantes. Você não poderia fazer com uma constante o que é visto no código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   6
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09.     Print("Result: ", Fibonacci_Interactive());
10. }
11. //+------------------------------------------------------------------+
12. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
13. {
14.     if (arg <= 1)
15.         return arg;
16. 
17.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
18. }
19. //+------------------------------------------------------------------+
20. #undef def_Fibonacci_Element_Default
21. //+------------------------------------------------------------------+
22. uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default)
23. {
24.     uint v, i, c;
25. 
26.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
27.         v += i;
28. 
29.     return (c == arg ? i : v);
30. }
31. //+------------------------------------------------------------------+

Código 05

Observe que na linha 20 deste código 05, estamos utilizando uma outra diretiva de compilação. Esta é a #undef. Quando esta diretiva é utilizada, podemos remover, ou destruir uma diretiva que tenha sido definida com o nome que se encontra na diretiva #undef. Este tipo de coisa é extremamente util. Porém antes de falarmos sobre as utilidades mais imediatas deste tipo de coisa. Precisamos falar do que acontece, quando você tentar compilar o código 05. Ao tentar fazer isto, o compilador irá disparar um erro. Este pode ser visto logo abaixo.

Imagem 01

Note que o erro está sendo gerado na linha 22. Porém, def_Fibonacci_Element_Default foi destruída na linha 20. Assim quando o compilador tentar encontrar a constante nomeada no banco de dados para compilar o código, ele não a irá encontrar. Gerando assim o erro que você pode observar na imagem 01. Por conta justamente disto, muitos programadores, tem o habito de colocar um prefixo a fim de ajudar a identificar que tipo de coisa está gerando o erro. Isto não é regra, apenas uma boa prática de programação. Como por exemplo, eu gosto de colocar o prefixo def_ antes do nome de qualquer definição constante nomeada. Isto me ajuda a separar entre uma constante geral e uma diretiva de compilação.

Ok, mas e desejarmos declarar um outro valor para a diretiva logo depois de a termos destruído. Podemos fazer isto? Mas é claro que sim meu caro leitor. Só que devemos tomar cuidado ao fazer isto. Mas depois irei mostrar como você pode se precaver a ponto de evitar dores de cabeça. Não é algo 100% seguro, mas pelo menos ajuda. Porém vamos ver o que acontece se mudarmos a diretiva, como você propos.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   6
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     Print("Result: ", Fibonacci_Recursive());
09.     Print("Result: ", Fibonacci_Interactive());
10. }
11. //+------------------------------------------------------------------+
12. uint Fibonacci_Recursive(uint arg = def_Fibonacci_Element_Default)
13. {
14.     if (arg <= 1)
15.         return arg;
16. 
17.     return Fibonacci_Recursive(arg - 1) +  Fibonacci_Recursive(arg - 2);
18. }
19. //+------------------------------------------------------------------+
20. #undef def_Fibonacci_Element_Default
21. //+----------------+
22. #define def_Fibonacci_Element_Default   7
23. //+------------------------------------------------------------------+
24. uint Fibonacci_Interactive(uint arg = def_Fibonacci_Element_Default)
25. {
26.     uint v, i, c;
27. 
28.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
29.         v += i;
30. 
31.     return (c == arg ? i : v);
32. }
33. //+------------------------------------------------------------------+

Código 06

Muito bem, neste código 06, podemos ver isto que foi proposto sendo aplicado. Note que na linha 22 estamos definindo um novo valor para a diretiva. E ao executar este código o resultado será o que é visto logo abaixo.

Imagem 02

Perceba que os resultados são diferentes. Mas isto já era esperado justamente por conta da mudança no valor. Caso a diretiva definida na linha quatro, fosse uma constante do tipo global. Não poderíamos fazer a mudança que é vista na linha 22. Tão pouco remover a constante global de ser vista por outras partes do código. Esta é a base da qual você deve procurar entender, meu caro leitor. Não é algo para se decorar. Mas sim algo para compreender e assimilar.

Muito bem, aqui vimos que podemos utilizar as diretivas de compilação #define e #undef em conjunto. Isto de uma forma simples. Pois como falei a pouco, existem forma mais elaboradas de usar estas duas diretivas em conjunto. Porém, para fazer isto, precisamos utilizar outras diretivas, para tornar o trabalho mais simples e fácil de ser controlado.


Controlando a versão de um código

Uma das formas mais interessantes de utilizar estas diretivas #define e #undef é no controle de versões diferentes de um mesmo código. Como este código do cálculo de um elemento na sequência Fibonacci, é algo bem simples de entender. Vamos usar ele para explicar esta questão de controle de versão.

Suponhamos que você, não saiba ainda como criar uma versão de cálculo interativo para um dado elemento na sequência Fibonacci. Ou queira criar um cálculo diferente do que é visto no código mostrado até aqui. Bem, você pode dizer que isto seria fácil de ser feito. Mas iria mais hora ou menos hora acabar fazendo alguma bobagem. Porém usando as diretivas de compilação, o risco de fazer alguma bobagem, cai bastante. Por exemplo. Vamos isolar os cálculos de maneira a podemos selecionar se queremos um cálculo interativo ou recursivo. Para fazer isto, basta que criemos algo parecido com o mostrado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define DEF_INTERACTIVE
05. //+----------------+
06. #define def_Fibonacci_Element_Default   6
07. //+------------------------------------------------------------------+
08. void OnStart(void)
09. {
10.     Print("Result: ", Fibonacci());
11. }
12. //+------------------------------------------------------------------+
13. #ifndef DEF_INTERACTIVE
14. //+----------------+
15. uint Fibonacci(uint arg = def_Fibonacci_Element_Default)
16. {
17.     if (arg <= 1)
18.         return arg;
19. 
20.     return Fibonacci(arg - 1) +  Fibonacci(arg - 2);
21. }
22. //+----------------+
23. #endif
24. //+------------------------------------------------------------------+
25. #ifdef DEF_INTERACTIVE
26. //+----------------+
27. uint Fibonacci(uint arg = def_Fibonacci_Element_Default)
28. {
29.     uint v, i, c;
30. 
31.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
32.         v += i;
33. 
34.     return (c == arg ? i : v);
35. }
36. //+----------------+
37. #endif
38. //+------------------------------------------------------------------+

Código 07

Aqui estou mostrando uma primeira alternativa, apenas para exemplificar o que podemos fazer. Observe que na linha quatro deste código 07, criamos uma definição. Esta não precisa conter nenhum valor, apenas existir. Ou melhor dizendo, que seja possível do compilador a enxergar. Agora observe que na linha 10, temos apenas uma função Fibonacci. Mas lhe pergunto: Qual será utilizada? A da linha 15 ou a da linha 27? Mas neste ponto você pode me dizer: Mas isto não faz o menor sentido. Podemos ter duas funções com o mesmo nome? Sim meu caro leitor, podemos. Mas isto será visto em outro momento. Por hora, vamos nos ater apenas ao que é mostrado aqui, no código 07.

Caso você NUNCA tenha visto este tipo de construção, vai ficar maluco tentando entender o que está acontecendo aqui. Isto por que, aparentemente não faz o menor sentido. Mas observe atentamente o código. Veja que na linha 13 estamos utilizando uma outra diretiva de compilação. Esta irá testar se a diretiva declarada existe ou não. E o código entre esta diretiva #ifndef e #endif, somente será compilado se e somente se, a diretiva NÃO EXISTIR. Caso contrário o código não será compilado. Algo parecido acontece na linha 25. Só que ali, o código será compilado, se e somente se a diretiva EXISTIR. Ou seja, como a linha quatro, está definindo a diretiva em que queremos utilizar o código. Apenas o código entre as linhas 25 e 37 será compilado. Já o código entre as linhas 13 e 23 será ignorado.

Não está acreditando? Bem, então vamos testar isto na prática. Mas para tornar isto realmente aceitável. Vamos adicionar uma pequena linha neste código 07, de forma que ele irá ficar como mostrado abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define DEF_INTERACTIVE 
05. //+----------------+
06. #define def_Fibonacci_Element_Default   6
07. //+------------------------------------------------------------------+
08. void OnStart(void)
09. {
10.     Print("Result: ", Fibonacci());
11. }
12. //+------------------------------------------------------------------+
13. #ifndef DEF_INTERACTIVE
14. //+----------------+
15. uint Fibonacci(uint arg = def_Fibonacci_Element_Default)
16. {
17.     Print("Testing ", __LINE__);
18.     if (arg <= 1)
19.         return arg;
20. 
21.     return Fibonacci(arg - 1) +  Fibonacci(arg - 2);
22. }
23. //+----------------+
24. #endif
25. //+------------------------------------------------------------------+
26. #ifdef DEF_INTERACTIVE
27. //+----------------+
28. uint Fibonacci(uint arg = def_Fibonacci_Element_Default)
29. {
30.     uint v, i, c;
31. 
32.     Print("Testing ", __LINE__);
33.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
34.         v += i;
35. 
36.     return (c == arg ? i : v);
37. }
38. //+----------------+
39. #endif
40. //+------------------------------------------------------------------+

Código 08

Ao executar este código 08, você irá ver a imagem logo abaixo.

Imagem 03

Agora mude a linha quatro para o que é mostrado logo abaixo.

// #define DEF_INTERACTIVE 

Compile o código 08 novamente e o resultado será o mostrado logo na sequência.

Imagem 04

Pronto. Está provado que funciona. Em uma versão, utilizamos o cálculo recursivo e em outra o cálculo interativo. E para escolher qual versão seria utilizada, bastou a mudança de uma simples linha de código. É ou não é algo interessante este tipo de implementação e utilização de diretivas no nosso código? Mas não é somente isto. Podemos fazer algo ainda mais interessante. Veja como este mesmo código mostrado até aqui, pode ser reconstruído de uma maneira muito mais simples.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define DEF_INTERACTIVE "Interactive"
05. //+----------------+
06. #define def_Fibonacci_Element_Default   6
07. //+------------------------------------------------------------------+
08. void OnStart(void)
09. {
10.     Print("Result to ",
11. #ifdef DEF_INTERACTIVE
12.         DEF_INTERACTIVE, " : "
13. #else
14.         "Recursive : "
15. #endif
16.     , Fibonacci());
17. }
18. //+------------------------------------------------------------------+
19. uint Fibonacci(uint arg = def_Fibonacci_Element_Default)
20. {
21. #ifdef DEF_INTERACTIVE
22. 
23.     uint v, i, c;
24. 
25.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
26.         v += i;
27. 
28.     return (c == arg ? i : v);
29. 
30. #else    
31. 
32.     if (arg <= 1)
33.         return arg;
34. 
35.     return Fibonacci(arg - 1) +  Fibonacci(arg - 2);
36. 
37. #endif
38. }
39. //+------------------------------------------------------------------+

Código 09

Aqui neste código 09 de fato estamos fazendo o uso das diretivas de uma maneira muito mais organizada. Já que podemos controlar diversas coisas, mudando apenas e somente uma única linha no código. Da mesma maneira que foi feito com o código 08. Então vamos fazer o seguinte: Primeiro vamos executar o código como visto logo acima. Ou seja, com a linha quatro existindo. O resultado da execução pode ser visto logo abaixo.

Imagem 05

A ao colocar a linha quatro da mesma forma que foi feito no código 08, o resultado da execução do código 09 é visto logo abaixo.

Imagem 06

Mas que coisa mais maluca e doida foi esta? Agora sim fiquei bastante curioso e interessado em entender melhor isto. Já que fiquei completamente perdido e sem entender o que se passou neste código 09. Então poderia me explicar, por favor o que aconteceu aqui e por que foi possível obter estes resultados? Mas é claro que sim, meu caro leitor. É para isto que estamos aqui escrevendo estes artigos.

Isto que foi feito aqui, neste código 09, foi somente uma pequena brincadeira, que fiz para mostrar que podemos fazer muito mais, com muito menos. Sei que muitos acham difícil entender como programadores pensam. Mas isto de fato não assim tão complicado. Bons programadores, estão sempre procurando formas e meios de reduzir seu trabalho, ao mesmo tempo que aumentam a sua produtividade. E aqui neste código 09 estamos fazendo justamente isto. Mas de uma forma um pouco mais criativa.

Acredito que você já tenha compreendido a questão referente a diretiva #ifdef. Mas vamos tornar as coisas mais interessantes. Assim como o comando IF que foi explicado em um outro artigo, no passado. A diretiva #ifdef, assim como a #ifndef, funcionam da mesma maneira. Ou seja, tudo que estiver dentro do bloco será ou não executado. Porém, o comando IF tem seu bloco sendo definido como um abre chave e fecha chave. Aqui quem fecha a diretiva, tanto #ifdef, quando #ifndef é a diretiva #endif. SEMPRE. No entanto, pode acontecer de estamos testando algo, e não queremos ficar repetindo comandos de diretiva. Neste caso, dentro do que seria o bloco construído da diretiva #ifdef ou #ifndef, podemos colocar a diretiva #else.

Agora preste atenção. Assim como acontece com o comando IF quando o teste da expressão é falso, e podemos executar o que estiver em um ELSE ligado a este comando IF. Podemos fazer a mesma coisa aqui. Usando as diretivas #ifdef e #ifndef. Ou seja, entender como o comando IF funciona, nos ajuda a implementar e usar de maneira mais adequada as diretivas que se parecem bastante com ele. Já que podemos colocar diretivas #ifdef e #ifndef todas aninhadas de forma a testar partes especificadas de um mesmo código que estamos querendo implementar.

Apesar de não estarmos fazendo este tipo de coisa aqui, você precisa entender que isto é possível de ser feito. Bem, mas vamos voltar a questão do código em si. Observe que na linha quatro definimos algo. Ao fazermos isto, podemos utilizar a diretiva #ifdef e #ifndef para modelar o nosso código. Mas talvez você esteja na dúvida sobre o fato, de que durante esta definição, da linha quatro. Estamos fazendo a mesma coisa que foi feita na linha seis. E que isto poderia vir a interferir nas diretivas de compilação #ifdef e #ifndef. Porém não é isto que acontece. Isto por que #ifdef e #ifndef estará testando se a definição existe ou não. Porém existe de fato, não aqui no MQL5, mas na linguagem C e C++ a diretiva #if onde podemos testar o valor que está sendo definido na diretiva. Porém acredito que por motivos de simplificar o próprio manuseio da linguagem, tal diretiva não foi colocada no MQL5. Apenas a diretiva #ifdef e #ifndef.

Sendo assim, podemos atribuir um valor a diretiva, tornando assim ela uma constante nomeada, e a utilizar como é visto na linha 12. A partir do momento em que temos a diretiva como sendo uma constante nomeada, podemos utilizar ela da mesma maneira que usaríamos uma constante convencional. Sendo por tanto perfeitamente fácil de entender, para quem vem estudando os artigos, compreender como o código 09 funciona. Mas podemos fazer algo ainda mais interessante. Ao mesmo tempo, que também servirá para você entender, como manipular dados, usando as diretivas de compilação, e isto, pode ser de alguma forma útil no seu dia a dia.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define DEF_INTERACTIVE
05. //+----------------+
06. #ifdef DEF_INTERACTIVE
07.     #define def_OPERATION "Interactive"
08. #else
09.     #define def_OPERATION "Recursive"
10. #endif
11. //+----------------+
12. #define def_Fibonacci_Element_Default   11
13. //+----------------+
14. #define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element"
15. //+------------------------------------------------------------------+
16. void OnStart(void)
17. {
18.     Print(def_MSG_TERMINAL, " ", def_Fibonacci_Element_Default, " : ", Fibonacci());
19. }
20. //+------------------------------------------------------------------+
21. uint Fibonacci(uint arg = def_Fibonacci_Element_Default)
22. {
23. #ifdef DEF_INTERACTIVE
24. 
25.     uint v, i, c;
26. 
27.     for (c = 0, i = 0, v = 1; c < arg; i += v, c += 2)
28.         v += i;
29. 
30.     return (c == arg ? i : v);
31. 
32. #else    
33. 
34.     if (arg <= 1)
35.         return arg;
36. 
37.     return Fibonacci(arg - 1) +  Fibonacci(arg - 2);
38. 
39. #endif
40. }
41. //+------------------------------------------------------------------+

Código 10

Este código 10, para quem olha o mesmo sem procurar entender o que está acontecendo. Parece ser algo muito complicado e difícil de ser entendido. Porém, absolutamente nada que está sendo feito neste código 10, é de alguma forma novidade. Pois tudo, e absolutamente tudo, que está sendo feito nele, já foi explicado neste artigo atual em algum momento. No entanto, pode ser um pouco confuso se você não praticar o que foi mostrado neste artigo. Como boa parte destes códigos estará disponível no anexo. Já que muitos são apenas voltados para mostrar o que precisa ser modificado. Você poderá utilizar os mesmos para praticar e estudar cada detalhe visto aqui.

De qualquer forma, vamos ver o que acontece quando este código 10 é executado. Primeiro, vamos deixar com que a diretiva declarada na linha quatro de fato faça seu trabalho. Para isto, você deverá compilar o código como mostrado acima. Isto irá nos fornece a seguinte saída no terminal, que é vista logo abaixo.

Imagem 07

Muito bem, agora procure entender o que aconteceu, antes de modificar o código como mostrado no fragmento abaixo. Isto irá tornar bem mais simples qualquer coisa que venhamos a precisar fazer no futuro. Assim que você conseguir entender como a mensagem mostrada na imagem 07, foi criada. Mude o código como mostrado no fragmento abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. // #define DEF_INTERACTIVE
05. //+----------------+
06. #ifdef DEF_INTERACTIVE
07.     #define def_OPERATION "Interactive"
08. #else
09.     #define def_OPERATION "Recursive"
10. #endif
11. //+----------------+
12. #define def_Fibonacci_Element_Default   11
13. //+----------------+
14. #define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element"
15. //+------------------------------------------------------------------+
                   .
                   .
                   .

Fragmento do código 10

Preste muita atenção para que este fragmento mostrado abaixo, não pareça ser algo sem nenhuma mudança em relação ao código 10. Pois apesar de muito sutil, de fato existe uma mudança sendo feita. E esta está justamente na linha quatro, onde tornamos a definição, um comentário. Sendo um comentário, o compilador irá ignorar esta linha quatro. Sendo assim, é como se a definição de fato não estivesse sendo feita.

Apesar de isto parecer não significar absolutamente nada para o código. Este simples fato, faz com que o compilador venha a gerar um código diferente do que existia antes. Assim, quando este novo código for executado você irá ver no terminal o que é mostrado na imagem logo abaixo.

Imagem 08

Muito bem, com isto acredito ter ficado muito bem esclarecido, a forma de utilizar tanto a definição, quanto também as diretivas #ifdef, #ifndef, #else e #endif. Porém apesar de tudo isto. Falta falar sobre uma outra utilização para a diretiva #define. Lembre-se de que no começo deste artigo, mencionei que esta diretiva serviria para dois propósitos. O primeiro foi este que exploramos e foi demonstrado neste artigo e que você poderá praticar e experimentar com os códigos que estarão no anexo. 

Esta forma vista aqui, visa podermos utilizar a diretiva #define, de modo a evitar a criação desnecessária de uma variável global. E ao mesmo tempo promover uma maneira simples, rápida e eficaz de analisarmos e implementarmos, versões diferentes de um mesmo código. O que para quem é iniciante é algo primoroso. E para quem já é um programador mais experiente é algo trivial. Já que ele faz uso deste tipo de coisa de maneira quase automática. Já que este tipo de coisa facilita muito a vida de programadores em geral. Pena que não temos as diretivas #if que existem no C e C++. Mas tudo bem. Faz parte.

E a segunda forma, de se utilizar a diretiva #define é na criação de macros. Porém como macros são coisas que precisam ser vistas com calma, e sem apavoramento. Decidi não incluir isto neste momento. Já que se o assunto fosse levando e demonstrado. Poderia acabar tornando tudo muito mais complicado e demasiadamente difícil de ser entendido. Isto por que, macros não são como simples trechos de código, como muitos podem imaginar. Macros são uma ferramenta que se bem empregada. Ajuda bastante. Mas se mal compreenda e utilizada de maneira indevida torna o código muito confuso e complexo.

Antes de terminar este artigo. Quero mostrar uma última coisa. Seria mais como sendo um BÔNUS, para você, meu caro leitor. Que teve paciência de ler e com toda a certeza já deve estar ansioso para começar a experimentar o uso da diretiva #define.

Este bônus, é a possibilidade de criar, dentro do MQL5, e sem mudar nada, tanto na linguagem, quanto no que já foi explicado. Alguns comandos simples. De modo que eles possam fazer um pouco mais de sentido. Isto será melhor explorado quando falarmos sobre macro. Mas já dá para passar mel no que será o assunto futuro.

Talvez você não tenha notado, ou percebido. Mas quando usamos a diretiva #define, estamos dizendo ao compilador que um dado texto, deverá ser substituído por outro. Devido a este conceito, podemos criar uma, sintaxe alternativa, isto sem mexer em absolutamente nada na forma de criar seus códigos.

Para demonstrar isto, vamos ver o código logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define BEGIN_PROC          {
05. #define END_PROC            }
06. #define RETURN              return
07. #define ENTER_POINT         void OnStart (void)
08. #define Z_SET_NUMBERS       long
09. #define EQUAL               ==
10. #define IS                  =
11. #define MINOR               <
12. #define OR                  ||
13. #define MORE                +=
14. //+------------------------------------------------------------------+
15. #define DEF_INTERACTIVE
16. //+----------------+
17. #ifdef DEF_INTERACTIVE
18.     #define def_OPERATION "Interactive"
19. #else
20.     #define def_OPERATION "Recursive"
21. #endif
22. //+----------------+
23. #define def_Fibonacci_Element_Default   11
24. //+----------------+
25. #define def_MSG_TERMINAL "Result of " + def_OPERATION + " operation of element"
26. //+------------------------------------------------------------------+
27. ENTER_POINT BEGIN_PROC  
28.     Print(def_MSG_TERMINAL, " ", def_Fibonacci_Element_Default, " : ", Fibonacci());
29. END_PROC
30. //+------------------------------------------------------------------+
31. Z_SET_NUMBERS Fibonacci(Z_SET_NUMBERS arg IS def_Fibonacci_Element_Default)
32. BEGIN_PROC  
33. #ifdef DEF_INTERACTIVE
34. 
35.     Z_SET_NUMBERS v, i, c;
36. 
37.     for (c IS 0, i IS 0, v IS 1; c MINOR arg; i MORE v, c MORE 2)
38.         v MORE i;
39. 
40.     RETURN (c EQUAL arg ? i : v);
41. 
42. #else    
43. 
44.     if ((arg EQUAL 1) OR (arg MINOR 1))
45.         RETURN arg;
46. 
47.     RETURN Fibonacci(arg - 1) + Fibonacci(arg - 2);
48. 
49. #endif
50. END_PROC
51. //+------------------------------------------------------------------+

Código 11

Este código funciona da mesma forma que o código 10. Só que aqui estamos criando algo bem diferente para os padrões que muitos de fato utilizam. Porém, toda via e, entretanto, este código 11 será perfeitamente compreendido pelo compilador. Tanto que de fato, ele irá gerar os mesmos resultados que você vier a obter com o código 10.

Mas olhando para ele, você pode estar pensando: Cara, mas isto NÃO É MQL5. Porém, com o que foi visto aqui neste artigo, você pode olhar de uma forma mais adequada e ver que, sim, apesar de diferente, este código 11 é MQL5 puro e simples. Só que escrito de uma maneira diferente. Voltada principalmente a se parecer com uma linguagem menos matemática e mais natural.

Observe que depois que as definições foram feitas, e isto entre as linhas quatro e treze. Todo o restante do código, mas principalmente a partir da linha 27, ficou muito, mas muito diferente. Muitos poderiam até mesmo dizer que isto não iria gerar um executável. Mas para surpresa destas mesmas pessoas. Sim, este código funciona.


Considerações finais

Neste artigo, fizemos diversas coisas, que para muitos de você pode até parecer estranho e totalmente fora de contexto. Mas que ser for bem empregado irá tornar sua fase de aprendizagem muito mais divertida e empolgante. Já que podemos construir coisas bem interessantes, a ponto de começarmos a compreender melhor a sintaxe da própria linguagem MQL5. Como o material aqui, precisa ser devidamente estudado e você precisa praticar para de fato entender os conceitos adotados. Vou encerrar o artigo neste ponto. Mas no próximo artigo iremos falar sobre a segunda forma de utilizar a diretiva #define. Então pratique e estude o que foi visto aqui, isto para não criar e gerar confusão com o material do próximo artigo.

No anexo você terá cinco dos códigos vistos aqui. Então boa diversão a todos.

Arquivos anexados |
Anexo.zip (2.65 KB)
Desenvolvendo um sistema de Replay (Parte 76): Um novo Chart Trade (III) Desenvolvendo um sistema de Replay (Parte 76): Um novo Chart Trade (III)
Neste artigo vamos compreender como o código faltante no artigo anterior, DispatchMessage, funciona. Aqui será feita a introdução do que será visto no próximo artigo. Sendo assim é importante compreender o funcionamento deste procedimento antes de ver o próximo artigo. O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como sendo, uma aplicação cuja finalidade não venha a ser o aprendizado e estudo dos conceitos mostrados.
Teoria do caos no trading (Parte 1): Introdução, aplicação nos mercados financeiros e o indicador de Lyapunov Teoria do caos no trading (Parte 1): Introdução, aplicação nos mercados financeiros e o indicador de Lyapunov
É possível aplicar a teoria do caos nos mercados financeiros? Vamos explorar nesta matéria como a teoria clássica do caos e os sistemas caóticos diferem do conceito proposto por Bill Williams.
Desenvolvendo um EA multimoeda (Parte 16): Influência de diferentes históricos de cotações nos resultados de testes Desenvolvendo um EA multimoeda (Parte 16): Influência de diferentes históricos de cotações nos resultados de testes
O EA em desenvolvimento deve apresentar bons resultados ao operar com diferentes corretoras. Porém, até agora, os testes foram realizados com base em cotações de uma conta de demonstração da MetaQuotes. Vamos verificar se o EA está pronto para operar em contas reais com cotações diferentes das utilizadas durante os testes e otimizações.
Desenvolvendo um EA multimoeda (Parte 15): Preparando o EA para o trading real Desenvolvendo um EA multimoeda (Parte 15): Preparando o EA para o trading real
À medida que nos aproximamos de um EA pronto, é necessário prestar atenção em questões secundárias na etapa de teste da estratégia de trading, mas que se tornam importantes ao migrar para o trading real.