3 de marzo de 2017

Cálculo de expresiones algebraicas en Python - infix a postfix

Programa "definitivo" que convierte expresiones algebraicas infix a postfix (rpn) y algoritmo de cálculo. 

Ayuda:
  1. Aunque reconoce las funciones de un número sin paréntesis se recomienda usarlos. Por ejemplo, sin(3) en vez de sin3
  2.  Los operadores que pueden usarse son  "+","-","*","/","^" y los paréntesis "(" y ")".
  3. El programa pide al inicio si necesitamos un cálculo con grados (escribe g) o radianes (escribe r).
  4. Listado de funciones: "sin","cos","tan","sec","cosec","cotan","ln","log","exp","sqr","abs","asin","acos","atan"
  5. Reconoce dos constantes: pi y e
  6. Reconoce la notación con exponente: 5*10e4 = 50000. O bien, 5e4. Pero no funcionará con negativos como 5e-4. En ese caso escribir 5*10e-4
  7. En general es preferible usar paréntesis.
  8. El resultado se presenta con 4 decimales.
  9. Puede contener aún algún error.
= = = 

# *-* coding: utf-8 *-*

import math

# http://csis.pace.edu/~wolf/CS122/infix-postfix.htm
# http://hawkee.com/snippet/16636/

# Funciones

def esFloat(a):
    try:
        float(a)
        return True
    except:
        return False

# Fin de funciones

radOGrados=raw_input("Pulsa r para radianes y g para grados: ")
radOGrados=radOGrados[0:1].lower()

if radOGrados=="g":
    factor=4*math.atan(1)/180
else:
    factor=1

seguir = True
while seguir:
  
    cadena=raw_input("Introduce una cadena con operadores válida: ")

    operadores=["+","-","*","/","^","(",")","sin","cos","tan","sec","cosec","cotan","ln","log","exp","sqr","abs","asin","acos","atan"]
    prefoper=[0,0,1,1,2,-1,-1,3,3,3,3,3,3,3,3,3,3,3,3,3,3]
    stack=[]
    postfix=""

    # =====================================
    # Pretratamiento para números negativos
    # =====================================
    cadena2=""
    previo=""
    bandera = 0
    for i in cadena:
        if ((i=="-" or i=="+") and (previo in operadores or previo=="" or not esFloat(previo))
            and previo!="e" and previo!=")" and bandera==0):
            cadena2+="(0"+i
            bandera=1
        elif i in operadores and bandera==1:
            cadena2+=")"+i
            bandera=0
        else:
            cadena2+=i
        previo = i

    if bandera==1:
        cadena2+=")"
    # ======================
    # Fin del pretratamiento
    # ======================

    print "Esta es la cadena a convertir: %s" % (cadena2)

    bandera = 0

    for count, i in enumerate(cadena2):
     
        if bandera>0:
            bandera-=1 # va restando 1
            continue # permite leer funciones
        
        if i in operadores and prefoper[operadores.index(i)]>=0: # todos menos los paréntesis
            indice = operadores.index(i) # indice del elemento nuevo
            # la 5ª regla, la potencia, se consigue con un while en la siguiente en vez de un if.
            while len(stack)>=1 and prefoper[indice]<=prefoper[operadores.index(stack[len(stack)-1])]:
                # y la preferencia del operador es mayor que la del último del stack
                # el igual incluye la asociación, 4ª regla
                postfix+=" "+stack[len(stack)-1] # añade el último al postfix
                stack.pop() # quita de la pila el último
            stack.append(i) # añade el operador al stack
            postfix+=" "
        elif (cadena2[count:count+3]=="sin"
            or cadena2[count:count+3]=="cos"
            or cadena2[count:count+3]=="tan"
            or cadena2[count:count+3]=="sec"
            or cadena2[count:count+3]=="log"
            or cadena2[count:count+3]=="exp"
            or cadena2[count:count+3]=="abs"
            or cadena2[count:count+3]=="sqr"): # si son estas funciones, las añade
            stack.append(cadena2[count:count+3])
            bandera=len(cadena2[count:count+3])-1
        elif (cadena2[count:count+4]=="asin"
            or cadena2[count:count+4]=="atan"
            or cadena2[count:count+4]=="acos"): # si son estas más largas, las añade también
            stack.append(cadena2[count:count+4])
            bandera=len(cadena2[count:count+4])-1
        elif (cadena2[count:count+5]=="cosec"
            or cadena2[count:count+5]=="cotan"): # si son estas más largas, las añade también
            stack.append(cadena2[count:count+5])
            bandera=len(cadena2[count:count+5])-1
        elif cadena2[count:count+2]=="ln": # si es el ln
            stack.append(cadena2[count:count+2])
            bandera=len(cadena2[count:count+2])-1
        elif cadena2[count:count+2].lower()=="pi": # si es el número pi
            postfix+=str(4*math.atan(1))
            bandera=len(cadena2[count:count+2])-1
        elif (cadena2[count:count+4].lower()=="10e-"
            or cadena2[count:count+4].lower()=="10e+"): # si es exponente positivo o negativo
            postfix+="1"+cadena[count+2:count+4]
            bandera=len(cadena2[count:count+4])-1
        elif cadena2[count:count+3].lower()=="10e": # si es exponente positivo sin el signo más
            postfix+="1e"
            bandera=len(cadena2[count:count+3])-1
        elif i=="e" and (cadena2[count+1:count+2] in operadores[0:7] or cadena2[count+1:count+2]==""): # número e
            postfix+=str(math.exp(1))
        elif i=="(":
            stack.append("(") # añade el paréntesis al stack
        elif i==")":
            while len(stack)>1 and stack[len(stack)-1]!="(":
                postfix+=" "+stack[len(stack)-1] # añade al postfix
                stack.pop() # lo quita de la pila
            if stack[len(stack)-1]=="(":
                stack.pop() # lo quita de la pila
        else: # si no está en operadores o no son paréntesis
            postfix+=i
        # imprime la lectura de la cadena, la posición, la cadena postfix y el stack
        # print cadena2.index(i), i, stack, postfix

    # termina añadiendo el stack a la cadena postfix
    while len(stack)>=1:
        if prefoper[operadores.index(stack[len(stack)-1])]>=0:
            postfix+=" "+stack[len(stack)-1]
        stack.pop()

    print "La cadena postfix: %s" % (postfix)

    # ============================
    # Cálculo de la cadena postfix
    # ============================

    pila=[]
    pila=postfix.split(" ")
    calculo=[]
    # print pila

    while len(pila)>0:
     
        item = pila.pop(0)
        # print item
     
        if item=="+" and len(calculo)>1:
            calculo.append(calculo.pop()+calculo.pop())
        elif item=="-" and len(calculo)>1:
            tmp = calculo.pop()
            calculo.append(calculo.pop()-tmp)
        elif item=="*" and len(calculo)>1:
            calculo.append(calculo.pop()*calculo.pop())
        elif item=="/" and len(calculo)>1:
            tmp = calculo.pop()
            calculo.append(calculo.pop()/tmp)
        elif item=="^" and len(calculo)>1:
            tmp = calculo.pop()
            calculo.append(pow(calculo.pop(),tmp))
        elif item=="sin":
            calculo.append(math.sin(factor*calculo.pop()))
        elif item=="cos":
            calculo.append(math.cos(factor*calculo.pop()))
        elif item=="tan":
            calculo.append(math.tan(factor*calculo.pop()))
        elif item=="sec":
            calculo.append(1/math.cos(factor*calculo.pop()))
        elif item=="cosec":
            calculo.append(1/math.sin(factor*calculo.pop()))
        elif item=="cotan":
            calculo.append(1/math.tan(factor*calculo.pop()))
        elif item=="ln":
            calculo.append(math.log(calculo.pop()))
        elif item=="log":
            calculo.append(math.log10(calculo.pop()))
        elif item=="exp":
            calculo.append(math.exp(calculo.pop()))
        elif item=="asin":
            calculo.append(math.asin(calculo.pop())/factor)
        elif item=="acos":
            calculo.append(math.acos(calculo.pop())/factor)
        elif item=="atan":
            calculo.append(math.atan(calculo.pop())/factor)
        elif item=="abs":
            calculo.append(abs(calculo.pop()))
        elif item=="sqr":
            calculo.append(pow(calculo.pop(),0.5))
        elif esFloat(item):
            calculo.append(float(item))
        else:
            pass
        
        print calculo
     
    print "El resultado es: %.4f" % (calculo[0])
   
    seguir2 = raw_input("¿Quieres hacer otro cálculo? [s,n] ")
    if seguir2.lower()=="n":
        seguir = False
   

No hay comentarios:

Publicar un comentario