Una pila o stack es un contenedor LIFO (last in first out), es decir, que el último que entra es el primero que sale. Imagina para esto una "pila" de platos, tú los vas amontonando uno encima de otro y cuando vas a coger uno, coges el de arriba del todo. Puedes fijarte en el contenedor <stack> de la STL para C++ para crear una pila en C lo más parecida posible a lo que existe. Entonces necesitas un contenedor de datos ordenado (en tu caso un array) y las siguiente funciones por lo menos (más o menos ya las tienes):
T top(); // devuelve el ultimo elemento de la pila del tipo que sea (int/float/char/...)
void push(T elemento); // introduce un nuevo elemento
void pop(); // saca el ultimo elemento de la pila
bool empty(); // devuelve true/false dependiendo de si la pila esta vacia o no
int size(); // devuelve el numero de elementos que contiene la pila
Una cola o queue es lo contrario a la pila, un contenedor FIFO (first in first out) donde el primero que entra es el primero que sale. Imagina una cola de personas que se ponen en un cajero, el primero que llega es el primero que usa el cajero y se va. En C++ también existe una cola de la STL <queue> y tiene los siguiente elementos además de un contenedor ordenado (que en tu caso usarías un array):
T front(); // devuelve el elemento que antes se inserto en la cola
void pop(); // elimina el elemento que antes se inserto
T back(); // devuelve el elemento que se inserto el ultimo
// No existe un pop() para eliminar el ultimo ya que entonces no seria una cola ya que dejaria de ser FIFO
void push(T elemento); // introduce un nuevo elemento a la cola
bool empty(); // devuelve true/false dependiendo de si la cola esta vacia o no
int size(); // devuelve el numero de elementos que contiene la cola
Una lista o list es como una mezcla, es un contenedor que permite insertar y eliminar elementos tanto por delante como por detrás. Al igual que para las otras, en C++ tenemos el contenedor <list> de la STL que tiene además del propio contenedor de valores las siguientes funciones:
void push_back(T elemento); // inserta un elemento al final de la lista
void push_front(T elemento); // inserta un elemento al comienzo de la lista
void pop_back(); // elimina el ultimo elemento de la lista
void pop_front(); // elimina el primer elemento de la lista
T front(); // devuelve el primer elemento de la lista
T back(); // devuelve el ultimo elemento de la lista
bool empty(); // devuelve true/false dependiendo de si la lista esta vacia o no
int size(); // devuelve el numero de elementos de la lista
Ahora respecto a las mejoras de tu programa. Algunas buenas prácticas son:
- Evitar el uso de variables globales siempre que se pueda
- Evitar mostrar mensajes dentro de las funciones a no ser que la función se encargue justo de eso, de mostrar un mensaje (ej; un menú).
- Las constantes en mayúsculas. Por ejemplo <max> -> <MAX>
Supongo que si estás aprendiendo ahora, todavía no te habrán enseñado el uso de <struct> para agrupar variables o el paso de arrays como parámetros por eso lo usas de forma global y por separado <pila> y <tope>. Para el caso de esas dos variables lo dejaré pasar por lo dicho, aún no te habrán dicho cómo se hace de otra forma.
Sin embargo, la variable <i> de la línea 10 es global y luego en las líneas 140 y 150 vuelves a declarar una variable <i> para cada función de forma local. En este caso te recomiendo borrar la de la línea 10 para evitar una variable global.
También tienes en la línea 10 una variable <acum> que sólo usas dentro de la función <sumaPila()> y que contiene lo mismo que la variable <suma> que declaras dentro de la función. Entonces la variable <acum> sobra también.
Otra cosa importante es el uso de funciones. Una función sirve para agrupar un grupo de código y poder reutilizarlo por lo que hay que crear funciones fáciles de reutilizar. Tú pregúntate que programa se entiende mejor, el primero o el segundo:
int main(){
int eleccion;
menu(eleccion);
}
int main(){
int eleccion = mostrarMenu();
while(eleccion != -1){
switch(eleccion){
case 1:
pilaNueva();
break;
case 2:
if(pilaVacia
()) printf("La pila esta vacia"); else printf("La pila no esta vacia"); break;
// y asi con todos que me canso de seguir...
}
eleccion = mostrarMenu(); // al acabar lo que sea volvemos a dar a elegir
}
}
Como ves en el primer caso delegas TODO el trabajo a otra función <menu()> por lo que te ves en las mismas. El <main> no te dice nada y todo el código está en <menu()>. La cosa está en que leyendo el <main> entiendas lo que el programa hace pero no hace falta que entiendas cómo lo hace. En el segundo ejemplo, se ve que muestras un menú y ese menú devuelve una opción, luego según la opción ves lo que el programa hace (por ejemplo, crear una pila nueva) pero si quieres saber CÓMO es cuando tienes que ir a la función <pilaNueva()>. Así el código queda limpio pero a la vez comprensible.
Paso de parámetros:Tú tienes que pasar una variable como parámetro a una función cuando necesites esa variable fuera de la función y el valor de esa variable condicione algo de la función.
En tu función <menu()> recibes un parámetro <eleccion> que:
- 1. No tiene ningún valor definido,
- 2. No sirve para nada fuera de la función.
Necesitas ese parámetro? NO.
En la función <apilar()> sin embargo es al revés.
- Objetivo de la función: insertar un elemento en la pila.
- Parámetro: el elemento a insertar.
Hasta aquí todo perfecto pero resulta que lo que luego haces en la función es insertar el elemento, pedir uno nuevo y sobreescribir el primero por el nuevo... Para qué mandas un elemento como parámetro si no lo usas para nada "productivo"??
Respecto al resto de funciones:
La función <menu()> hace demasiadas cosas y recibe parámetros innecesarios.
Recomendación: hacer una función <int mostrarMenu()> que sólo muestre las opciones, de al usuario una a elegir y devuelva ese valor. La función <crearPilaVacia()> tiene que retornar un <bool> y no retorna nada. Por tanto lo que retorna es -1 que es el valor de la asignación de la función. Y -1 traducido a <bool> es siempre <true>. No dejes la tarea de adivinar el valor de retorno. Deja bien claro qué devuelve una función (si tiene que devolver algo, claro, yo en este caso lo veo innecesario...).
Recomendacion: una función <void pilaNueva()> que lo único que haga sea poner el <tope> a -1.Las funciones <pilaVacia()> y <pilaLlena()> podrías ahorrártelas ya que conoces el tamaño de la pila <MAX> y el número de elementos <tope>. Pero tampoco es un problema grave, si prefieres usar esas funciones eres libre de hacerlo pero se pueden acortar:
bool pilaVacia(){
if(tope == -1){ // comprueba si lo de dentro del parentesis es true o false...
return true; // ... si el parentesis vale true, devuelve true
}
else{ //... si el parentesis vale false...
return false; // ... devuelve false
}
}
// Al final lo que haces es devolver lo mismo que el resultado del parentesis
// No sera mejor entonces devolver directamente el resultado del parentesis?
bool pilaVacia(){
return (tope == -1);
}
Recomendación: acortar ambas funciones como se ve en el ejemplo. La función <apilar()>, como antes te he explicado, no tiene fundamento lo que haces. Además como antes decía es mejor evitar mostrar mensajes dentro de las funciones.
Recomendación: una función <bool apilar(int elemento)> que compruebe si se puede insertar el elemento. Si es posible, lo inserta y devuelve true y si no es posible no lo inserta y devuelve false. Así sin mostrar mensajes tú puedes saber si el elemento se insertó o no.Para la función <desapilar()> más de lo mismo. Se pueden evitar los mensajes haciendo lo mismo que en <apilar()>
Recomendación: una función <bool desapilar()> que compruebe si la pila está vacía. Si está vacía devuelve false y si no está vacía, elimina el elemento (restar 1 a <tope>) y devuelve true. (No es necesario poner el elemento a 0 antes de eliminarlo). En la función <visualizarElementos()> se podría eliminar el mensaje que sirve de "titulillo" y dejar únicamente los elementos. En caso de querer que se vea el titulillo ese siempre puedes ponerlo antes de llamar a la función.
printf("Los elementos de la pila son: "); visualizarElementos();
Recomendación: dejar la función igual <void visualizarElementos()> pero quitando el primer mensaje. Y para la última función, <sumaPila()>, que también muestra los mensajes en pantalla tenemos que evitar esto.
Recomendación: una función <int sumaPila()> que calcule la suma y en vez de mostrarla por pantalla, la devuelva. Entonces para mostrar la suma haríamos:printf("La suma de los elementos de la pila es: %d", sumaPila
());
Unos consejos extra:
// Cuando se repite la misma variable justo antes y justo despues del = se puede acortar:
suma += otro; // equivale a: suma = suma + otro
resta -= otro; // equivale a: resta = resta - otro
producto *= 2; // equivale a: producto = producto * 2
// etc...
// Para incrementar variables en una unidad se puede hacer:
++numero; // equivale a: numero = numero + 1
numero++; // equivale a: numero = numero + 1
// Para decrementar variables en una unidad se puede hacer:
--numero; // equivale a: numero = numero - 1
numero--; // equivale a: numero = numero - 1
Y para los condicionales:
// Poner:
if(condicion == true){...}
// ...es lo mismo que poner:
if(condicion){...}
// Al igual que para:
if(condicion == false){...}
// ...es lo mismo que poner:
if(!condicion){...}