Muchas veces, cuando uno tiene que escribir algún programa, nos encontramos con la necesidad de evaluar alguna expresión algebraica. Lo que normalmente hacemos es codificar en el lenguaje que estamos trabajando esa expresión y asunto arreglado. Sin embargo, las cosas pueden complicarse. ¿Qué tal si de pronto tenemos que procesar expresiones algebraicas en tiempo de ejecución? es decir, cuando el programa está corriendo. Ahí no podemos codificar la expresión para compilarla con nuestro entorno de desarrollo y por ende, estamos en un pequeño gran problema.

Quizás alguien piense que esto no pasa nunca, pero imaginemos una aplicación tradicional, como la de una calculadora. En este caso, nosotros vamos metiendo los números y los operadores, y así hacemos los cálculos, en la medida que estos se van alimentando al sistema. Pero ¿qué pasa si tenemos expresiones como esta: 34 * 24 + 98 / 7? Aquí, desde luego, estamos en la disyuntiva de si hacemos 34 * 24 y al resultado le sumamos 98 séptimos. O bien, hacemos la suma, 24 + 98, dividimos entre 7 y lo multiplicamos por 34. ¿Cuál es el correcto? No lo hay en realidad. Si dejamos la expresión como la escribimos originalmente, la computadora y nuestro lenguaje de desarrollo usarán la precedencia de operadores, primero la multiplicación y la división, después la suma y la resta. La opción es desde luego, usar paréntesis y listo. Podemos cambiar la precedencia de operadores separando la expresión aritmético con paréntesis.

Y hasta ahí muy bien, pero entonces tenemos que hacer algo para evaluar, en tiempo de ejecución la expresión correspondiente. Por ejemplo, (34 * 24) + (98 / 7). En esta situación tendremos que escribir un parser, un analizador sintáctico que nos permita resolver la expresión. Esto se deja muchas veces como tarea en los primeros cursos de programación. Nuestro parser puedecomplicarse en la medida que la expresión que queremos analizar tenga valores como de senos y cosenos, o exponenciales, etcétera. Hacer un analizador para todo este tipo de expresiones puede resultar una penosa labor. Finalmente lo que estamos haciendo es escribir un intérprete que pueda resolver en tiempo de ejecución este tipo de expresiones directamente.

Ya más de un programador ha escrito algún intérprete para lidiar con el problema y esto me sacó de un buen apuro cuando estaba escribiendo un programa en Delphi. Hallé que ArtFormula contiene dos componentes no visuales (para Delphi y Lazarus – la versión libre de Delphi), que permite el analizador de expresiones simbólicas, así como su evaluación. Tiene un motor en tiempo real para hacer scripts que puedan automatizar estas tareas fácilmente.

El sistema da:

  •     10 operaciones lógicas
  •     7 operaciones aritméticas
  •     6 operaciones a nivel de bits
  •     concatenación de cadenas (strings) (@) y operador (==)
  •     24 funciones aritméticas
  •     10 funciones estadísticas
  •     2 funciones lógicas
  •     17 funciones de cadenas de caracteres (strings)
  •     13 funciones para fechas
  •     14 funciones de programación
  •     Definición de constantes del usuario
  •     Definición de variables del usuario
  •     Funciones definidas por el usuario (módulos)
  •     Subrutinas, variables globales y constantes
  •     Diferenciación simbólica de funciones coin simplificación

Operaciones aritméticas:
x + y, x – y, x * y, x / y, x % y (Mod), x ^ y (power), x\ y (Div)

operación lógica (true=1, false=0):
x > y, x < y, x >= y, x <= y, x = y, x <> y, ! x (not), x & y (and), x | y (or), x xor y

Operaciones con bits:
x && y (band), x || y (bor), !!x (bnot), x bxor y, x >> y (shr), x << y (shl)

Constantes predefinidas:

  • Pi = 3.1415926535897932385
  • True = 1
  • False = 0

Funciones:
sin, cos, tan, sinh, cosh, tanh, asin, acos, atan, asinh, acosh, atanh, sqrt, exp, log, lg (log base 10), int (integer part of a number), frac (fractional part of a number), abs, sign, rnd, randomize, trunc, round, max(x,y…), min(x,y,…), count(x,y,…), sum(x,y,…), sumofsquares(x,y,…), avg(x,y,…), variance(x,y,…), variancep(x,y,…), stddev(x,y,…), stddevp(x,y,…) iff(cond,x,y) (if cond = true then result = x else result = y), isnumber(x), chr(x), length(s), trim(s), trimleft(s), trimright(s) lowercase(s), uppercase(s), midstr(s,x,y), leftstr(s,x), rightstr(s,x), pos(s,t), code(s), format(s,x), formatf(s,x), stringofchar(c,n), concat(s1,s2,…), hex(s), date(s), now, formatdate(s,d), year(d), month(d), day(d), hour(d), minute(d), second(d), millisecond(d), isleapyear(n), dayofweek(d), encodedate(y,m,d)

Programación:

Este componente da dos estilos de programación: el estilo tipo fórmula y el estilo script. El primero asume que todas las instrucciones toman la forma de una llamada a una función. El segundo estilo implica que se usa una notación común de un lenguaje de programación. Por ejemplo:

  •     block(defines(‘i’,’n’), set(‘n’,1),
  •           series(set(‘i’,1), val(‘i’)<=5, inc(‘i’), set(‘n’,val(‘n’)*val(‘i’))),
  •           msg(‘5! = ‘+val(‘n’),’result’,0))

es equivalente a:

  •    begin
  •      var ‘i’, ‘n’ end;
  •      $n:=1;
  •      for $i:=1;  $i<=5;  $i++ do
  •        $n := $n*$i;
  •      next;
  •      msg(‘5! = ‘+val(‘n’),’result’,0);
  •    end

Se pueden mezclar ambos estilos, aunque yo no lo suscribo.

El lenguaje de TArtFormula soporta:

  •     Definición de variables
  •     Asignación de variables
  •     Operaciones de incremento y decremento
  •     Regreso de funciones
  •     Instrucciones para bloques (BEGIN, END)
  •     Instrucción IF
  •     Bucle WHILE-UNTIL
  •     Bucle FOR

Funciones de interfaz: msg(‘text’,’caption’,props), input(‘caption’, ‘text’, defvalue)

Como puede verse, este es un microintérprete de expresiones matemáticas y puede ser muy útil para los desarrolladores. A veces, antes de programar, hay que ver si alguien ya hizo ese código que no tenemos o que no nos sale.

Referencias

Descarga de TArtFormula
Intérprete de fórmulas para Delphi