sábado, 19 de diciembre de 2020

Lenguaje C: Calculadora didáctica

Uno de los proyectos que ha sobrevivido hasta ahora y que recoje lo que se puede hacer en lenguaje C es el llamado calculadora didáctica que consiste en tomar una expresión matematica en forma de texto, como por ejemplo:  34  * 2 / ( 4 - 2) y mostrar el resultado. 

Para ello se usa un analizador sintactico que funciona de esta manera: la expresión se pasa de infijo a postfijo y luego se evalúa usando una pila para dar un resultado. Pero para hacer todo esto se vale de listas enlazadas simples que se usan para almacenar la expresión. La pila se implementa bajo una lista enlazada simple. 

El programa pretende mostrar lo que ocurre cuando la calculadora está realizando el proceso. Así que muestra la expresión en infijo y en posfijo, tal como está implementada en la lista enlazada, tambien muestra el contenido de la pila mientras se evalua la expresión y, por supuesto, muestra el resultado.

Como se puede ver, es un programa en modo texto o consola, al estilo de los programas que se hacian para DOS. Inicialmente fue creado para ser compilado Borland C++. El siguiente código fue ajustado para ser compilado en Dev C++. 

Los ajustes tienen que ver con algunas funciones de las librerías conio.h y dos.h que por no ser librerías estandar no se incluyen en Dev C++. Hubo que prescindir del color y reemplazar unas funciones por otras:

  • La función iraxy() se declara e implementa en el código y reemplaza a la función gotoxy().  
  • La función Sleep() reemplaza a la función delay().
  • La función system("cls") reemplaza a clrscr();

El programa aún tiene los siguientes bugs:

  1. Cuando se evalúa una expresión los resultados parciales se van guardando en una variable tipo entera pero cuando se realizan divisiones se obtienen valores decimales o el valor es muy grande y se desborda y ocasionando error en el resultado. Es mejor usar variables de tipo float o double para este proceso.
  2. No se realiza una validación de lo que se ingresa. Si se ingresa un valor diferente a número o a los signos +, -, *, /, ( o) lo más probable es que el programa estalle.

El siguiente es el codigo fuente. Recordar que este código está escrito en C y que la extensión del archivo debe ser "c". 


calculadora.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <windows.h>

/* Estructuras de datos usadas para los nodos y  la  lista enlazada simple. */
typedef struct ListaNodo
  {
  char info[30];
  struct ListaNodo *sig;
  }TNodo,*pListaNodo;

typedef struct
  {
  pListaNodo primero, ventana;
  int longitud;
  } TLista, *Lista;

/* Declaración de las funciones para el manejo de la lista enlazada simple */
Lista inicLista (void);
void anxLista(Lista lst, char elem[]);
void elimLista(Lista lst);
void primLista(Lista lst);
void sigLista(Lista lst);
void ultLista(Lista lst);
void posLista(Lista lst,int pos);
void infoLista(Lista lst,char s[]);
int longLista(Lista lst);
int finLista(Lista lst);
void destruirLista(Lista lst);
void insLista(Lista lst, char elem[]);
void mostrarpos(Lista pos);

/* Declaración de la función que pasa la la expresion de la lista de infijo a postfijo */
Lista infijo_a_posfijo (Lista L);

/* Evalua la expresión */
int eval(Lista exp);

/* declaración de un apuntador global al comienzo de pila */
typedef Lista Pila;

/* Declaracion de las funciones de pilas */
Pila InicPila(void);
void AdicPila(Pila Pil, char elem[]);
void ElimPila(Pila Pil);
void InfoPila(Pila Pil, char s[]);
int VaciaPila(Pila Pil);
void DestruirPila(Pila Pil);

/* declaración de función que muestra el contenido de la pila */
void mostrarpila(Pila P);

/* Declaración de función que pasa de string a la lista */
Lista char2list( char s[]);

/* Declaración de la función que dibuja un cuadro en la pantalla de acuerdo aunas corrdenadas */
void CC(int C1,int F1, int C2, int F2);

/* Declaración de una función que dibuja un cuadro externo en la pantalla */
void incuadro ();

/* Declaración de la función que reemplaza a gotoxy() */
void iraxy(int x,int y);

/* SECCIÓN DE IMPLEMENTACIÓN DE LAS FUNCIONES ANTES DECLARADAS */

/* Funciones para el manejo de la lista enlazada simple */
Lista inicLista(void)
  {
  Lista nuevaLista;
  nuevaLista = malloc(sizeof(TLista));
  nuevaLista->primero=NULL;
  nuevaLista->ventana=NULL;
  nuevaLista->longitud=0;
  return (nuevaLista);
  }

void anxLista(Lista lst, char elem[])
  {
  pListaNodo nuevoNodo;
  nuevoNodo = malloc(sizeof(TNodo));
  strcpy(nuevoNodo->info,elem);
  nuevoNodo->sig = NULL;
  if (lst->primero==NULL)
    lst->primero = nuevoNodo;
  else
    {
    nuevoNodo->sig = lst->ventana->sig;
    lst->ventana->sig = nuevoNodo;
    }
  lst->longitud++;
  lst->ventana=nuevoNodo;
  }

void insLista(Lista lst, char elem[])
  {
  pListaNodo nuevoNodo, p;
  nuevoNodo = malloc(sizeof(TNodo));
  strcpy(nuevoNodo->info,elem);
  nuevoNodo->sig = NULL;
  if (lst->primero==NULL)
    lst->primero=nuevoNodo;
  else
    if (lst->primero==lst->ventana)
      {
      nuevoNodo->sig = lst->primero;
      lst->primero =nuevoNodo;
      }
    else
      {
      p=lst->primero;
      while(p->sig!=lst->ventana)
	p=p->sig;
      nuevoNodo->sig = lst->ventana;
      p->sig=nuevoNodo;
      }
  lst->longitud++;
  lst->ventana = nuevoNodo;
  }

void elimLista(Lista lst)
  {
  pListaNodo p;
  if (lst->ventana == lst->primero)
    {
    lst->primero = lst->primero->sig;
    free(lst->ventana);
    lst->ventana=lst->primero;
    }
  else
    {
    p=lst->primero;
    while(p->sig!=lst->ventana)
      p=p->sig;
    p->sig =lst->ventana->sig;
    free(lst->ventana);
    if(p->sig==NULL) /* ¿el ultimo nodo fue el eliminado?*/
      lst->ventana = p;
    else
      lst->ventana=p->sig;
    }
  lst->longitud--;
  }

void primLista(Lista lst)
  {
  lst->ventana = lst->primero;
  }

void sigLista(Lista lst)
  {
  if (lst->ventana!=NULL)
    lst->ventana = lst->ventana->sig;
  }

void ultLista(Lista lst)
  {
  int l;
  l= longLista(lst);
  posLista(lst,l);
  }

void posLista(Lista lst,int pos)
  {
  pListaNodo p;
  int i;
  if (lst->longitud >0)
    {
    i=1;
    p=lst->primero;
    while((i<pos)&&(p!=NULL))
      {
      p=p->sig;
      i++;
      }
    if((i==pos)&&(p!=NULL))
      lst->ventana =p;
    }
  }

void infoLista(Lista lst,char s[])
  {
  strcpy(s,lst->ventana->info);
  }

int longLista(Lista lst)
  {
  return(lst->longitud);
  }

int finLista(Lista lst)
  {
  if (lst->ventana==NULL)
    return 1;
  else
    return 0;
  }

void destruirLista(Lista lst)
  {
  primLista(lst);
  while(!finLista(lst))
    elimLista(lst);
  }

/* Funciones para el manejo de la Pila */

Pila InicPila(void)
	     {
	      return inicLista();
	     }

void AdicPila(Pila Pil, char elem[])
	     {
	      primLista(Pil);
	      insLista(Pil, elem);
	     }

void ElimPila(Pila Pil)
	     {
	      primLista(Pil);
	      elimLista(Pil);
	     }

void InfoPila(Pila Pil,char s[])
	      {
	       primLista(Pil);
	       infoLista(Pil,s);
	      }

int VaciaPila(Pila Pil)
	     {
	      return longLista(Pil)==0;
	     }

void DestruirPila(Pila Pil)
		 {
		  destruirLista(Pil);
		 }

/* Pasa de string a una lista enlazada simple */
Lista char2list( char s[])
{
Lista L;
int i=0,largo;
char dato[30],linea[20];
L = inicLista();
largo = strlen(s);
do  {
  if (s[i]!=32){
    if (strchr(")(",s[i]))
       {
       dato[0]=s[i];
       dato[1]='\0';
       anxLista(L,dato);
       i++;
       }
    else
       if (strchr("+-*/^",s[i]))
	  {
	  dato[0]=s[i];
	  dato[1]='\0';
	  anxLista(L,dato);
	  i++;
	  }
       else
	  {
	  strcpy(linea,"");
	  while(!(strchr("+-*/()",s[i]))&&(i<largo))
	    {
	     dato[0]=s[i];
	     dato[1]='\0';
	     strcat(linea,dato);
	     i++;
	     }
	     anxLista(L,linea);
	  }
   }
   else
     i++;
    }while(i<largo);
return (L);
}

/* Pasa de infijo a posfijo */
Lista infijo_a_posfijo (Lista L)
{
 char x[20],elem[20];
 int i;
 Pila Pil;
 Lista Pos;
 Pil = InicPila();
 Pos = inicLista();
 for(primLista(L);!finLista(L);sigLista(L))
    {
		mostrarpila(Pil);
		mostrarpos( Pos);
		Sleep(1000);
		infoLista(L,x);
		if (isdigit(x[0])) // Si es operando
		{
		ultLista(Pos);
		anxLista(Pos,x);
		infoLista(Pos,x);
		}
		else
		{
		    switch (x[0])
		    {
			// Los parentesis abiertos siempre se apilan
			case '(':
			    AdicPila(Pil,x);
			    break;
			// Los parentesis cerrados desapilan todos los elementos
			// hasta encontrar un parentesis abierto.
			case ')':
			    do {
				InfoPila(Pil,x);
				if (x[0]!= '(')
				    {
				    ultLista(Pos);
				    anxLista(Pos,x);
				     }
				ElimPila(Pil);
			    }while ((x[0] != '('));     //&& (!VaciaPila(Pil))) ;

			    break;
			case '+':
			case '-':
			case '*':
			case '/':
			    AdicPila(Pil,x);
			    break;
		    }
		}
    }
    while (!VaciaPila(Pil))
    {
	InfoPila(Pil,x);
	ElimPila(Pil);
	ultLista(Pos);
	anxLista(Pos,x);
	mostrarpila(Pil);
	mostrarpos( Pos);
	Sleep(1000);
    }
return Pos;
}

/* Evalúa la expresión */
int eval(Lista L)
{
char elem[20],res[20];
int op1,op2;
char cop1[20], cop2[20];
Pila Pos=InicPila();
for(primLista(L);!finLista(L);sigLista(L))
{
	infoLista(L,elem);
	if(isdigit(elem[0]))
		AdicPila(Pos, elem);
	else{
		InfoPila(Pos,cop1);
		ElimPila(Pos);
		InfoPila(Pos,cop2);
		ElimPila(Pos);
		op1=atoi(cop1);
		op2=atoi(cop2);
		switch (elem[0]){
		case '+': itoa( op1+op2,res,10);
			  AdicPila(Pos,res);
		break;
		case '-': itoa( op2-op1,res,10);
			  AdicPila(Pos,res);
		break;
		case '*': itoa( op1*op2,res,10);
			  AdicPila(Pos,res);
		break;
		case '/': itoa( op2/op1,res,10);
			  AdicPila(Pos,res);
		break;
		}
	}
	mostrarpila(Pos);
	Sleep(500);
}
InfoPila(Pos,res);
return atoi(res);
}

/* Función que muestra el contenido de la pila */
void mostrarpila(Pila P)
{
int i,col=40,fila =25;
char elem[20];
iraxy(col-2,14);
printf("Pila:");
iraxy(col-3,24);printf("ÀÄÄÄÄÄÙ");
for (i=15;i<=23;i++) {
  iraxy(col-3,i); printf("³     ³");}
for (primLista(P);!finLista(P);sigLista(P))
  {
  infoLista(P,elem);
  iraxy(col,fila-2);
  printf("%s",elem);
  fila--;
  }
}

/* Muestra en orden Postfijo */
void mostrarpos(Lista p)
{
char elem[20];
iraxy(2,8);printf("   ORDEN POSTFIJA");
iraxy(2,10);
for(primLista(p);!finLista(p);sigLista(p))
	{
	infoLista(p,elem);
	printf("[");
	printf("%s",elem);
	printf("]->");
	}
printf(" ² \n \n");
}

/* Dibuja un cuadro alrededor de la pantalla */
 void incuadro ()
 {
 system("cls");
 CC(1,1,75,25);
  }

/* Dibuja un cuadrado de acuerdo a unas coordenadas dadas
  Se llama CC porque antes dibujaba un Cuadro con Color */
 void CC( int C1,int F1, int C2, int F2)
 {
 int C1AUM,C2DIS,F1AUM,F2DIS,I;
 C1AUM=C1+1;
 C2DIS=C2-1;
 F1AUM=F1+1;
 F2DIS=F2-1;
 iraxy(C1,F1);printf("Ú");
 iraxy(C1,F2);printf("À");
 iraxy(C2,F1);printf("¿");
 iraxy(C2,F2);printf("Ù");
 for (I=C1AUM;I<=C2DIS;I++) { iraxy(I,F1);printf("Ä");
			     iraxy(I,F2);printf("Ä"); }
 for (I=F1AUM;I<=F2DIS;I++) { iraxy(C1,I);printf("³");
			     iraxy(C2,I);printf("³"); }
 }

/* Equivale a la función gotoxy() */
void iraxy(int x,int y){
      HANDLE hcon;
      hcon = GetStdHandle(STD_OUTPUT_HANDLE);
      COORD dwPos;
      dwPos.X = x;
      dwPos.Y= y;
      SetConsoleCursorPosition(hcon,dwPos);
 }

void main(void)
{
Lista l,p;
char exp[30],elem[20];
int resul,respuesta;
do{
	system("cls");
	incuadro ();
	iraxy(2,2);
	printf("    Escriba la Expresion Use +-*/^() y numeros :  ");
	gets(exp);
	l= inicLista();
	l = char2list(exp);
	iraxy(2,4); printf("    ORDEN INFIJA \n ");
	iraxy(2,6);
	for(primLista(l);!finLista(l);sigLista(l))
		{
		infoLista(l,elem);
		printf("[");
		printf("%s",elem);
		printf("]->");
		Sleep(500);
		}
	printf(" ²\n \n");
	p=infijo_a_posfijo (l);
	resul= eval(p);
	iraxy(8,16);
	printf("resultado: %d",resul);
	iraxy(4,22);
	printf(" desea continuar S/N?");
	respuesta=getch();
} while((respuesta =='s')||(respuesta =='S'));

}


No hay comentarios.:

Publicar un comentario