Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: agustinp99 en 10 Octubre 2019, 15:01 pm



Título: Ayuda
Publicado por: agustinp99 en 10 Octubre 2019, 15:01 pm
Hola buen dia, tengo una duda con un ejercicio que me dieron. Tengo que armar una calculadora de polaca inversa y estoy tratando de, a partir de la ecuación brindada por el usuario, tomar el primer numero, el segundo, luego el signo y asi sucesivamente. El problema es que no logro hacerlo, ya que no encuentro alguna funcion o algo que me detecte si el caracter es un numero o un signo (ya que si es un espacio lo salteo con un if antes). Si alguien puede tirarme algun consejo se lo agradeceria!


Título: Re: Ayuda
Publicado por: agustinp99 en 10 Octubre 2019, 15:01 pm
Esto es en codigo C


Título: Re: Ayuda
Publicado por: K-YreX en 10 Octubre 2019, 15:09 pm
En vez de poner dos mensajes seguidos es mejor que modifiques el primero e incluyas lo que necesites.

Para lo que quieres hacer tienes una librería que es <ctype.h> en la que tienes funciones como <isdigit()>, <isspace()>, <ispunct()>, <isgraph()>, etc. Vamos, para saber qué tipo de carácter tienes.
Te dejo un enlace donde aparecen las funciones que tienes y una tabla para que veas qué caracteres se consideran de puntuación y cuáles se consideran graficables, etc.
http://www.cplusplus.com/reference/cctype/?kw=cctype


Título: Re: Ayuda
Publicado por: agustinp99 en 10 Octubre 2019, 15:16 pm
Uh perfecto, ahora averiguo bien. Muchisimas gracias


Título: Re: Ayuda
Publicado por: agustinp99 en 10 Octubre 2019, 15:29 pm
En vez de poner dos mensajes seguidos es mejor que modifiques el primero e incluyas lo que necesites.

Para lo que quieres hacer tienes una librería que es <ctype.h> en la que tienes funciones como <isdigit()>, <isspace()>, <ispunct()>, <isgraph()>, etc. Vamos, para saber qué tipo de carácter tienes.
Te dejo un enlace donde aparecen las funciones que tienes y una tabla para que veas qué caracteres se consideran de puntuación y cuáles se consideran graficables, etc.
http://www.cplusplus.com/reference/cctype/?kw=cctype
Tengo otra duda, porque yo tengo que ingresar la ecuacion en notacion polaca al programa. Como puedo hacer para ir guardando el primer numero, el segundo y su signo? He visto que lo hacian con pilas pero mi profesor me dijo que lo trate de hacer de otra forma pero no me sale una idea

ESTO ES LO QUE VENIA PENSANDO

#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main()
{
   char c[100];
   char str[10];
   int i;
   int u;
   printf ("Ingrese notacion polaca inversa: ");
   scanf ("%s",c);
      for (i=0;i<= strlen(c);i++){
         if (isdigit(c) && c!= " "){
         str=c;
         u++;
         }   
   }
   printf ("El numero guardado es %s", str);
   }


Título: Re: Ayuda
Publicado por: K-YreX en 11 Octubre 2019, 01:33 am
No está mal aunque hay que pulir un par de cosas:
Antes de nada, los códigos ponlos entre etiquetas de Código GeSHi porque sino los índices no se ven y la i entre corchetes se convierte en cursiva y la u entre corchetes, en subrayado...
  • Para pedir una cadena al usuario no se usa <scanf()>, se usa <fgets()>
Código
  1. #define SIZE 100 // definimos un maximo de longitud para la cadena. Esto se pone justo despues de las librerias
  2. char cadena[SIZE];
  3. printf("Introduce una cadena: ");
  4. fgets(cadena, SIZE, stdin);
El siguiente problema es que al introducir una cadena y pulsar ENTER, el ENTER también se guardará en la cadena. Esto lo arreglamos rápido así:
Código
  1. cadena[strlen(cadena)-1] = '\0';
  • La segunda condición de tu bucle <while()> es innecesaria. Preguntas si <c> es un dígito Y si no es un espacio... Siempre que se cumpla que es un dígito, se cumplirá también que no es un espacio. Y si no es un dígito, la segunda condición dará igual.

Si lo que quieres conseguir es el resultado de calcular la expresión en notación polaca inversa, te falta esa parte.


Título: Re: Ayuda
Publicado por: dijsktra en 11 Octubre 2019, 15:37 pm
Tengo otra duda, porque yo tengo que ingresar la ecuacion en notacion polaca al programa. Como puedo hacer para ir guardando el primer numero, el segundo y su signo? He visto que lo hacian con pilas pero mi profesor me dijo que lo trate de hacer de otra forma pero no me sale una idea


Las pilas por debajo están simuladas con vectores... O sea, que expones los vectores que se ocultarían en la implementación de una pila normalemente...

EDITO
En esta versión implemento un analizador de notación polaca inversa.
Esto es, primero los operandos y después el operador. Como en (3 4 -)


Código
  1. /*
  2.  
  3. An stack can be simulated by a vector
  4.  
  5. +-----+
  6. |     |
  7. +-----+ N
  8. |  2  |
  9. +-----+
  10. |  1  |
  11. +-----+
  12. |  +  |
  13. +-----+ 0
  14.  
  15.  
  16. */
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <assert.h>
  20. #include <string.h>
  21.  
  22. #define MAX 100000
  23. #define DEBUG
  24.  
  25. int polish (const char V[][25], const int N)
  26. {
  27.  int n,top;
  28.  int S[MAX];
  29.  for( n=top=0;n<N;n++)
  30.  {
  31. #ifdef DEBUG
  32.    int i;
  33.    for(  i=n ; i < N ; i++) printf("%s ",V[i]);
  34.    printf("\n\n");
  35. #endif
  36.  
  37.    if (strtol(V[n],NULL,10))
  38.      S[top++]=atoi(V[n]);
  39.    else
  40.      {
  41. assert(top>1);
  42. const int a = S[--top];
  43. const int b = S[--top];
  44. if (!strcmp(V[n],"+"))
  45.  S[top++] = a + b;
  46. else if (!strcmp(V[n],"-"))
  47.  S[top++] = a - b;
  48. else if (!strcmp(V[n],"*"))
  49.  S[top++] = a * b;
  50. else if (!strcmp(V[n],"/"))
  51.       {
  52. assert(!b);
  53. S[top++] = a / b;
  54.       }
  55.      }
  56. #ifdef DEBUG
  57.    for(  i=0 ; i < top-1 ; i++) printf("%d ",S[i]);
  58.    printf("%d\n\n",S[top-1]);
  59. #endif
  60.  };
  61.  assert(top==1);
  62.  return S[--top];
  63. }
  64.  
  65.  
  66. int main(int argc, char* args[])
  67. {
  68.  char V[MAX][25];
  69.  
  70.  int N;
  71.  for(N=0;scanf("%s",V[N])==1 && strcmp(V[N],"\n");N++);
  72.  printf("%d\n",polish(V,N));
  73.  return 0;
  74.  
  75. }

Y un ejemplo de ejecuci'on. Para la expresi'on en notaci'on polaca
Código:
3 4 + 7 * 9 10 5 6 + * - - 

En la ejecución se da la evoluición de la cadena de entrada y del estado de la pila...
Código:
3 4 + 7 * 9 10 5 6 + * - - 

3

4 + 7 * 9 10 5 6 + * - -

3 4

+ 7 * 9 10 5 6 + * - -

7

7 * 9 10 5 6 + * - -

7 7

* 9 10 5 6 + * - -

49

9 10 5 6 + * - -

49 9

10 5 6 + * - -

49 9 10

5 6 + * - -

49 9 10 5

6 + * - -

49 9 10 5 6

+ * - -

49 9 10 11

* - -

49 9 110

- -

49 101

-

52






Título: Re: Ayuda
Publicado por: agustinp99 en 11 Octubre 2019, 17:05 pm
Las pilas por debajo están simuladas con vectores... O sea, que expones los vectores que se ocultarían en la implementación de una pila normalemente...

¡Atención ! mi solución entiende que notación polaca es   + 3 4 , en lugar de 3 + 4 o 3 4 +.
(Solo hay que cambiar el orden de lectura de los operandos)

 El programa solo evalua una expresion polaca metida correctamente... y sin parantesis...

no se puede + 3 (4+4) , sino que hay que escribir + 3 4 4 +

REPITO, no se si es lo que buscas...

Código
  1. /*
  2.  
  3. An stack can be simulated by a vector
  4.  
  5. +-----+
  6. |     |
  7. +-----+ N
  8. |  2  |
  9. +-----+
  10. |  1  |
  11. +-----+
  12. |  +  |
  13. +-----+ 0
  14.  
  15.  
  16. */
  17.  
  18.  
  19. #include <cstdlib>
  20. #include <iostream>
  21. #include <cassert>
  22. #include <cstring>
  23.  
  24. using namespace std;
  25. int polish(char S[][25],  int N)
  26. {
  27.  for(N; N > 1 ; )
  28.    {
  29.      const int b = atoi(S[--N]);
  30.      const int a = atoi(S[--N]);
  31.      const char op = *S[--N];
  32.      cout << b << " " << a <<  " " << op << endl;
  33.      switch (op) {
  34.      case '+': sprintf(S[N++],"%d",a+b);
  35. break;
  36.      case '-': sprintf(S[N++],"%d",a-b);
  37. break;
  38.      case '*': sprintf(S[N++],"%d",a*b);
  39. break;
  40.      case '/':
  41. assert(b!=0);
  42. sprintf(S[N++],"%d",a/b);
  43. break;
  44.      }
  45.    }
  46.  return atoi(S[N-1]);
  47. }
  48.  
  49.  
  50.  
  51. #define MAX 100000
  52. int main(int argc, char* args[])
  53. {
  54.  char S[MAX][25];
  55.  int top ;
  56.  for(top=0;scanf("%s",S[top])==1 && strcmp(S[top],"\n");top++ );
  57.  printf("%d\n",polish(S,top));
  58.  return 0;
  59. }

Y un ejemplo de ejecuci'on.

Para la expresi'on en notaci'on polaca
Código:
- 15 + 1 * 2 + 3 4

el programa da
Código:
4 3 +
7 2 *
14 1 +
15 15 -
0


Hola!! Si me sirve un monton, te hago una pregunta nomas, que hace el args en el main? porque yo uso el argv pero nose si es lo mismo

Ademas de que no entiendo que hace el for de esa forma


Título: Re: Ayuda
Publicado por: agustinp99 en 11 Octubre 2019, 19:33 pm
No está mal aunque hay que pulir un par de cosas:
Antes de nada, los códigos ponlos entre etiquetas de Código GeSHi porque sino los índices no se ven y la i entre corchetes se convierte en cursiva y la u entre corchetes, en subrayado...
  • Para pedir una cadena al usuario no se usa <scanf()>, se usa <fgets()>
Código
  1. #define SIZE 100 // definimos un maximo de longitud para la cadena. Esto se pone justo despues de las librerias
  2. char cadena[SIZE];
  3. printf("Introduce una cadena: ");
  4. fgets(cadena, SIZE, stdin);
El siguiente problema es que al introducir una cadena y pulsar ENTER, el ENTER también se guardará en la cadena. Esto lo arreglamos rápido así:
Código
  1. cadena[strlen(cadena)-1] = '\0';
  • La segunda condición de tu bucle <while()> es innecesaria. Preguntas si <c> es un dígito Y si no es un espacio... Siempre que se cumpla que es un dígito, se cumplirá también que no es un espacio. Y si no es un dígito, la segunda condición dará igual.

Si lo que quieres conseguir es el resultado de calcular la expresión en notación polaca inversa, te falta esa parte.
Voy entendiendo, el problema es que sigo sin poder guardar lo numeros en alguna variable, ya que hago un for que vaya desde 1 hasta el argc y lo que quiero hacer ahora es preguntar si el argv es un digito(que eso sé como se hace) y luego, a partir de ese numero guardarlo en una variable entera, por ejemplo x. Al hacer esta iteracion con la condicion, si yo tengo mas de dos numeros ya la variable x tomaria solamente el ultimo y no se como hacer para poder guardar todo en distintas variables


Título: Re: Ayuda
Publicado por: K-YreX en 11 Octubre 2019, 23:37 pm
La función <main()> puede recibir dos parámetros: un <int> y un <char*[]>. El primero indica el número de parámetros que le pasas al programa cuando lo ejecutas desde línea de comandos y el segundo es como una matriz donde cada fila es uno de los parámetros y cada columna es una letra. El nombre que le des da igual, siempre es lo mismo.

Como te han dicho, una pila internamente se puede implementar con un <array>. Entonces lo que tienes que hacer es un <array> de números en donde guardar los números hasta encontrar un operador. Yo estoy hablando para una notación polaca inversa, es decir, 3 4 +.
Te dejo el siguiente pseudocódigo. Esto es para que lo entiendas. Cuando entiendas cómo funciona tienes que pasarlo a C ya que el pseudocódigo no está escrito en C.
Código:
INICIO
    posicionActual := 0
    PEDIR expresion
    PARA i := 0 HASTA expresion.longitud - 1 HACER
        SEGUN expresion[i] HACER
            numero:
                numeros[posicionActual++] = expresion[i]
            +:
                numeros[posicionActual - 2] := numeros[posicionActual - 2] + numeros[posicionActual - 1]
                posicionActual := posicionActual - 2
            *:
                numeros[posicionActual - 2] := numeros[posicionActual - 2] + numeros[posicionActual - 1]
                posicionActual := posicionActual - 2

            //...igual para el resto de operadores

        FIN SEGUN
    FIN PARA

    MOSTRAR numeros[0]
FIN


Título: Re: Ayuda
Publicado por: Serapis en 13 Octubre 2019, 08:46 am
No entiendo por qué (los profesores) se empeñan en intentar profundidar en teoría de compiladores en el primer año, cuando el alumno, todavía está demasiado verde... Una introducción estaría bien, pero sin profundizar.

La notación polaca inversa, tiene bastante trabajo incluso en su forma más simple...
Al lío...

La notación que usamos 'humanamente' se llama infija, esto es el operador aparece en medio de los operandos...
Siguiendo la idea, es fácil entender que el operador puede aparecer también al inicio o al final de ambos operandos, lo que da nombre a la notación prefija (o polaca) y la postfija (o polaca inversa).

Se usa bastante en los compiladores, porque simplifica las expresiones de cara a poder usarlos de forma cómoda y rápida desde la pila, aunque no siempre resulta eficiente.
...Tu caso (crear una calculadora que opere así), entra en el caso de los intérpretes, no será compilado, pero todavía puede resultar útil...

La forma de hacerlo eficiente, es diseñado a bajo nivel, no delegando a funciones, la ventaja es que puedes acomodar el algoritmo al completo según tus necesidade,s lo cual es siempre más eficiente que tener que diseñar y luego adaptar tu algoritmo, a lo que te dejen hacer las funciones que disponga tu lenguaje...

en esta implementación como tiene más propósito ilustrativo de cara a aprender, vamos a considerar que cada token se compone de solo 1 carácter (para no perdernos en detalles más alejados), una vez se comprenda y domine el tema, puede ampliarse, para reconocer números incluso en  diferentes bases numéricas...

Así de entrada hay que definir alguna enumeración y algún array, para mantener dicha eficiencia arriba...
Código:
Enumeracion AtributosChar
    ATRIBCHAR_ERROR = -1                        // cualquier otro carácter (no considerado a continuación)...
    ATRIBCHAR_IGNORA = 0                        // espacios
    
    ATRIBCHAR_OPERADOR_PRECEDENCIA_1 = 1        //  */
    ATRIBCHAR_OPERADOR_PRECEDENCIA_2 = 2        //  +-
    ATRIBCHAR_OPERADOR_MAX = 2                  // Valor mayor de la precedencia (no precedencia mayor, pués están en orden inverso).
    // Este valor se consigna desde NextToken, pero no se asigna en la tabla.
    ATRIBCHAR_OPERADOR_UNARIO = 3               // +- (pero cuando va delante de otro operador o de un paréntesis de apertura,o al comienzo.
    
    ATRIBCHAR_DELIMITADOR_ABRE = 5              // (
    ATRIBCHAR_DELIMITADOR_CIERRA = 6            // )
    ATRIBCHAR_OPERANDO = 7                      // [±] (A-Z|a-z|0-9) ...aunque solo operamos con dígitos, porque luego quieres calcular resultados...
fin Enumeracion

Estructura RegistroTblSimb
    string Valor                      
    AtributosChar Token
    //real Calculo                    
End Type
La enumeración la usaremos para identificar cada tipo de carácter derivándolo a nuestras necesidades...
La estructura, dispuesta en un array hará las veces de la tabla de símbolos que será usada  para almacenar los tokens cuando sean analizados.

Ahora procede crear los arrays estáticos con sus valores...
Esto típicamente se hace en el Main...

Primero declaramos los arrays que vamos a usar como apoyo...
Código:
Array de Atributoschar s_Tabla(0 a 255) 
array de RegistroTblSimb s_TblSimb(0 a 79)      
//80 parece un número suficiente para probar la calculadora, será raro que en una calculadora sencilla haya expresiones con más de 80 tokens...
//pero vamos si se espera que suceda, basta ampliar el número a lo que se considere adecuado.

// y rellenamos ya el array de atributos de los caracteres...
funcion Main
    llamada a CrearTabla
fin funcion

// se deja en una función aparte para mantener despejado 'main', y para centrar la función a lo que dice su nombre y hace.
funcion CrearTabla
    int k
    
    // Todos se marcan como error, luego los válidos con su valor adecuado...
    Bucle para k desde 0 hasta 255
        s_Tabla(k) = ATRIBCHAR_ERROR                // todos excepto los que siguen.              
    Siguiente

    // Digitos:
    Bucle para k desde 48 hasta 57
        s_Tabla(k) = ATRIBCHAR_OPERANDO             // 0-9
    siguiente
    
    // Caracteres:
    Bucle para k desde 65 hasta 90
        s_Tabla(k) = ATRIBCHAR_OPERANDO             // A-Z
        s_Tabla(k + 32) = ATRIBCHAR_OPERANDO        // a-z
    siguiente
    
    // Separador:
    s_Tabla(32) = ATRIBCHAR_IGNORA                  // el espacio (también se podría poner l espacio duro)

    // Delimitadores:
    s_Tabla(40) = ATRIBCHAR_DELIMITADOR_ABRE        //  (
    s_Tabla(41) = ATRIBCHAR_DELIMITADOR_CIERRA      //  )
    
    // Operadores:
    s_Tabla(42) = ATRIBCHAR_OPERADOR_PRECEDENCIA_1  //  *
    s_Tabla(43) = ATRIBCHAR_OPERADOR_PRECEDENCIA_2  //  +
    s_Tabla(45) = ATRIBCHAR_OPERADOR_PRECEDENCIA_2  //  -  
    s_Tabla(47) = ATRIBCHAR_OPERADOR_PRECEDENCIA_1  //  /
fin funcion

Nos bastan esos 4 operadores y los paréntesis, para ejemplificar...
Ahora lo siguiente será una simple función que será el analizador léxico...
Su función es pedir tokens que se extraen d ela entrada y los guarda en la tabla de símbolos. es una función muy simplificada dado que para el caso, los tokens se limitan a:
Operando, Operador, ParentesisAbre, ParentesisCierra
Código:
// El scan léxico: Debe reconocer cada tipo de token válido.
//  que almacena en el array s_TblSimb
// Devuelve la cantidad de tokens hallados, si devuelve 0, es que hubo algún problema:
//   1 - El string de entrada estaba vacío: ""
//   2 - Hay caracteres 'raros' no considerados en nuestra tabla: 5+3-6+?*@   (? y @ no sons ímbolos admitidos en 'nuestra gramática').
//   3 - La expresion está mal formada (error sintáctico):  5****3;   4+345;  5+(((3-2)
int = Funcion ScanLex(string Expr )
    int k, n
    string st
    AtributosChar tk  
    
    // n = 1 ciertos lenguajes considera el primer carácter de un string, como el 1, no el 0, descomentar en tal caso...
    Hacer mientras (n <= Expr.Size)
        st = NextToken(Expr, n, tk)

        Si (tk = ATRIBCHAR_ERROR)
            devolver 0
        Osi (tk = ATRIBCHAR_OPERADOR_UNARIO)
            // Un operador unario solo afecta a un operando, simulamos que el otro es 0
            // y lo encerramos entre paréntesis, para forzar la precedencia dentro.
            // ejemplo: 5*-1 = -5  ó -1*5 = -5   También si se usa el signo +   5*+7
            // queda como si fuera: 5*(0-1)   (0-1)*5    5*(0+7)
            // la misma técnica vale para otros operadores unarios como not(x), cos(x), Abs(x), etc...  
            s_TblSimb(k).Valor = "("
            s_TblSimb(k).Token = ATRIBCHAR_DELIMITADOR_ABRE
            k = (k + 1)
            s_TblSimb(k).Valor = "0"
            s_TblSimb(k).Token = ATRIBCHAR_OPERANDO
            k = (k + 1)
            s_TblSimb(k).Valor = st
            s_TblSimb(k).Token = ATRIBCHAR_OPERADOR_PRECEDENCIA_2
            k = (k + 1)
            // y ahora pedimos el operando suelto, para luego cerrar el paréntesis, antes de seguir...
            st = NextToken(Expr, n, tk)
            
            s_TblSimb(k).Valor = st
            s_TblSimb(k).Token = tk
            k = (k + 1)
            s_TblSimb(k).Valor = ")"
            s_TblSimb(k).Token = ATRIBCHAR_DELIMITADOR_CIERRA
        YSiNo
            // introduccir los datos del token en la tabla de símbolos.
            s_TblSimb(k).Valor = st
            s_TblSimb(k).Token = tk
        Fin si

        k = (k + 1)
    Siguiente
    
    devolver k
fin Funcion

Ahor aprocede poner como sería la función para obtener el siguiente token... es también muy simple, ya que de entrada cada token ocupa un único carácter... y tenemos pocos tokens distintos.
Código:
// NOTA: Se considera token de 1 solo carácter
//    operando:     [±] A-Z|a-z|0-9
//    operador:     *|/|+|-
//    parentesis:    (|)
//  Para algo más exahustivo, habría que reconocer:
//    - identificador y número
//    - operadores de 1 o más símbolos (como en '>=')
// OJO: No se verifica si se alcanza el límite de la expresión,
//   lo cual puede suceder con expresiones mal formadas
//   (con caracteres no esperados a la derecha del último caracter válido).
// Los 3 parámetros son pasados por referencia...
string = funcion NextToken(string Expr, int Index, AtributosChar tkUltimo)
    string s
    AtributosChar ac
    
    Hacer
        s = Expr.Substring(Index, 1)   // tomamos el carácter en la posición index
        ac = s_Tabla(s.toByte)    // Evaluamos de qué tipo es el caracter.
        // en ciertos lenguajes caracter y byte se usan indistintamente, en otros marca error,
        // luego exige una conversión a bytes implícita, explícita, por casting o por coerción...
        Index += 1
    repetir mientras (ac = ATRIBCHAR_IGNORA) // saltar espacios
     // cualquier otro carácter no aceptado será detectado como error, tras la devolución.
    
    // Detectar si es un operador unario... Esto se sabe si hay otro operador delante.
    //  casos: *+2,  /-2,  +-4,  -+4,  (-1, +1    (total: 6 casos distintos)
    // es decir aparece un ± detrás de otro operador, de un paréntesis de apertura, o al comienzo de expresión.
    Si (ac = ATRIBCHAR_OPERADOR_PRECEDENCIA_2)  // solo signos +- que son los posibles unarios en esta implementación.
        Si ((tkUltimo <= ATRIBCHAR_DELIMITADOR_ABRE)  // es posible hacerlo así de simple,
             // porque se ha estudiado el orden de los tipos en la enumeración. Así acepta los 6 casos.
            ac = ATRIBCHAR_OPERADOR_UNARIO  
            //marcamos temporalmente como un token de operador unario,
        Fin si
    fin Si
    
    tkUltimo = ac
    Devolver s
Fin Funcion

Ahora toca hacer la función que implementa el algoritmo de la notación polaca inversa.
Para qien tenga bien a fondo conocimeinto en teoría de compiladores, básicamente puede lograr lo mismo mediante la técnica de 'compilación recursiva descendente'... que eso sería una analizado sintáctico...
Pero dado la sencillez d ela gramática usada, aquí se deja constancia de una sencilla implementación del algoritmo de Dijkstra (de Edsger Dijkstra, no nuestro forista: Dijsktra)

Como bien te han dicho una pila puede implementarse con un array, lo mismo que una cola, aquí incluso en un string... fíjate al caso que la diferencia entre pila y cola así usados será que la pila añade en orden inverso:
// cola: "A      ";  "An     "; "Ant    "; "Anti   "; "Antig  "; "Antigu "; "Antiguo"
// pila: "      A"; "     nA"; "    tnA"; "   itnA"; "  gitnA"; " ugitnA";  "ougitnA"
Código:
// Ini y fin, son el punto de la tabla de símbolos entre los que queramos analizar.
// Si siempre va a ser desde el comienzo, ini puede dejar de ser un parámetro para ser una variable interna de la función con valor 0.
// Fin en cualquier caso, indica el final, o para la situación anterior la cantidad-1 de tokens almacenados durante el análisis léxico.
string = Funcion DijkstraRPN(int Ini, int Fin)
    AtributosChar AtChar As , PrevTkChar         // tipo de token.
    string cola, int ixCola                                // cola de operandos (y operadores) y su puntero.
    string pila, int ixPila                                  // pila de operadores y su puntero
    int IxMax =   (Fin - Ini + 1)
    
    ixPila = (IxMax + 1)
    ixCola = 0  // 1, si el lenguaje considera el primer carácter de un string, como 1.
    // creamos el espacio máximo de cada string, en realidad cola, nunca crece mucho...
    // Si se opera con arrays de chars, incluso mejor, cuando el lenguaje no supone pérdida de eficiencia en conversiones 'tontas' entre char y byte.
    // la reserva de espacio en el string, al giaul que en un array, previene que la modificación de su tamaño exija contruir y copiar el contenido en otro, como sucede con append, etc...
    pila = Espacios(IxMax)
    cola = espacios(IxMax)
    
    Hacer mientras (Ini <= Fin)
        AtChar = s_TblSimb(Ini).Token
        Seleccionar  Caso para AtChar
            Caso ATRIBCHAR_OPERANDO   // accion: añadir a la cola
                cola.Replace(ixCola, 1) = s_TblSimb(Ini).Valor   // remplaza el carácter en la posición indicada.
                ixCola += 1                
            Caso ATRIBCHAR_DELIMITADOR_ABRE  // accion: Añadir a la pila.
                ixPila -= 1
                pila.Replace(ixPila, 1) = s_TblSimb(Ini).Valor
            Caso ATRIBCHAR_DELIMITADOR_CIERRA
                // Accion: mover operadores de la pila a la cola, hasta que
                //   se encuentre el paréntesis de apertura de su mismo anidamiento.
                Hacer mientras (s_Tabla(pila.Substring(ixPila, 1).ToByte) <> ATRIBCHAR_DELIMITADOR_ABRE)
                    cola.Replace(ixCola, 1) = pila.Substring(ixPila, 1)  // mover elemento a la cola
                    ixCola += 1
                    pila.Replace(ixPila, 1) = " "   // eliminar de la pila (basta poner un espacio)
                    ixPila += 1
                    si (ixPila = IxMax) Salir del bucle
                Repetir
                
                pila.replace(ixPila, 1) = " "          // eliminar paréntesis de apertura encontrado.
                ixPila += 1
            Resto de casos // operadores: considerar precedencia.
                // Accion: Mover a la cola, mientras el operador en la pila siendo examinado
                // sea de menor o igual precedencia que el operador actual.
                Si (ixPila <= IxMax)  // es habitual que la pila quede vacía...
                    PrevTkChar = s_Tabla(pila.Substring(ixPila, 1).ToByte)

                    Hacer mientras (PrevTkChar <= AtChar)  // y (PrevTkChar <> ATRIBCHAR_DELIMITADOR_ABRE)), pero el diseño en la enumeración ya considera esto, con la previa condición.
                        cola.Replace(ixCola, 1) = pila.Substring(ixPila, 1)  // mover elemento a la cola
                        ixCola += 1
                        pila.Replace(ixPila, 1) = " " // eliminar de la pila (basta poner un espacio)
                        ixPila += 1
                        si (ixPila > IxMax) Salir del bucle
                        PrevTkChar = s_Tabla(pila.Substring(ixPila, 1).ToByte)
                    Repetir  // el bucle se parece bastante al anterior, pero no llegan a ser iguales...
                Fin si
                
                ixPila -= 1   // la pila crece hacia abajo...
                pila.Replace( ixPila, 1) = s_TblSimb(Ini).Valor
        Fin seleccion de casos
        imprimir cola tabulador pila  // solo para ver como evoluciona...
        Ini += 1
    Repetir
    
    // Pasar contenido restante de la pila a la cola (pero si quedan paréntesis = error).
    //   En realidad, no se espera este error, toda vez que pasó un scan léxico.
    //   Sin embargo se deja por si se decide modificar el algoritmo para aceptar directamente
    // el texto de la expresión como parámetro, e ignorar el scan léxico previo....
    pila = pila.Right(IxMax - ixPila + 1)  // la pila descarta los espacios a la izquierda...
    Si (pila.Contiene( "(")) = FALSE)
        cola.Replace(ixCola) = pila    // copia el contenido de la pila en la cola, a partir de la posición indicada.
        ixCola += pila.Size
        imprimir cola tabulador pila  // solo para ver el resultado final ...
        devolver cola.Left(ixCola - 1)   // devuelve el contenido de la cola, descartando los espacios a su derecha.
    fin si
fin funcion

Y finalmente como lo que se te pide es una calculadora, y no solo la conversión de la notación, pués hale esto completa el intérprete...
Código:
// Calcula el valor de una expresión postfija.
//   A - Se supone que es una expresión aritmética, donde todos los operandos son números.
//   B - Si se admite que fueran identificadores, se supone que estos o bien refieren direcciones de memoria o bien su valor se haya en una tabla hash (por ejemplo),
//     luego antes de guardar a la 'pila' se debe dereferenciar el identificador. Por ejemplo:   s_Temp(i) = tHash(identificador)
//     Por eso si el propósito es meramente un ejercicio, es preferible ceñirse al caso 'A', exclusivamente y usar solo dígitos.
// devuelve el valor calclado, como puede tener decimales... el array temporal conviene que así sea...
real = Funcion CalcularRPN(string RPN)
    int k, i, X, Y
    AtributosChar  tk
    string s
    real Temp(0 a 79) // mismo tamaño que dejamos para la tabla de símbolos, adaptar según necesidades..
    // incluso conviene mover este array al nivel del módulo...
    
    bucle para k desde 0 hasta RPN.Size-1   // nuevamente si el lenguaje trata el primer char de la cadena , como 1, ira de 1 a rpn.size
        s = RPN.Substring( k, 1)   // toma el enésimo carácter de la cadena (o array)
        tk = s_Tabla(s.ToByte)
        // Mientras se obtengan operandos se guardan en temp
        // cuando aparece un operador se toma los dos últimos operandos guardados y se opera con ellos
        Si (tk <= ATRIBCHAR_OPERADOR_MAX)
            // sacar dos últimos operandos de la pila
            i -= 2
            X = Temp(i)
            Y = Temp(i + 1)
            
            // calcular y almacenar en la pila.
            Seleccionar Caso para s
                Caso "*"
                    Temp(i) = (X * Y)
                Caso "/"
                    Temp(i) = (X / Y)
                Caso "+"
                    Temp(i) = (X + Y)
                Caso "-"
                    Temp(i) = (X - Y)
            fin seleccion casos
        Ysino // Osi (tk = ATRIBCHAR_OPERANDO)
            //  una expresión RPN (bien formada) solo tiene operandos y operadores.
            // leer y almacenar operandos hasta encontrar un operador...
            Temp(i) = s
        // YSiNo
         //  solo si se espera errores en caso de expresion malformada... activar el 'Osi' del caso anterior y activar este 'YSino'
        Fin Si
        i += 1
    Siguiente
    
    devolver Temp(0)  // i acabará valiendo 1
Fin funcion

Se invocaría como:
Código:
funcion Main (string command)
    string rpn
    real valor
    llamada a CrearTabla

    rpn = ExpToRPN(command)
    Si (rpn.size > 0)
        valor = CalcularRPN(rpn)
        imprimir rpn tabulador valor
    Ysino
        Mensaje "el texto de la expresion no es correcto..."
    fin si
fin funcion

string = Function ExpToRPN(string Expr )
    int k As Integer
    
    
    k = ScanLex(Expr)
    si (k = 0) devolver ""
    devolver DijkstraRPN(0, k - 1)
fin Funcion

Y solo resta algún ejemplo:
Expr Infija: 3+6*2
Expr Postfija: 362*+   Valor: 15

Expr Infija: 3+(6*2)
Expr Postfija: 362*+   Valor: 15

Expr Infija: (3+6)*2
Expr Postfija: 36+2*   18

Expr Infija: 9*3+2-5-4*3
Expr Postfija: 93*2+5-43*-  Valor: 12

Expr Infija: 3+4-5*7-(4/2)*3
Expr Postfija: 34+57*-42/3*-   valor: -34

Expr Infija: 5-3*8/2+(5-4/2)+(8+(5*3)-1+(6/3))+5*3
Expr Postfija: 538*2/-542/-+853*+1-63/++53*+   Valor: 35

Y un ejemplo con un operador unario (la misma expresión previa vale)
--------------------------------------------!!-3!!--------- (se ha añadido un signo - delante del 3.
Expr Infija: 5-3*8/2+(5-4/2)+(8+(5*-3)-1+(6/3))+5*3
Expr Postfija: 538*2/-542/-+8503-*+1-63/++53*+  Valor: 5

Y un ejemplo de la evolución, para que al iplementarlo, puedas ir verificando cada cosa:
Expr Infija: 5-3*8/2+(5-4/2)+(8+(5*3)-1+(6/3))+5*3

--- Cola ------------------------------------------------ Pila ---
-------------------------------------------------------------------
5                                                                        
5                                                                        -
53                                                                       -
53                                                                      *-
538                                                                     *-
538*                                                                    /-
538*2                                                                   /-
538*2/-                                                                  +
538*2/-                                                                 (+
538*2/-5                                                                (+
538*2/-5                                                               -(+
538*2/-54                                                              -(+
538*2/-54                                                             /-(+
538*2/-542                                                            /-(+
538*2/-542/-                                                             +
538*2/-542/-+                                                            +
538*2/-542/-+                                                           (+
538*2/-542/-+8                                                          (+
538*2/-542/-+8                                                         +(+
538*2/-542/-+8                                                        (+(+
538*2/-542/-+85                                                       (+(+
538*2/-542/-+85                                                      *(+(+
538*2/-542/-+853                                                     *(+(+
538*2/-542/-+853*                                                      +(+
538*2/-542/-+853*+                                                     -(+
538*2/-542/-+853*+1                                                    -(+
538*2/-542/-+853*+1-                                                   +(+
538*2/-542/-+853*+1-                                                  (+(+
538*2/-542/-+853*+1-6                                                 (+(+
538*2/-542/-+853*+1-6                                                /(+(+
538*2/-542/-+853*+1-63                                               /(+(+
538*2/-542/-+853*+1-63/                                                +(+
538*2/-542/-+853*+1-63/+                                                 +
538*2/-542/-+853*+1-63/++                                                +
538*2/-542/-+853*+1-63/++5                                               +
538*2/-542/-+853*+1-63/++5                                              *+
538*2/-542/-+853*+1-63/++53                                             *+
538*2/-542/-+853*+1-63/++53*+             *+
538*2/-542/-+853*+1-63/++53*+   35

Y bueno, como cada lenguaje tiene sus pecualiaridades, cualquiere que lo implemente en el de su interés, que considere que a veces hay siempre un ± 1 bailando por ahí...


Título: Re: Ayuda
Publicado por: dijsktra en 13 Octubre 2019, 18:03 pm
Hola!

Basado en el trabajo de  YreX-DwX, ahora creo que si he dado con una respuesta a tu problema. Un interprete de expresiones en notación polaca inversa.

En algún lado de la Wikipedia he leído que una de sus propiedades es que el empleo de paréntesis se hace innecesario con independencia de la precedencia de los operadores. Cada secuencia (correcta) determina una y solo una expresión valida...

Para no repeterlo aquí, lo podéis ver en el correo modificado anterior:

https://foro.elhacker.net/programacion_cc/ayuda-t499837.0.html;msg2206082#msg2206082


Título: Re: Ayuda
Publicado por: Serapis en 13 Octubre 2019, 20:16 pm
En efecto, el cambio a la notación polaca inversa, es resolver la precedencia de la evaluación de la expresión debida por un lado a los paréntesis, y por otro al resto de  operadores que con distinta precedencia aparecen incluso sin paréntesis, y paliar por tanto el tiempo que se pierde en evaluar la precedencia de la expresión cada vez que deba ser ejecutada. Además simplifica el uso de la pila... pués tiene acciones muy sencillas para efectuar el cálculo.

Tampoco hay que olvidar que el código máquina/ensamblador suele ser notación prefija:
Mov  AX, 5
Add  BH, AH
Jmp  800
...
por lo que los compiladores, usan como código intermedio lo que se llama 'código de 3 direcciones', para finalmente compilar al sistema destino de una forma sencilla y rápida, precisamente teniendo en mente estas cuestiones.