English Русский Português
preview
Del básico al intermedio: Definiciones (II)

Del básico al intermedio: Definiciones (II)

MetaTrader 5Ejemplos |
115 0
CODE X
CODE X

Introducción

El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse una aplicación final, cuyo objetivo no sea el estudio de los conceptos mostrados aquí.

En el artículo anterior, «Del básico al intermedio: Definiciones (I)», hablamos sobre la directiva de compilación #define. Vimos cómo utilizar esta directiva para simplificar, acelerar y facilitar la implementación de nuestro código, así como para utilizarla de manera creativa y eficaz durante la fase de aprendizaje del lenguaje para hacer las cosas un poco más sencillas. Para utilizar este mismo recurso es necesario entender lo que se está haciendo. En general, podemos darle a nuestro código MQL5 un aspecto mucho más exótico. Muy bien, por esta razón,

existe una segunda forma de utilizar la directiva de compilación #define. Para no complicar demasiado el artículo anterior, decidí mostrar esto en un artículo totalmente aparte. De esta manera, podremos abordar el tema con más calma. Y tú, querido y estimado lector, podrás estudiar y practicar de una forma mucho más agradable, lo que realmente facilitará la comprensión y asimilación de los conceptos, ya que este conocimiento será de suma importancia para lo que veremos a lo largo de los próximos artículos, principalmente cuando empecemos a trabajar en un nivel de programación que ya considero intermedio.


Entender lo que sería una macro

Una macro, de manera muy simplificada, sería una pequeña rutina o procedimiento que queremos o podemos utilizar varias veces en un código. Esto es una manera muy simplificada de plantear las cosas. En realidad, en la mayoría de las ocasiones —y esto se extiende a otras situaciones un poco más complicadas—, creamos una macro siempre que una parte del código se repita de forma muy constante. Así, en lugar de codificar siempre lo mismo, lo incluimos todo en una unidad o procedimiento, que recibe el nombre de macro.

Sin embargo, esta forma de definir una macro no es, en realidad, adecuada. Esto se debe a algunos factores agravantes que complican un poco más la creación de dicha definición.

El problema es que, en gran medida —si no en casi todo momento— las macros se definen de manera que el código se coloca inline, en lugar de colocarlo dentro de un procedimiento, lo que generaría llamadas apilando y desapilando cosas en memoria. En mi opinión, esta sería la mejor definición de una macro. Pero decir que al utilizar una macro estaremos colocando código inline no la convierte en algo realmente especial. Esto se debe a que, en teoría, también podemos hacer esto con cualquier procedimiento o función. Digo en teoría porque no he notado una gran diferencia en el tiempo de ejecución de funciones o procedimientos declarados como inline o no.

Es muy probable que no tengas ni idea de lo que estoy hablando. Permíteme que te aclare las cosas un poco más. Así como en C y C++, MQL5 posee una palabra reservada que difícilmente veo utilizar a otros programadores, al menos aquí. Dicha palabra reservada es inline. Pero, ¿qué significa esta palabra en la práctica? Bueno, mi querido lector, normalmente, cuando un programador crea un código, puede convertir llamadas de procedimiento o incluso funciones en un código inline si el lenguaje lo permite. Es decir, dejamos de tener llamadas de procedimiento para tener un código que crece de manera exponencial con el fin de hacer su ejecución más rápida, lo que conlleva utilizar más memoria.

Puede parecer una tontería o incluso una locura. Sin embargo, cuando se usa bien, forzar al compilador a crear un código más rápido, a costa de utilizar más memoria, puede ser el camino correcto. Sin embargo, hay que tener cuidado al hacer esto, porque si el código crece de manera exponencial, tarde o temprano se llegará a un callejón sin salida, ya que se necesitará cada vez más memoria y la ganancia en términos de velocidad de procesamiento no compensará el coste de necesitar más memoria.

Para ejemplificar, veamos cómo se haría esto. Para ello, observa el código justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Fibonacci_Element_Default   11
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. inline 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 01

Ahora, presta atención a lo que voy a explicar, querido lector, porque es muy importante para que puedas comprender adecuadamente qué es una macro al utilizar una directiva de compilación #define para crear una rutina o procedimiento que se utilizará en nuestro código.

Observa que este código 01 se parece mucho a lo visto en los demás artículos hasta ahora. Sin embargo, en la línea 20 hay algo diferente. Esa diferencia es precisamente la palabra reservada inline. Ahora, tú, al saber cómo funciona este código 01, podrías preguntarme: «Pero ¿qué tiene de especial esta simple palabra, añadida en la línea 20, para que el código sea diferente?».

La respuesta es que el código no se creará como tú esperas. Esto se debe a que este mismo código, si se lleva a C o C++, generará un código ligeramente diferente. No se trata de lo que puedes ver, sino de cómo el compilador manejará esta forma de escribir el código.

Nuevamente, presumo que el compilador de MQL5 hace las cosas de manera que no se noten diferencias entre el código escrito de la forma en que está hecho el código 01 y otro código visto en los artículos anteriores. Sinceramente, no logré notar cambios en términos de velocidad de procesamiento al usar inline o no en la declaración de un procedimiento o función.

De cualquier modo, lo que el compilador entenderá al ver esta línea 20 es que SIEMPRE que esta función Fibonacci_Interactive aparezca en el código, TODO EL CÓDIGO entre las líneas 20 y 28 deberá sustituir la llamada que exista como Fibonacci_Interactive. Al hacer esto, deberá crear una base de variables locales presentes en el procedimiento o función, de manera que no entren en conflicto con posibles variables locales presentes en el lugar donde se añadirá el código.

Para aclarar esto más, este mismo código 01 sería montado por el compilador como algo parecido a lo que se ve justo abajo.

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

Código 02

Ahora bien, este código 02 es, de hecho, el que se crearía y ejecutaría. Sin embargo, observa que fue necesario modificar el código de una forma muy concreta. Y sería el compilador quien haría esto. Nosotros, como programadores, solo le diríamos al compilador si debe o no colocar una función o procedimiento inline. Pero sería el propio compilador el responsable de ajustar las cosas.

Vale, ahora mismo podrías estar pensando: correcto. Pero no hay motivo para preocuparse. Porque, aparentemente, todo siguió igual. En efecto, mi querido lector. Este es un ejemplo sencillo. Pero considera el hecho de que la función de la línea 20, mostrada en el código 01, se utilizará mil veces en tu código. El compilador hará lo que se muestra en el código 02 mil veces. Esto haría que el ejecutable fuera cada vez más grande, además de ocupar cada vez más espacio y necesitar cada vez más tiempo para cargarse. Aunque la velocidad de ejecución fuera mayor, tal vez no compensaría.

Por tanto, si has entendido esto, entender lo que es una macro —como vamos a definir en breve— será muy sencillo. Incluso podemos definir la primera macro usando este código. Observa que, entre las líneas 10 y 18, tenemos un código completo, o, mejor dicho, un bloque aislado del resto del código. Así, para convertir este código en nuestra primera macro, bastaría con cambiarlo por algo parecido a lo que se ve justo abajo.

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

Código 03

Ahora viene la parte interesante. Este código 03 es casi, que quede bien claro, casi, igual que el código 02. Sin embargo, cuando se ejecute, tendrá un resultado diferente al del código 02. ¿Puedes ver cuál es la diferencia, querido lector? Probablemente no. Esto se debe a que no es algo tan evidente la primera vez que vemos una macro declararse.

Mi querido lector, nota lo siguiente: todo lo que hice de diferente entre los códigos 02 y 03 fue añadir la directiva de compilación #define en la línea 10. Esta viene seguida de una constante. Sí, la macro ES UNA CONSTANTE. Nunca lo olvides.

Una macro, al ser definida, debe estar contenida en UNA ÚNICA LÍNEA.

NO ES POSIBLE crear una macro con más de una línea de código. Esta es una regla impuesta por el lenguaje de programación. Así, para construir la macro usando varias líneas, hay que añadir un carácter especial al final de cada una. Este es el símbolo que puedes ver en cada una de las líneas, a partir de la línea 10, pero ten cuidado de NO COLOCAR ESTA BARRA en la última línea. De lo contrario, la macro no terminará donde esperabas, y es posible que aparezcan alertas de error al intentar compilar el código.

Bueno, parece bastante simple, ¿verdad? Bueno, más o menos. Si tienes los cuidados necesarios, sí, es bastante sencillo crear macros. Pero si piensas que esto es todo lo que se ve en el código 03, estás equivocado. Esta sería la macro basada en el código 02. Recuerda que al ejecutar el código 03, el resultado será diferente al del código 02. Pero ¿por qué? El motivo es que la macro NO ESTÁ SIENDO UTILIZADA. Solo la declaramos. Por lo tanto, en la salida del código 02, como se puede ver en la imagen de abajo, tendremos dos informaciones.

Imagen 01

Sin embargo, al ejecutar el código 03, la salida será la que se ve en la imagen a continuación.

Imagen 02

Bueno, pero entonces, ¿cómo podemos resolver esto? Para hacerlo, mi querido lector, debemos indicarle al compilador que utilice la macro en el código. Esto es muy simple, como puedes notar justo abajo.

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

Código 04

Ahora sí. Con la inclusión de la línea 16 en este código 04, se obtiene el mismo resultado que se ve en la imagen 01, lo que demuestra que el código funciona como se esperaba. Sin embargo, esta fue la parte fácil. Ahora viene la parte divertida. Para ello, vamos a separar las cosas en un nuevo tema. Esto es necesario porque primero debes entender lo que se ha mostrado hasta ahora para poder comprender lo que se hará a continuación.


Pasar argumentos a una macro

Virgen María santísima. Pensé que ya habíamos terminado, y ahora vienes tú y me dices que podemos pasar argumentos a una macro. Bueno, déjame ver si he entendido el concepto de macro. Corrígeme si me equivoco.

Una macro es el código que existe en una rutina que queremos colocar de forma inline en nuestro código, ¿no es así? Sí. Estás en lo cierto. Entonces, si logro crear una forma de implementar toda una función o procedimiento dentro de una macro, no necesitaré crear la misma función o procedimiento, ya que para el compilador no habrá diferencia a la hora de construir el código. ¿Estoy en lo cierto? De nuevo, correcto. Solo que hay un pequeño detalle: implementar procedimientos es más sencillo que funciones, ya que estas exigen una variable extra en la mayoría de los casos. De todos modos, es correcto.

Entonces ya lo entendí. Si quiero pasar un argumento dentro de una macro, basta con que utilice la misma forma de declarar las cosas que se usa en la declaración de una función o procedimiento. Así de simple. Lo tengo clarísimo.

Bueno, mi querido lector, en este caso estás casi en lo correcto. Esto es así porque no declaramos los argumentos que se pasarán dentro de una macro de la misma manera que cuando usamos una función o procedimiento. En este caso, la cosa funciona de manera un poco diferente. Y es aquí donde está la dificultad. A diferencia de lo que ocurre al pasar parámetros dentro de funciones y procedimientos, donde podemos decir: «este argumento no puede ser manipulado», «este sí puede ser manipulado», «este argumento es de tal tipo» y «este otro es de tal otro tipo», cuando hacemos esto en macros NO TENEMOS este control. Y, por consiguiente, no contamos con la ayuda del compilador para evitar algunos tipos de errores bastante desagradables.

La diferencia radica en eso. Por eso, muchos programadores evitan utilizar macros. Esto se debe a que cualquier pequeño descuido o tontería que hagas a lo largo del código puede meterte en serios aprietos, ya que los errores presentes en las macros son muy difíciles de corregir, precisamente porque son difíciles de encontrar. Porque, en una parte del código, la macro puede estar proporcionando valores correctos, mientras que, en otro punto del mismo código, un poco más abajo, la misma macro puede estar proporcionando valores erróneos. Y lograr detectar este tipo de fallo y corregirlo es, en mi opinión, una de las tareas más complicadas y agotadoras.

Así que ten mucho cuidado al utilizar macros en tu código. Son un recurso valioso, pero pueden hacerte perder horas intentando resolver un problema que, de otra forma, sería sencillo de corregir.

Vale, entonces, hagamos lo siguiente: como el código 04 no es más que una modificación del código 01, hasta el punto de que podamos utilizar macros, podemos modificar este código 04 con el fin de entender cómo pasar valores dentro de una macro.

Observa lo siguiente: En el código 01, en las líneas ocho y nueve, tenemos la capacidad de utilizar dos funciones diferentes: una donde la respuesta sería recursiva y otra donde la respuesta sería interactiva. Pero, en el código 04, a pesar de que tenemos ambas respuestas, NO PODEMOS pasar un valor a la parte del cálculo interactivo, a menos que modifiquemos el valor indicado en la definición de la línea cuatro. Pero eso no es válido. Queremos poder pasar cualquier valor, como se haría en el caso del código 01.

Para hacer más fácil de entender lo que acabo de decir, observa el código justo abajo.

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

Código 05

Al ejecutar este código 05, verás, como resultado, lo que se ve en la imagen justo abajo.

Imagen 03

Obviamente, los resultados son diferentes. Esto se debe a que, en la línea 14 del código 05, estamos pasando un valor como parámetro de la función de la línea 18. Algo que no ocurre en la macro, ya que el valor está fijado en lo que se refiere a la definición de la línea cuatro. Entonces, puedes ver que trabajar con macros exige algunos cuidados y más atención. Para resolver esto, necesitamos pasar algún argumento a la macro. Para hacerlo, modificamos ligeramente el código de la macro. Este cambio puede verse en el código justo debajo.

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

Código 06

Al ejecutar este código 06, el resultado es el que podemos ver en la imagen justo abajo.

Imagen 04

Claramente, los valores son diferentes, pero esto se hizo de manera intencional, precisamente para mostrar que podemos pasar valores independientes y que una cosa no está ligada a la otra. Así pues, creo que queda más claro que una macro no está ligada a ninguna otra función o procedimiento.

Bueno, esto fue interesante, pero no es divertido, ya que la macro no se comporta como una función, como sería en el código 01, ni como un procedimiento aparte. En realidad, simplemente es un código aislado. Sin embargo, este código 06 sigue funcionando como si fuera el código 02, y esto, en la mayoría de los casos, es algo completamente inútil, ya que no sirve para absolutamente nada, salvo para colocar lo que sería la línea 15 de este código 06 en varios otros puntos. Sin embargo, como estos códigos tienen un propósito didáctico, son simples y no necesitan utilizar tal herramienta, que son las macros. Pero estamos haciendo esto aquí precisamente para explicar cómo utilizarla y trabajar con ella.

Pensemos entonces cómo lograr que la macro se comporte como una función. En realidad, NO PODEMOS usar una macro como la del código 06 como una función. Recuerda que una función es como una variable especial: siempre devuelve un valor cuando la usamos para obtenerlo en función de los argumentos pasados a ella. Presta atención a lo que se ha dicho. NO ESTOY diciendo que no podamos usar macros como funciones. Lo que digo es que NO PODEMOS usar ESTA MACRO como si fuera una función. Cuidado con no confundir churras con merinas.

Sin embargo, a pesar de esta limitación inicial, podemos hacer que ESTA MACRO se comporte como un procedimiento, donde estamos haciendo paso por referencia. ¿Cómo? Sencillo: basta con modificar el código como se muestra abajo.

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

Código 07

Ahora las cosas se ponen interesantes. Esto se debe a que empezamos a usar la macro para un objetivo más noble, por así decirlo. Fíjate en el siguiente hecho: en la línea 14 declaramos una variable, pero NO LA INICIALIZAMOS. La inicializamos en la línea 16. Ahora, podrías preguntarte: «Amigo, ¿pero qué locura es esta en la línea 16?». Tranquilo, querido lector, no es ninguna locura.

Nota que la variable de la línea 14 es del tipo ulong, y el tipo esperado por la función de la línea 21 es del tipo uint. Como queremos usar el mismo valor tanto para la función de la línea 21 como para la macro que se utilizará a continuación, necesitamos hacer una conversión explícita de tipo para que el compilador no genere alertas. Por este motivo, ves la declaración de la línea 16 de esta manera.

Muy bien, justo después tenemos la línea 17. Aquí es donde la cosa empieza a ponerse interesante. Esto es porque estamos pasando la variable por referencia. Así, cuando la macro modifique el valor de esta, el cambio se reflejará aquí, en nuestro código principal. El cambio de valor se produce en la línea 9. Por eso, es necesario estar atento. De lo contrario, podemos acabar con una bomba de tiempo en las manos. Y, para demostrar que este sistema funciona y se puede utilizar, tenemos la línea 18, donde se imprime el valor de la variable en el terminal. Al ejecutar este código 07, obtendremos la imagen que se ve justo abajo.

Imagen 05

Vale, creo que quedó claro que, cada vez que pasamos una variable dentro de una macro, lo hacemos usando un paso por referencia. Y, precisamente por esta razón, debemos tener cuidado para no modificar el valor indebidamente por parte de la macro. Pero espera un momento. Este tipo de macro que podemos ver en el código 07 es una macro que funciona como si fuera un procedimiento. ¿Existe alguna forma de usar una macro como si fuera una función? Es decir, de manera que podamos enviar un valor y obtener otro como respuesta. Esta es una duda que muchos programadores tienen al principio de su aprendizaje sobre macros.

Bueno, en el fondo, las macros están más orientadas a trabajar como procedimientos. Sin embargo, dependiendo de cómo construyamos el código dentro de una macro, podemos hacer que funcione como si fuera una función. En este caso que estamos viendo en los códigos presentados en estos artículos, podemos crear un ejemplo de esto, solo como una forma de presentar el mecanismo. Aunque no sea algo muy elaborado y pueda parecer incluso poco interesante, podemos intentarlo. Un ejemplo de esto puede verse en el código justo debajo.

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

Código 08

Aquí, en este código 08, podemos ver una pequeña demostración de una macro que funciona como una función. Esta macro, definida en la línea seis, es un caso sencillo de macro que funciona como una función. Fíjate que en la línea 11 la estamos utilizando. Básicamente, esto tiende a ocultar parte de la complejidad que pueda existir en el código, ya que, dependiendo del nombre que le des a la macro, resulta mucho más sencillo entender su funcionamiento.

Ten en cuenta que el objetivo aquí NO ES hacer el código más eficiente, sino más legible. Por ejemplo: podrías crear un conjunto de macros para manipular valores de fecha y hora con el fin de utilizar el tipo datetime. Este sería un ejemplo típico bastante útil para las macros con funcionalidad de función. Para hacer esto más claro y comprensible, vamos a crear algunas macros solo para demostrar lo que he mencionado.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. enum eConvert {
05.     FORMAT_DECIMAL,
06.     FORMAT_OCTAL,
07.     FORMAT_HEX,
08.     FORMAT_BINARY
09. };
10. //+------------------------------------------------------------------+
11. #define macro_GetDate(A)            (A - (A % 86400))
12. #define macro_GetTime(A)            (A % 86400)
13. //+----------------+
14. #define macro_GetSec(A)             (A - (A - (A % 60)))
15. #define macro_GetMin(A)             (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
16. #define macro_GetHour(A)            (int)((A - (A - ((A % 86400) - (A % 3600)))) / 3600)
17. //+----------------+
18. #define PrintX(X)                   Print(#X, " => ", X);
19. //+------------------------------------------------------------------+
                   .
                   .
                   .

Fragmento 01

En este fragmento 01 tenemos lo que sería nuestro archivo de inclusión. Estoy incluyendo macros para mostrar que podemos ampliar sustancialmente nuestra capacidad de programación con el tiempo, a medida que adquirimos experiencia y montamos lo que podría llamarse una biblioteca particular.

Bueno, para experimentar lo que hacen estas macros vistas entre las líneas 11 y 18, vamos a usar un pequeño código de pruebas. Este se ve justo abajo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Tutorial\File 01.mqh"
05. //+------------------------------------------------------------------+
06. void OnStart(void)
07. {
08.     datetime dt = D'2024.08.15 12:30:27';
09.     ulong v;
10. 
11.     Print("Date And Time: ", dt);
12.     Print("0x", ValueToString(dt, FORMAT_HEX));
13.     PrintX((datetime)(v = macro_GetDate(dt)));
14.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
15.     PrintX((datetime)(v = macro_GetTime(dt)));
16.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
17.     PrintX((datetime)(v = macro_GetSec(dt)));
18.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
19.     PrintX((datetime)(v = macro_GetMin(dt)));
20.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
21.     PrintX((datetime)(v = macro_GetHour(dt)));
22.     Print("0x", ValueToString(v, FORMAT_HEX), " :: ", ValueToString(v, FORMAT_DECIMAL));
23. }
24. //+------------------------------------------------------------------+

Código 09

Esto es muy interesante y divertido, ya que, con muy poco trabajo, podemos analizar una serie de cosas que, de otra manera, tendrían que hacerse utilizando otros recursos que aún no han sido explicados. Al ejecutar este código 09, se mostrará lo que se ve en la imagen de abajo.

Imagen 06

Ahora, verás por qué dije que este código 09 es bastante interesante. En las líneas 13, 15, 17, 19 y 21 hay llamadas a una macro. Esta macro está definida en la línea 18 del fragmento 01. El objetivo de esta macro es decirnos el nombre de la variable y su valor. En este caso, el nombre de la variable es, en realidad, una función que utiliza una macro diferente en cada una de las líneas mencionadas en el código 09. Observa que es una combinación bastante inusual e interesante, ya que el valor devuelto por cada macro se coloca en una variable local y, justo después, se imprime el valor hexadecimal de dicha variable, así como su valor decimal.

Esto demuestra que, de hecho, estamos capturando los valores correctos que están en el formato datetime. Sé que muchos de ustedes pueden pensar que esto es una locura. Pero, como puedes observar y constatar, esta macro es muy útil, sobre todo cuando queremos que un código se ejecute de la manera más rápida y segura posible, además de hacer que todo el trabajo de codificación sea considerablemente más sencillo, agradable y divertido.


Consideraciones finales

En este artículo, vimos qué son y cómo debemos pensar en macros con fines de codificación, ya que son uno de los recursos que, muchas veces, se emplean incorrectamente y, en otras, se ignoran, precisamente por la falta de conocimiento y práctica en su utilización. Muchos programadores acaban sin entender por qué algo que ellos consideraban imposible —o incluso inalcanzable— puede ser hecho por otro programador, muchas veces desconocido.

Yo mismo tengo —y me gusta pensar— la siguiente tesis:

No existe un programador malo, sino programadores que no han encontrado su camino. Existe, sí, el profesional mal capacitado, o que se cree preparado cuando, en realidad, todavía es un principiante en programación.

Este tipo de concepto que intento transmitir en estos artículos está dirigido precisamente a personas que están comenzando a estudiar programación. Si empiezas por el camino correcto, comprendiendo los conceptos y el motivo de la existencia de esta o aquella herramienta, te resultará más sencillo transformar tus ideas en realidad. Así que, querido lector, es posible que este contenido te parezca algo tonto y sin mucho propósito u objetivo. Sin embargo, si comprendes y practicas lo que se ha visto aquí, verás que mucho de aquello que antes considerabas complicado y difícil de hacer será perfectamente alcanzable. Claro que aún faltan algunas cosas por explicar, pero ya hemos dado muchos pasos en la dirección correcta.

Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/15588

Archivos adjuntos |
Anexo.zip (4.58 KB)
Desarrollo de un sistema de repetición (Parte 77): Un nuevo Chart Trade (IV) Desarrollo de un sistema de repetición (Parte 77): Un nuevo Chart Trade (IV)
En este artículo, explicaré algunos detalles y precauciones que debes tener en cuenta al crear un protocolo de comunicación. Son cosas bastante básicas y simples. No voy a profundizar demasiado en este artículo. Pero es necesario que comprendas su contenido para entender lo que sucederá en el receptor.
Obtenga una ventaja sobre cualquier mercado (Parte IV): Índices CBOE de volatilidad del euro y el oro Obtenga una ventaja sobre cualquier mercado (Parte IV): Índices CBOE de volatilidad del euro y el oro
Analizaremos datos alternativos curados por el 'Chicago Board Of Options Exchange' (CBOE) para mejorar la precisión de nuestras redes neuronales profundas al pronosticar el símbolo XAUEUR (oro).
Asesores Expertos Auto-Optimizables con MQL5 y Python (Parte IV): Apilamiento de modelos Asesores Expertos Auto-Optimizables con MQL5 y Python (Parte IV): Apilamiento de modelos
Hoy demostraremos cómo se pueden crear aplicaciones comerciales impulsadas por IA capaces de aprender de sus propios errores. Demostraremos una técnica conocida como apilamiento, mediante la cual usamos 2 modelos para hacer 1 predicción. El primer modelo suele ser un alumno más débil, y el segundo modelo suele ser un modelo más potente que aprende los residuos de nuestro alumno más débil. Nuestro objetivo es crear un conjunto de modelos, para lograr, con suerte, una mayor precisión.
Del básico al intermedio: Definiciones (I) Del básico al intermedio: Definiciones (I)
En este artículo, haremos cosas que para muchos parecerán extrañas y totalmente fuera de contexto, pero que, si se aplican bien, harán que tu aprendizaje sea mucho más divertido y emocionante, ya que podemos construir cosas bastante interesantes basándonos en lo que se muestra aquí, lo que permite una mejor asimilación de la sintaxis del lenguaje MQL5. El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse una aplicación cuya finalidad no sea el aprendizaje y el estudio de los conceptos mostrados.