Calculadora de notación polaca inversa en C

La Notación Polaca Inversa (RPN en inglés, Reverse Polish Notation) es un método de introducción de datos alternativo al algebráico. Fue creada en 1920 por el matemático polaco Jan Lukasiewicz como una forma de escribir expresiones matemáticas sin tener que utilizar paréntesis y corchetes.

En la notación polaca inversa los operadores suceden a sus operandos. Por ejemplo, la expresión algebráica5+((1+2)*4)-3se traduce a la notación polaca inversa como5 1 2 + 4 * + 3 -.

Su principio es el de evaluar los datos directamente cuando se introducen y manejarlos dentro de una estructura LIFO (Last In First Out), lo que optimiza los procesos a la hora de programar.

El siguiente programa es una implementación en C de una calculadora que funciona con notación polaca inversa. El programa es una versión modificada del que figura en el libro “El lenguaje de programación C” de Kernighan y Ritchie.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
## ************
## @file calc.c
## ************

#include <stdio .h>
#include <stdlib .h>

#define MAXVAL 100
#define NUMBER '0'

/**
 *  Variables
 */

int verbose;
int sp = 0;
double val[MAXVAL];

/**
 *  Prototypes
 */

int toi (char const *s);
void push (double);
double pop (void);

/**
 *  Main
 */

int main (int argc, char const *argv[])
{
  int offset, i;
  double op2;

  if (argc < 2)
  {
    fprintf (stderr,
      "Usage: %s [-v] EXP\n", argv[0]);
    return 1;
  }

  verbose = offset =
    (strcmp (argv[1], "-v") == 0) ? 1 : 0;
 
  for (i = 1 + offset; i < argc; i++)
  {
    switch (toi (argv[i]))
    {
      case '+' :
        if (verbose)
          printf ("operator +\n");
        push (pop() + pop());
        break;
      case '-' :
        if (verbose)
          printf ("operator -\n");
        op2 = pop();
        push (pop() - op2);
        break;
      case '*' :
        if (verbose)
          printf ("operator *\n");
        push (pop() * pop());
        break;
      case '/' :
        if (verbose)
          printf ("operator /\n");
        op2 = pop();
        if (op2 != 0.0)
          push (pop() / op2);
        else
          fprintf (stderr,
            "Error: division by zero\n");
        break;
      case NUMBER :
        push (atof (argv[i]));
        break;
      default :
        fprintf (stderr,
          "Error: unknown operator %s\n",
          argv[i]);
        break;
       
    }
  }

  // Result
  printf ("%f\n", val[0]);

  return 0;

}

/**
 *  Toi
 */

int toi (char const *s)
{
  return (isdigit(s[0])) ?
    NUMBER : (int) s[0];
}

/**
 *  Push
 */

void push (double e)
{
  if (sp < MAXVAL)
  {
    if (verbose)
      printf ("push %f in %d\n", e, sp);
    val[sp++] = e;
  }
  else
    fprintf (stderr, "Error: full stack\n");
}

/**
 *  Pop
 */

double pop (void)
{
  if (sp > 0)
  {
    --sp;
    if (verbose)
      printf ("pop %f from %d\n", val[sp], sp);
    return val[sp];
  }
  else
  {
    fprintf (stderr, "Error: empty stack\n");
    return 0.0;
  }
}
</stdlib></stdio>

Para compilarlo ejecutamos

$ gcc -o calc calc.c

A diferencia del programa original publicado en el libro de C, aquí se utiliza el arrayargv[]como fuente de operandos y operadores. Esto permite que podamos llamar al programa desde línea de comandos de la siguiente manera:

$ ./calc 5 1 2 + 4 \* + 3 -

Notar que para el caso especial del operador multiplicación, debemos escaparlo para evitar la expansión propia de bash antes de la llamada efectiva al programa.

El programa admite la opción-v(verbose), que muestra información acerca de uno de los pasos realizados.

Ejemplo I

$ ./calc -v 5 1 2 + 4 \* + 3 -
push 5.000000 in 0
push 1.000000 in 1
push 2.000000 in 2
operator +
pop 2.000000 from 2
pop 1.000000 from 1
push 3.000000 in 1
push 4.000000 in 2
operator *
pop 4.000000 from 2
pop 3.000000 from 1
push 12.000000 in 1
operator +
pop 12.000000 from 1
pop 5.000000 from 0
push 17.000000 in 0
push 3.000000 in 1
operator -
pop 3.000000 from 1
pop 17.000000 from 0
push 14.000000 in 0
14.000000


Read more »

Tags: ,