Mi granito de java: Interpreter

jueves, 9 de junio de 2011

Interpreter

Este patrón busca representar un lenguaje mediante reglas gramáticas. Para ello define estas reglas gramáticas y como interpretarlas. Utiliza una clase para representar una regla gramática.
Si un tipo particular de problema se presenta frecuentemente, puede ser provechoso expresar los diferentes casos del problema como sentencias de un lenguaje simple. Se puede, entonces, construir un intérprete que resuelva el problema interpretando dichas sentencias.

Cuando Utilizarlo.

Este patrón se debe utilizar cuando hay un lenguaje que interpretar y se puede interpretar sus palabras como árboles sintácticos abstractos. Para ello, la gramática debe ser simple.
Difícilmente el desarrollador utilice este patrón en algún momento de su vida, lo que no quita que no sea un patrón utilizado. La situación ideal que se debe considerar para aplicar este patrón es que exista un lenguaje sencillo que pueda interpretarse con palabras. El ejemplo más claro es JAVA: este lenguaje permite escribir en archivos .java entendibles por humanos y luego este archivo es compilado e interpretado para que pueda ejecutar sentencias entendibles por una máquina.



Diagrama UML



AbstractExpression: declara una interfaz para la ejecución de una operación.
TerminalExpression: implementa una operación asociada con los símbolos terminales de la gramática
NonterminalExpression: implementa una operación de interpretación asociada con los símbolos no terminales de la gramática.
Context: contiene información global para el interprete.
Client: construye un árbol sintáctico abstracto que representa una sentencia particular en el lenguaje que la gramática define. El cliente construye una expresión


Ejemplo

Luego de leer el ejemplo, se entenderá porque este patrón sólo funciona con lenguajes con reglas sencillas... de hecho este ejemplo no lo pensé yo, lo vi en una página hace muchos años y no recuerdo cual era para colocar la fuente...
Veamos un ejemplo donde se utiliza el interpreter para interpretar los números romanos mediante ciertas reglas matemáticas y convertirlo en un número de escala decimal.

Primero creamos el contexto: esto es una clase donde tiene un input (un String ya que los numeros romanos son letras) y un output (un int ya que la respuesta será un número).


Lo que sigue a continuación es puro algoritmo: si pensamos como funcionan los numeros romanos vamos a encontrar un patrón (pongo como ejemplo con escala de 10 pero es lo mismo para el resto de las escalas): de 1 a 3 es el mismo signo I (ocupa 1 espacio por vez), el 4 es una combinacion del 5 con el 1.... IV (ocupan dos lugares), el 5 es V (ocupa 1 lugar) y el próximo signo que hace combinación es el 9 (IX) que utiliza un signo de una escala mayor.




 Una vez que entendemos el algoritmo y logramos la interpretación correcta, agregar una nueva expresión es muy sencillo. Aca corte en 1000 porque ya se entiende la idea.

Veremos esto en práctica:


Consecuencias
Se puede destacar las siguientes ventajas:
  • Facilidad para cambiar la gramática, mediante herencia, dado que las diferentes reglas se representan con objetos.
  • Facilidad para implementar la gramática, dado que las implementaciones de las clases nodo del árbol sintáctico son similares, pudiendo usarse para ello generadores automáticos de código.
  • Facilidad para introducir nuevas formas de “interpretar” las expresiones en la gramática.
Y las siguientes desventajas:
  • Limitación en el tipo de gramática: si no es simple, es casi imposible implementarlo.
  • No es conveniente utilizarlo si la eficiencia es un punto clave.

3 comentarios:

C. Moya dijo...

Nada más que agradecerte este ejemplo, como bien comentabas siempre sale el mismo ejemplo y le falta algo nuevo, muchas gracias!

José dijo...

no se sumar, crees q pueda aprender así?

Anónimo dijo...

no hace falta una clase?