Documente Academic
Documente Profesional
Documente Cultură
Recuerde que para esta parte del programa se va a suponer que la expresión que dig
ita el usuario no tiene errores, luego se darán consejos para detectarlos.
En los ejemplos de la sección anterior se observó que para realizar la traducción de l
a expresión se necesitan dos pilas de Strings (texto), una para los números y otra p
ara los operadores. JAVA ya posee una clase que maneja pilas, esta clase se llam
a Stack que se encuentra dentro del paquete java.util, lo primero que tenemos qu
e hacer es llamar a esta librería y hacer nuestra clase Parseador6; el inicio de n
uestro programa se debe ver como:
import java.util.*;
public class Parseador{
...
}//fin de Parseador
Para crear una nueva pila se debe definir como un nuevo objeto:
nuevaPila.push(Objeto);
Para sacar un objeto de la pila (recuerde que una pila saca el último objeto que s
e introdujo) utilizamos
nuevaPila.pop();
Para "mirar'' un objeto de la pila sin sacarlo se usa
nuevaPila.peek()
Además se puede preguntar si la pila está vacía con la instrucción
nuevaPila.empty()
que devuelve True si está vacía o False si no lo está.
Para empezar con la clase Parseador, definimos la variable global ultimaParseada
como sigue:
try{
while(pos<expr.length()){
Lo que se haría dentro del while es ver si lo que sigue en la expresión es algo válido
y tomar decisiones dependiendo si es un número, un operador, un paréntesis o una fu
nción.
El código:
tamano=0;
cont=1;
while (tamano==0 && cont<=6){
if(pos+cont<=expr.length() &&
funciones[cont-1].indexOf(expr.substring(pos, pos+cont))!=-1){
tamano=cont;
}
cont++;
}
Hace que el contador vaya de 1 a 6 que es la máxima cantidad de caracteres que tie
ne una función y se inicializa tamano en cero. Luego se pregunta si la posición actu
al (pos) más el contador (cont) es menor de la longitud del texto y si el fragment
o de texto que sigue está en alguna de las funciones, si esto pasa el siguiente te
xto que se tiene que procesar es de tamaño cont.
Ahora se van tomando algunos casos con respecto al tamaño encontrado.
if (tamano==0){
ultimaParseada="0";
throw new SintaxException("Error en la expresión");
Si tamano continúa siendo cero quiere decir que el fragmento de texto que sigue no
coincidió con ninguna función ni con algo válido por lo que se lanza una excepción y se
pone la última expresión parseada en 0.
}else if(tamano==1){
Pero si el tamaño es uno tenemos varias opciones, la primera es que sea un número
if(isNum(expr.substring(pos,pos+tamano)){
fragmento="";
do{
fragmento=fragmento+expr.charAt(pos);
pos++;
}while(pos<expr.length() && (isNum(expr.substring(pos,pos+tamano)) ||
expr.charAt(pos) == '.' || expr.charAt(pos) == ','));
try{
Double.parseDouble(fragmento);
}catch(NumberFormatException e){
ultimaParseada="0";
throw new SintaxException("Número mal digitado");
}
PilaNumeros.push(new String(fragmento));
pos--;
En la primera línea, para preguntar si el caracter es un número se utiliza la función
isNum definida por
}else if (expr.charAt(pos)=='^'){
PilaOperadores.push(new String("^"));
En este caso, simplemente se mete el operador a su pila correspondiente.
Si el caracter fuera un paréntesis de apertura simplemente se mete en la pila de o
peradores.
}else if (expr.charAt(pos)=='('){
PilaOperadores.push(new String("("));
Y si el caracter es un paréntesis de cierre se deben sacar operadores hasta encont
rar una apertura de paréntesis en la pila.
}else if (expr.charAt(pos)==')'){
while(!PilaOperadores.empty() &&
parentesis.indexOf(((String) PilaOperadores.peek()))==-1){
sacaOperador(PilaNumeros, PilaOperadores);
}
if(!((String)PilaOperadores.peek()).equals("(")){
PilaNumeros.push(new String(((String)PilaNumeros.pop()) + " " +
PilaOperadores.pop()));
}else{
PilaOperadores.pop();
}
}
Si el paréntesis de apertura no era el caracter "('' (es decir, era una función) ent
onces la concatena al final del texto en la notación postfija, si era un paréntesis
simplemente lo desecha (recuerde que en notación postfija no hay paréntesis).
Aquí se terminan de procesar todos los elementos posibles de un solo caracter, aho
ra se pasará al caso de dos o más caracteres
}else if(tamano>=2){
fragmento=expr.substring(pos,pos+tamano);
if(fragmento.equals("pi")){
PilaNumeros.push(fragmento);
}else if(fragmento.equals("rnd()")){
PilaNumeros.push("rnd");
}else{
PilaOperadores.push(fragmento.substring(0,fragmento.length()-1));
}
}
pos+=tamano;
}//Fin del while
En este caso se toma la expresión en fragmento, si fragmento es igual a "pi'', lo
metemos en la pila de números; lo mismo hacemos si es rnd().
Cualquier otra posibilidad de tamaño mayor que dos es que sea una función por lo que
se mete en la pila de operadores quitándole el paréntesis. Al final se aumenta la p
osición de acuerdo al tamaño del texto procesado para pasar al siguiente caracter en
la expresión.
Ya cuando se acabó la expresión se debe procesar todo lo que quedó en las pilas en el
proceso final, esto se hace con un while que saque todos los operadores que qued
aron
//Procesa al final
while(!PilaOperadores.empty()){
sacaOperador(PilaNumeros, PilaOperadores);
}
}catch(EmptyStackException e){
ultimaParseada="0";
throw new SintaxException("Expresión mal digitada");
}
ultimaParseada=((String)PilaNumeros.pop());
if(!PilaNumeros.empty()){
ultimaParseada="0";
throw new SintaxException("Error en la expresión");
}
return ultimaParseada;
}//Parsear
Si hubo algún error con las pilas en el proceso se lanza una excepción y se pone a u
ltimaParseada en cero. Al final se devuelve ultimaParseada.
Con esto ya acabamos el programa que convierte una expresión matemática del lenguaje
natural a la notación postfija.
------------------------------------------------------------------
Inicializar la Expresión Postfija EP
For cada caracter C en la expresión infija
{
Switch( C )
{
Case operando : Agregar( EP, C )
Case '(' : Empuja( P, C )
Case ')' : // Sigue Sacando hasta que se encuentre el paréntesis a
bierto
{
while( MirarPila( P ) != '(' )
{
Agregar( EP, Saca( P ) )
}
Saca( P ) // Saca el paréntesis abierto
}
Case operador : // Guardar C hasta que pueda ser determinado dónde colo
carlo
\
while( (!PilaEstaVacia( P )) && (MirarPila( P ) != '(') &&
(Prioridad( MirarPila( P ) ) >= Prioridad( C )) )
{
Agregar( EP, Saca( P ) )
}
Empuja( P, C )
}
}
}
// Agregar a EP los operadores restantes en la Pila
while( !PilaEstaVacia( P ) )
{
Agregar( EP, Saca( C ) )
}
--------------------------------------------
For cada caracter C en la expresión
{
If C es un operador llamado Op
{
Operando2 = Saca( P )
Operando1 = Saca( P )
Resultado = Operando1 Op Operando2
Empuja( P, Resultado )
}
else Empuja( P, C )
}
-------------------------------------------------
Si el lector tiene conocimientos acerca de Expresiones Algebraicas en forma pref
ija, infija, y postfija, entonces puede saltarse esta sección y continuar con la s
ección 4. Algoritmo: Pasar de Infija a Postfija.
Las expresiones algebraicas que usamos en el mundo occidental son las llamadas i
nfijas. Esto quiere decir que los operadores están colocados entre medias de dos o
perandos. Por ejemplo, "2+3". "2" y "3" son operandos y "+" es el operador, el c
ual está situado entre los operandos.
Las expresiones de forma prefija y postfija siguen la misma lógica que la infija,
excepto que la prefija sitúa el operador antes (pre-) de los dos operandos y postf
ija, detrás (post). En el ejemplo anterior, "2+3", la expresión en forma prefija sería
"+23", y en forma postfija, sería "23+".
Otro ejemplo más complejo:
Infija: 2*3+9/3 = 6+9/3 = 6+3 = 9
Prefija: +*23/93 = +6/93 = +63 = 9
Postfija: 23*93/+ = 693/+ = 63+ = 9
La forma de convertir de infija a prefija o postfija es relativamente simple:
1.Se dejan los números en la misma colocación: 2 * 3 + 9 / 3
2.Se "borran" los operadores: 2 3 9 3
3.Ahora se colocan los operadores a la derecha (prefija) o izquierda (postfija)
de los operandos. Para prefija: + * 2 3 / 9 3, y para postfija: 2 3 * 9 3 / +
Las ventajas de tener una expresión algebraica en prefija o postfija en vez de inf
ija son:
Evaluando una expresión en forma prefija se asemeja a programar en ensamblaje: <in
strucción> <operando1> <operando2>
Evaluando una expresión en forma postfija es más fácil a la hora de implementar analiz
adores de lenguajes, calculadoras (como veremos pronto), etc..
Las formas prefijas o postfijas no contienen paréntesis, como los suele tener la f
orma infija. Ejemplo:
Infija: 2*(3+9)/3 = 2*12/3 = 24/3 = 8
Prefija: /*2+393 = / * 2 12 3 = / 24 3 = 8
Postfija: 239+*3/ = 2 12 * 3 / = 24 3 / = 8
La ventaja de la forma infija es que es más sencilla de dictar, ya que es más lógica (
al menos para nosotros humanos) que las formas prefijas y postfijas. Estas dos últ
imas formas necesitan tener la expresión entera antes de poder evaluar, mientras q
ue la infija suele ser evaluada a medida que se vaya obteniendo información. Hay q
uizá otra ventaja al usar la forma infija, los operadores separan una agrupación de
dígitos de otra; así es más fácil de leer los números, que las formas prefijas y postfijas
.