• Domingo 19 de Mayo de 2024, 08:41

Autor Tema:  Implementacion De Una Pila  (Leído 5724 veces)

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Implementacion De Una Pila
« en: Domingo 19 de Diciembre de 2004, 23:14 »
0
Hola, dado que la mayoria de los programadores trabajamos mucho con estructuras de datos dinámicas (sobre todo para las practicas de la universidad), me he propuesto crear una serie de implementaciones "genericas" para las estructura de datos dinamicas mas comunes.

He empezado por la implementación de una pila, pues es la más facil en cuanto a funciones a utilizar. El siguiente código administra el uso de una serie de pilas que el usuario puede crear.

El codigo funciona, al menos en las pruebas que he realizado, os invito a probarlo si quereis.

Pero sobre todo, me gustaria que le echarais un vistazo, no solo a la implementación, sino tambien a las funciones principales que facilita esta "mini-libreria". ¿Os resultan suficientes? Si fuerais vosotros los que usarais esta implementación, ¿añadiriais alguna función mas?.

Aqui os lo pongo:

Archivo pila.h
<!--xc1--></div><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td>XCODE </td></tr><tr><td id='XCODE'><!--exc1-->
/* IMPLEMENTACION DE UNA PILA "GENERICA"
 * --------------------------------------------------------
 *
 *   La siguiente implementacion permite el uso de una pila lo mas generica
 * posible. Existe una estructra nodo que almacena un puntero a la
 * informacion que desea introducirse a la pila. La varaible **pilas almacena
 * las pilas que estan siendo usadas.
 *   La funcion creaPila() devuelve un entero para el acceso a la pila creada
 * que sera usado en las demas funciones. El uso del entero puede asemejarse a
 * un descriptor de archivos.
 */

 
#include <stdio.h>

/* Estructura que actuara como NODO */
struct nodo {
    /* Elemento a almacenar */
    void *elem;
    /* Puntero al elemento siguiente */
    struct nodo *sig;
};

/* Puntero a las pilas que estan siendo utilizadas */
struct nodo **pilas;

/* Crea una pila. Devuelve el entero para controlarla */
int creaPila();
/* Introduce un elemento en la pila especificada */
void apila(int pila, void *elemento);
/* Desapila un elemento de la pila especificada */
void *desapila(int pila);
/* Recupera el primer elemento de una pila, sin desapilarlo */
void *recupera(int pila);
<!--xc2--></td></tr></table><div class='postcolor'><!--exc2-->

Archivo pila.c
<!--xc1--></div><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td>XCODE </td></tr><tr><td id='XCODE'><!--exc1-->
/* Implementacion de una pila generica
 */

#include "pila.h"

/* Crea una pila. Devuelve el entero para controlarla */
int creaPila(){
    static int num_pilas = 0;
    struct nodo **aux;
    int i;

    aux = (struct nodo **) malloc(sizeof(struct nodo *)*num_pilas);
    for(i = 0; i < num_pilas; i++)
        aux[i] = pilas[i];
    
    num_pilas++;
    pilas = (struct nodo **) malloc(sizeof(struct nodo *)*num_pilas);

    for(i = 0; i < num_pilas - 1; i++)
        pilas[i] = aux[i];
    
    pilas[num_pilas-1] = NULL;
    return num_pilas-1;
}

/* Introduce un elemento en la pila especificada */
void apila(int pila, void *elemento) {
    struct nodo *aux;

    aux = (struct nodo *) malloc(sizeof(struct nodo));
    aux->elem = elemento;
    aux->sig = pilas[pila];
    pilas[pila] = aux;
}

/* Desapila un elemento de la pila especificada */
void *desapila(int pila) {
    void *aux;
    struct nodo *act;
    
    if(pilas[pila] == NULL) return NULL;
    
    aux = pilas[pila]->elem;
    act = pilas[pila];
    pilas[pila] = pilas[pila]->sig;
    free(act);

    return aux;
}

/* Recupera el primer elemento de una pila, sin desapilarlo */
void *recupera(int pila) {
    return pilas[pila]->elem;
}
<!--xc2--></td></tr></table><div class='postcolor'><!--exc2-->

Como veis, la implementación intento que sea genérica utilizando punteros a void. Pero usando C, esa "genericidad" es un poco superficial, pues además acarrea otros problemas como es la uniformidad de los datos que contiene la pila. Esto podría llevarnos a largas disertaciones acerca de la reutilización, modularidad y demás del lenguaje C y de mi implementación, pero no pretendo ir por esos derroteros (aunque si alguno se presta, que no dude en comunicarmelo ;)).

La implementacion es sencilla y corta, y solo quiero saber vuestra opinion acerca de su utilidad y cosas que podrían faltar y demás. Por supuesto, si detectais algun error, no dudeis en comunicarmelo.

Muchas gracias!

Nos vemos :hola:
Core Dumped
zirrus.es

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #1 en: Lunes 20 de Diciembre de 2004, 02:09 »
0
Bueno asi por encimita...
no debiera haber una función

destruye_pila(); :question:

Y por otro lado operaciones como

-copiar pila
-crear copia invertida de la pila

podrian ser de gran utilidad.

Normalmente las librerias de manejo de pilas incluyen otras cosas que no se si esten contempladas dentro de tu alcance:

- Ordenar alfabeticamente
- Ordenar numericamente
- Ordenar alfabeticamente desc
- Ordenar numericamente desc
- 'Concatenar' una pila con otra
- Un metodo que devuelva un string con el contenido de la cabeza de la pila

Otra funcionalidad podria ser por ejemplo que en cada nodo de la pila exista realmente una estructura con un dato tipo void (como ya lo tienes) y un campo que indique que tipo de dato esta almacenado en el nodo, ejemplo

Código: Text
  1.  
  2. #define 0 TIPO_INDEFINIDO
  3. #define 1 TIPO_CHAR
  4. #define 2 TIPO_INT
  5. #define 3 TIPO_LONG
  6. #define 4 TIPO_CHAR_POINTER
  7. #define 5 TIPO_INT_POINTER
  8. #define 6 TIPO_LONG_POINTER
  9. ...
  10. ...
  11. ...
  12.  
  13. /* Estructura que actuara como NODO */
  14. struct nodo {
  15.    /* Elemento a almacenar */
  16.    void *elem;
  17.     /* Tipo del elemento a almacenar*/
  18.     char tipoDato;
  19.    /* Puntero al elemento siguiente */
  20.    struct nodo *sig;
  21. }
  22.  
  23.  

Una recomendacion para tu programacion es que utilices la notacion al 'estilo pascal' creo que se llama...
es decir para que tu codigo sea un poco mas claro los nombres de las funciones deberian comenzar siempre en mayusculas y si son de nombres compuestos deberia ser mayuscula en la pimera letra de un nuevo nombre.

Y la forma que utilizas actualmente para los nombres de las funciones utilizarla para los nombres de las variables.

Mas tarde hare otros comentarios sobre tu codigo.
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #2 en: Lunes 20 de Diciembre de 2004, 03:08 »
0
Hola de nuevo

1- static int num_pilas = 0;
no lo pueedes hacer porque la palabra reservada estatic es de C++ no de lenguaje C, asiq ue deberias declarar una variable global :
int num_pilas = 0;
2- mas abajo haces
Código: Text
  1.  
  2. num_pilas++;
  3.    pilas = (struct nodo **) malloc(sizeof(struct nodo *)*num_pilas);
  4.  
  5.  
Lo cual esta mal, solo deberias incrementar numpilas  una vez ya hayas asiganno memoria.
3-En toda la libreria estas fallando porque no estas controlando los errores que se pueden  derivar del malloc como falta de memoria etc, asi que tendras multim¡ples errores como por ejemlo que sucede si hacer free o asignas a una porcion de memoria que nunca ha sido reservada?
4- esta funcion es mas estructurada de esta manera:
Código: Text
  1.  
  2. /* Desapila un elemento de la pila especificada */
  3. void *desapila(int pila) {
  4.    void *aux;
  5.    struct nodo *act;
  6.    
  7.    if(pilas[pila] != NULL)
  8.    {
  9.       aux = pilas[pila]-&#62;elem;
  10.       act = pilas[pila];
  11.       pilas[pila] = pilas[pila]-&#62;sig;
  12.       free(act);
  13.    }
  14.     else
  15.     {
  16.          aux = NULL;
  17.     }
  18.  
  19.    return aux;
  20. }
  21.  
  22.  

Eso es todo por el momento.
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Re: Implementacion De Una Pila
« Respuesta #3 en: Lunes 20 de Diciembre de 2004, 11:32 »
0
Uff uff, vayamos por partes ;). Antes que nada gracias por centrarte y haber analizado mi codigo. Vayamos al lio.

Como ves, la implementacion necesitaba de opiniones acerca de posibles mejoras y correcciones, y agradezco mucho tus opiniones. Las funciones destruyePila, copiaPila, concatenaPila e inviertePila las veo realmente útiles para una libreria de pilas, asi que me propongo implementarlas.

Las funciones que comentas para ordenar numericamente/alfabeticamente solo serían posibles si se conociera el tipo del contenido de los nodos de la pila. Cosa que se puede subsanar con la idea que has propuesto de incluir un campo en los Nodos para indicar el tipo del dato que se incluye. Esto dara mucho juego porque así también se podrán asegurar que los elementos de una pila son del mismo tipo, por ejemplo.Tambien lo veo muy útil, lo implementaré.

En cuanto a tu segundo post y las anotaciones acerca del codigo:
- Sobre el punto 1: He estado revisando algunas fuentes y creo que la palabra reservada static si que esta contemplada en el lenguaje C. Hice ese "trapicheo" para evitar introducir variables globales, con una (que apunta a la estructura de listas) creo que es suficiente.

- Sobre el punto 2: Ves mas correcto hacer:
Código: Text
  1.  
  2. pilas = (struct nodo **) malloc(sizeof(struct nodo *)*(num_pilas+1));
  3. num_pilas++;
  4.  
  5.  
El resultado es el mismo, quizas es mas elegante? ¿Te referias a eso?. Ok, comprendo la idea, la variable representativa del numero de pilas debe estar en consonancia con el numero de pilas reservadas en memoria, en mi codigo, el programa podría fallar el malloc y no tener la memoria reservada, entonces el valor de num_pilas sería incorrecto. ¿Van por ahi los tiros?

- Sobre el punto 3: Es verdad, debo controlar en todo momento si los malloc y free estan actuando convenientemente. Quizas esto sea un vicio de hacer practicas en la universidad con prisa. Lo tendre que revisar y corregir.

- Sobre el punto 4: Jejeje, tienes razon, si estamos en programacion estructurada, que nuestros procedimientos sigan esa filosofia. Ok, de acuerdo. (Esto me ha recordado a uno de mis profesores ;)).

Bueno, me pongo al lio en cuanto pueda y comento mis evoluciones.

Muchas gracias JuanK!!!! Eres un genio!

Nos vemos :hola:
Core Dumped
zirrus.es

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #4 en: Lunes 20 de Diciembre de 2004, 15:57 »
0
Volviendo a lo del static , realmente dudo que sea de lenguaje c, pues esta palabra reservada se utiliza para  para mantener un  miembro comun en cada una de las instancias de una clase, y dado que en el lenguaje  C no existe el concepto de clases, es muy seguro que sea de C++ y no de C.

Imaginate que algun usuario de C++ usara tu libreria...
la utilizaria indistintamente  en cada una  de las clases que ha creado y reamente esperaria interdependencia entre los componente  de una clase y de otra, pero como existe una variable static dentro de uno de los procedimeintos que has definido entonces sucederen cosas inesperadas,

de hecho se supone (en teoria) que la variable sea o no static se destruye al terminar la ejecucion de su scope (en tu caso el procedimiento) asi que no serviria de variable global..
por el contrario si volvemos al asunto de las clases imagina que a traves de hilos se estan ejecutando  las clases que ha creado el programador, y en un instante determiando ambas c lases accedieron a tu procedimiento, se supone que cada una tiene su propio contador de pilas pero al ingresar ambas al tiempo y al ser una variable static ambas veran el valor de la variable segun la ultima asignacion y ...
imaginate lo que puede suceder mientras ambas estan en el for por ejemplo!!! la una cambiando el valor de la otra... tenaz!!!

Mi recomendacion es que uses una variable global...
y me imagino que no lo hiciste por alguna cosa que le escuchaste a algun profesor...

Luego veremos el tema del redimiento.  :comp:
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

rir3760

  • Miembro activo
  • **
  • Mensajes: 37
    • Ver Perfil
Re: Implementacion De Una Pila
« Respuesta #5 en: Lunes 20 de Diciembre de 2004, 17:00 »
0
Cita de: "JuanK"
Volviendo a lo del static , realmente dudo que sea de lenguaje c, pues esta palabra reservada se utiliza para  para mantener un  miembro comun en cada una de las instancias de una clase, y dado que en el lenguaje  C no existe el concepto de clases, es muy seguro que sea de C++ y no de C.
La palabra reservada 'static' forma parte del C estandard, uno de los libros mas conocidos (y antiguos) que describen su uso es 'The C Programming Language 2nd Ed' de Brian W. Kernighan y Dennis M. Ritchie.

Cuando el calificador 'static' se aplica a variables automaticas (locales) cambia el tipo de almacenamiento a estatico por lo que la variable se inicializa a cero (si no se le da un valor inicial) cuando se carga el programa y su duracion continua hasta la terminacion del mismo.

En el caso de variables externas (globales) la palabra reservada 'static' cambia la vinculacion (o 'linkage') a interna por lo cual la variable solo puede ser accesada desde su punto de declaracion hasta el final del archivo de codigo fuente.

Un saludo
The capacity to learn is a gift; The ability to learn is a skill; The willingness to learn is a choice. -- Rebec of Ginaz

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #6 en: Lunes 20 de Diciembre de 2004, 17:15 »
0
Ya he realizado algo de consulta y en efecto si es del ANSI C.

pero no estoy seguro de que consecuencias podria tener usarla asi:

static int num_pilas = 0;

ya que podria implicar que se reasiganara 0 cada vez  no?

Adicionalmente cuando implementes DestruyePila ya no te va a servir igual,
pues
Citar
la variable solo puede ser accesada desde su punto de declaracion hasta el final del archivo de codigo fuente.

Realmente entiendo que solo puede ser accesada desde su punto de declaracion hasta donde termina su scope, no hasta el final  del archivo de codigo fuente.

Corrijanme si me equivoco.
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

rir3760

  • Miembro activo
  • **
  • Mensajes: 37
    • Ver Perfil
Re: Implementacion De Una Pila
« Respuesta #7 en: Lunes 20 de Diciembre de 2004, 18:21 »
0
Cita de: "JuanK"
pero no estoy seguro de que consecuencias podria tener usarla asi:
static int num_pilas = 0;
ya que podria implicar que se reasiganara 0 cada vez  no?

No, ya que la asignacion solo ocurre una vez al inicar el programa y las llamadas a la funcion creaPila() van a ignorar la asignacion.

Cita de: "JuanK"
Adicionalmente cuando implementes DestruyePila ya no te va a servir igual, pues
Citar
la variable solo puede ser accesada desde su punto de declaracion hasta el final del archivo de codigo fuente.

Realmente entiendo que solo puede ser accesada desde su punto de declaracion hasta donde termina su scope, no hasta el final  del archivo de codigo fuente.
Cierto, pero lo que mencione arriba solo se aplica a variables globales.

En lo que se refiere a la variable num_pilas me parece que lo mas recomendable es lo que sugiere JuanK de definirla como global, si ademas se añade el calificador static tendriamos que las variables globales definidas en el modulo de implementacion (pila.c) no podrian accederse directamente desde el modulo cliente, solo mediante las funciones definidas en la interfaz (pila.h), algo similar a lo que sucede con la encapsulacion y los metodos get/set de la POO.

Un saludo
The capacity to learn is a gift; The ability to learn is a skill; The willingness to learn is a choice. -- Rebec of Ginaz

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #8 en: Lunes 20 de Diciembre de 2004, 18:29 »
0
Citar
si ademas se añade el calificador static tendriamos que las variables globales definidas en el modulo de implementacion (pila.c) no podrian accederse directamente desde el modulo cliente, solo mediante las funciones definidas en la interfaz (pila.h), algo similar a lo que sucede con la encapsulacion y los metodos get/set de la POO.

Muy buena info, no lo sabia.   :smartass:   :think:
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Re: Implementacion De Una Pila
« Respuesta #9 en: Martes 21 de Diciembre de 2004, 00:06 »
0
Bueno, ya estamos de nuevo por aqui. Bienvenido rir3760! :D gracias por tu ayuda.

He implementado las funciones destruyePila e inviertePila. La variable num_pilas la he convertido en global segun vuestras recomendaciones, además es static pq conviene que solo tenga alcance dentro del codigo de pila.c. Toda llamada a malloc esta comprobada para impedir posibles fallos.

Veamos algunas puntualizaciones que estoy encontrando y creo que puede provocar reestructurar y alterar el codigo:
- Dado que los nodos de las pilas almacenan un puntero a los datos (y dicho puntero es void para intentar un poco de genericidad) el hecho de duplicar una pila me resulta un poco confuso :blink:. ¿Como duplico los nodos de una pila si no se cuanto espacio debo reservar para los nuevos nodos? ¿Puedo usar un malloc con el tipo de datos void?.
Por ejemplo, tengo una pila con dos enteros, dichos enteros en la pila realmente son apuntadores a void hacia la zona de memoria donde estan estos valores. Si ahora duplico la pila, duplico los nodos y con ello los apuntadores, pero sigo apuntando a la misma zona de memoria!!! los datos son los mismos!. ¿Como puedo saber cuanta memoria ocupa ese puntero a void (que realmente apunta a int) para poder reservar memoria nueva y duplicar su contenido?
Creo que una buena solución sería saber que tipo de datos se almacena en los nodos, como anteriormente comentaste JuanK, eso ayudaría a reservar memoria y a duplicar su información. ¿Que opinais?

- El problema anterior tambien me ha surgido al intentar implementar la funcion de concatenar pilas. Dado que aunque concatene pilas, la pila resultante tendra nuevos nodos, vale, pero sin embargo dichos nodos tendran apuntadores a los datos que tambien estan siendo apuntados por las pilas que han sido concatenadas.

- Me parece que voy a tener que cambiar el concepto de la variable global pilas. Actualmente es un array a las pilas que estan siendo usadas, y para acceder a una determinada pila el usuario hace uso de su indice (que recibe al llamar a crearPila) y trabajar con la pila. Sin embargo, si por ejemplo tengo tres pilas (la 0, la 1 y la 2) y ahora borro la 1. El numero de pilas usandose es 2, y tendria un hueco en el array en la posicion 1 que podria reutilizarse. Sin embargo, tal y como esta orientado el programa, la variable num_pilas siempre se incrementa para dar nuevas pilas (peligro de desbordamiento en un futuro!!).
Estoy pensando en mantener una lista enlazada que crezca por cada pila que se solicite y que contenga un identificador entero para identificar la pila dentro de la lista. Esto permitiría hacer más llevadero el control de las pilas y su borrado. ¿Se os ocurre algo mejor? ¿Creeis que debería refinar el control de los huecos en el array?

El codigo por ahora queda así:

Archivo pila.h
<!--xc1--></div><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td>XCODE </td></tr><tr><td id='XCODE'><!--exc1-->

/* IMPLEMENTACION DE UNA PILA "GENERICA"
 * --------------------------------------------------------
 *   La siguiente implementacion permite el uso de una pila lo mas generica
 * posible. Existe una estructra nodo que almacena un puntero a la
 * informacion que desea introducirse a la pila. La varaible **pilas almacena
 * las pilas que estan siendo usadas.
 *   La funcion creaPila() devuelve un entero para el acceso a la pila creada
 * que sera usado en las demas funciones. El uso del entero puede asemejarse a
 * un descriptor de archivos.
 */

 
#include <stdio.h>

/* Estructura que actuara como NODO */
struct nodo {
    /* Elemento a almacenar */
    void *elem;
    /* Puntero al elemento siguiente */
    struct nodo *sig;
};

/* Puntero a las pilas que estan siendo utilizadas */
struct nodo **pilas;
/* Variable que mantiene el numero de pilas usadas */
static int num_pilas = 0;

/* Crea una pila. Devuelve el entero para controlarla */
int creaPila();
/* Introduce un elemento en la pila especificada */
void apila(int pila, void *elemento);
/* Desapila un elemento de la pila especificada */
void *desapila(int pila);
/* Recupera el primer elemento de una pila, sin desapilarlo */
void *recupera(int pila);
/* Destruye la pila que se la pasa como parametro */
void destruyePila(int pila);
/* Invierte la pila que se le pasa como parametro */
void inviertePila(int pila);

<!--xc2--></td></tr></table><div class='postcolor'><!--exc2-->

Archivo pila.c
<!--xc1--></div><table border='0' align='center' width='95%' cellpadding='3' cellspacing='1'><tr><td>XCODE </td></tr><tr><td id='XCODE'><!--exc1-->

/* Implementacion de una pila generica
 */

#include "pila.h"

/* Crea una pila. Devuelve el entero para controlarla */
int creaPila(){
    struct nodo **aux;
    int i;

    if((aux = (struct nodo **) malloc(sizeof(struct nodo *)*num_pilas)) == NULL) {
        perror("Error al crear la pila");
        exit(1);
    }
    for(i = 0; i < num_pilas; i++)
        aux[i] = pilas[i];
    
    if((pilas = (struct nodo **) malloc(sizeof(struct nodo *)*(num_pilas + 1))) == NULL) {
        perror("Error al crear la pila");
        exit(1);
    }

    num_pilas++;
    for(i = 0; i < num_pilas - 1; i++)
        pilas[i] = aux[i];
    
    pilas[num_pilas-1] = NULL;
    return num_pilas-1;
}

/* Introduce un elemento en la pila especificada */
void apila(int pila, void *elemento) {
    struct nodo *aux;

    if((aux = (struct nodo *) malloc(sizeof(struct nodo))) == NULL) {
        perror("Error al apilar");
        exit(1);
    }
    aux->elem = elemento;
    aux->sig = pilas[pila];
    pilas[pila] = aux;
}

/* Desapila un elemento de la pila especificada */
void *desapila(int pila) {
    void *aux;
    struct nodo *act;
    
    if(pilas[pila] != NULL) {    
        aux = pilas[pila]->elem;
        act = pilas[pila];
        pilas[pila] = pilas[pila]->sig;
        free(act);
        
        return aux;
    }
    else return NULL;
}

/* Recupera el primer elemento de una pila, sin desapilarlo */
void *recupera(int pila) {
    return pilas[pila]->elem;
}

/* Destruye la pila que se la pasa como parametro */
void destruyePila(int pila) {
    struct nodo *aux, *aux1;

    aux = pilas[pila];
    while(aux != NULL) {
        aux1 = aux->sig;
        free(aux);
        aux = aux1;
    }
    pilas[pila] = NULL;
}

/* Invierte la pila que se le pasa como parametro */
void inviertePila(int pila) {
    int pilaAux;
    struct nodo *aux, *aux1;

    aux = pilas[pila];
    pilas[pila] = NULL;

    while(aux != NULL) {
        apila(pila, aux->elem);
        aux1 = aux->sig;
        free(aux);
        aux = aux1;
    }
}
<!--xc2--></td></tr></table><div class='postcolor'><!--exc2-->

La cuestion de ordenar los valores de las pilas lo dejo para un poco más adelante, pues ahora ya me estoy planteando bastante cosas y prefiero perfilar bien esto.

Empece con la implementación de una pila porque pensaba que sería lo más fácil, ya estoy viendo que tiene su intrigulis, jejeje. Veremos cuando pase a la implementacion "generica" de listas simples enlazadas, doblemente enlazadas y demás... Bueno todo se andará.

De nuevo, muchas gracias, pretendo contruir una implementación que nos ayude a todos a usar estas estructuras de datos en cualquier situación sin tener que implementarlas cada vez que las necesitemos. Gracias!

Nos vemos :hola:
Core Dumped
zirrus.es

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #10 en: Martes 21 de Diciembre de 2004, 04:07 »
0
Citar
¿Como duplico los nodos de una pila si no se cuanto espacio debo reservar para los nuevos nodos?

Lo ideal seria realizar una duplicacion nodo a nodo verificando el tipo de dato contenido,
sin embargo si el dato almacenado es otro puntero (como en elcaso de las cadenas) tal vez seria de gran ayuda (sino necesario) tener otro campo en la estructura que indique el tamaño del tipo de dato almacenado en el caso de que sea un puntero.

Citar
¿Puedo usar un malloc con el tipo de datos void?.
No lo recuerdo pero supongo que si.

Citar
a la misma zona de memoria!!! los datos son los mismos!.

umm si asi es, pero si llo vemos desde el punto de vista funcional puede que no siempre esas sean las intenciones del desarrolador...
adivina...
requeririamos otro campo que nos indique si el puntero posee su propio espacio en memoria o si apunta a un segmento 'compartido' por decirlo de algun modo, esto con el fin de que en el momento de liminar un nodo se verifique que tipo de puntero es, y si es compartido se borra la referencia pero no el contenido en memoria y si es propio se borarian tanto la referencia como el contenido.
Citar
- El problema anterior tambien me ha surgido al intentar implementar la funcion de concatenar pilas. Dado que aunque concatene pilas, la pila resultante tendra nuevos nodos, vale, pero sin embargo dichos nodos tendran apuntadores a los datos que tambien estan siendo apuntados por las pilas que han sido concatenadas.
Se solucionaria con el comentario anterior.
Citar
Estoy pensando en mantener una lista enlazada que crezca por cada pila que se solicite y que contenga un identificador entero para identificar la pila dentro de la lista.

Esto es lo mejor, pero adicionalmente podrias poner un limite a la creacion de pilas y decir por ejemplo que puedes manejar 255 pilas seria una buena idea.

Ahora pasando al tema de la programacion tengo nuevas observaciones.

1-no deberias usar usar exit(1) ni ninguna de sus variantes realmente esta instruccion invoca una de las interrupciones del DOS y se usaba para recuperar el contexto de ejecucion del sistema operativo una ves ejecutado un programa, pero esta fucion contribuye a una mala estructura en el codigo ya que causa una salida abrupta del programa y no deja que este continue su ciclo normal.
en su lugar deberias hacer algo como esto:
Código: Text
  1.  
  2. int creaPila(char *error)
  3. {
  4.     struct nodo **aux;
  5.     int i;
  6.     int ret;
  7.     char err[255];
  8.  
  9.     memset(err,'&#092;0',255);
  10.  
  11.     if((aux = (struct nodo **) malloc(sizeof(struct nodo *)*num_pilas)) == NULL)
  12.     {
  13.         strcpy(err,&#34;Error al crear la pila&#34;);
  14.         ret = NULL;
  15.     }
  16.     else
  17.     {
  18.         for(i = 0; i &#60; num_pilas; i++)
  19.             aux[i] = pilas[i];
  20.  
  21.         if((pilas = (struct nodo **) malloc(sizeof(struct nodo *)*(num_pilas + 1))) == NULL)
  22.         {
  23.             ret = NULL;
  24.             strcpy(err,&#34;Error al crear la pila&#34;);
  25.         }
  26.         else
  27.         {
  28.             num_pilas++;
  29.             for(i = 0; i &#60; num_pilas - 1; i++)
  30.                 pilas[i] = aux[i];
  31.  
  32.             pilas[num_pilas-1] = NULL;
  33.             ret = num_pilas-1
  34.         }
  35.     }
  36.    error=err;
  37.     return ret;
  38. }
  39.  
  40.  
Bueno eso es una idea muy basica puedes mejorarla un poco más.

2- En tu libreria no deberian haber perror (por eso los quite arriba) y de hecho nada que imprima ni que termine el programa (como el exit), todo deberia estar tan controlado y manejado que el usuario podria validar que si por ejemplo CrearPila fallo el se entere revisando que devolvio null y si quiere detalles del error revisaria el contenido de la cadena error, y seria el usuario (osea el desarrolador que use tu libreria) quien decida si termina el programa o si libera recursos para que haya mas memoria o si trata de hacer cualquier cosa que el quiera. La libreria no puede decidir por el usuario.
Por eso en el primer post te sugeria que existiera una fucion que devolviera un string con el contenido del nodo incluso variantes que solo devuelvan el dato y ortras que devuelvan la info completa pero nunca que lo impriman directamente.
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Re: Implementacion De Una Pila
« Respuesta #11 en: Miércoles 22 de Diciembre de 2004, 17:18 »
0
Ya estoy de nuevo. Con esto de las fiestas, uno tiene menos tiempo para entretenerse, aunque deberia ser al reves :D.

Para el problema que tenia acerca de qué ocupaba cada nodo de la pila (realmente es cuánto ocupaba a lo que apuntaba cada nodo de la pila) he incluido un campo en los nodos que especifica el tamaño (en bytes) de los datos que son apuntados por el campo elem. Así cuando duplico una pila, se cuanta memoria debo reservar y por consiguiente cuánta debo duplicar para cada nodo. La función afectada ha sido apilar, que contiene un parámetro más indicando el número de bytes que ocupan los datos que se apilan. El usuario deberá tener cuidado especialmente con los datos de tipo cadena, pues si usa la función strlen para establecer el contenido, estará pasando por alto el '\0' de fin de cadena, y al duplicar (como uso la funcion memcpy) solo copiaré exactamente la longitud de la cadena, sin incluir el '\0'.

Asi que ya he implementado las funciones de duplicarPila y concatenaPila (la segunda usa a la primera). Tambien he modificado lo del control de errores. Como me has comentado JuanK, he creado una variable global (grrrr, tantas globales...) que se llama errorPila que contiene el error que se ha producido, y las funciones que provocan el error devuelven -1 en caso de haber pasado algo. Si el usuario de la librería quiere comprobar el error, puede acudir a la variable errorPila para ver que ha pasado.

En la función crearPila he desestructurado un poco la implementación para el control de errores, creo que se ve mas claro poner en medio un return -1, porque si empiezo a poner if...else y tabulo, me quedo sin pantalla ;). De hecho, como comente, mi costumbre era esa, hacer el return cuando quisiera, pero pensandolo estructuradamente, y ya que intento hacer una implementación para el "mundo en general" mejor hacerlo "bien", como he tenido que hacer en funciones como duplicaPila y demás.

Sigo pensando que el modo que tengo de crearPilas es ineficiente, y tendre que modificarlo para optimizarlo. Por ahora uso un array, pero esta claro que debo controlar mejor los "huecos" de ese array cuando se liberen.

He estado pensando en las funciones de ordenar el contenido de las pilas, me parece que no terminan de ser muy útiles y pueden ser bastante complicadas. La finalidad de usar una pila es para usar el concepto de LIFO (last input first output), ¿quien va a querer ordenarlas? Para eso, que use una lista enlazada (que tambien me he propuesto implementar, en cuanto acabe esto ;)). Además, en algún momento tendre que echar el freno, si no, no termino de implementar nunca la librería. ¿Qué opinais?

Así que centrémonos en lo que llevo hecho. ¿Alguna función más que sea realmente útil para el manejo de pilas? Por ahora, lo que pretendo modificar es la administración de las pilas (creación y demás). Estoy abierto a cualquier opinion para refinar esto.

Adjunto un ZIP con el fichero .h y el fichero .c que si no voy a saturar este post con tado XCODE.

Muchas gracias a todos!!!

Nos vemos  :hola:
Core Dumped
zirrus.es

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Re: Implementacion De Una Pila
« Respuesta #12 en: Lunes 27 de Diciembre de 2004, 00:24 »
0
Buenas de nuevo. Como dije, parece mentira pero en vacaciones (y mas en navidad) es cuando menos encuentra uno tiempo para dedicarse a sus cosas ;).

Ya he modificado la ultima implementación, en lo relativo a la creación y modificacion de las pilas. Ahora cada pila es representada como un nodo dentro de una lista enlazada. Cada uno de esos nodos contiene un puntero a la pila en sí, además de un identificador para poder ser localizada.

Bueno, creo que la cosa esta casi lista. Espero posibles correcciones.

Nos vemos :hola:
El mensaje contiene 1 archivo adjunto. Debes ingresar o registrarte para poder verlo y descargarlo.
Core Dumped
zirrus.es

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #13 en: Martes 28 de Diciembre de 2004, 17:01 »
0
Bueno en esta epoca es muy complicado...
tratare de sar tiempo luego para continuar con el proyecto.
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Re: Implementacion De Una Pila
« Respuesta #14 en: Lunes 10 de Enero de 2005, 11:34 »
0
Bueno, vuelta a la rutina con ello intentare sacar un poco de tiempo para acabar esto.

Voy a ver si termino de comentar el código y a ver si realizo un poco de documentacion acerca del funcionamiento de las funciones y de las estructuras que se crean/destruyen en memoria.

No estoy muy puesto en cuestion de licencias, pero si quiero que sea "libre", que licencia debo adjuntar, la de GNU? o la de GPL? Algun "abogado" que me guíe un poco plis :blink:

Nos vemos :hola:
Core Dumped
zirrus.es

JuanK

  • Miembro de ORO
  • ******
  • Mensajes: 5393
  • Nacionalidad: co
    • Ver Perfil
    • http://juank.io
Re: Implementacion De Una Pila
« Respuesta #15 en: Lunes 10 de Enero de 2005, 13:21 »
0
Isa la LPL, es un proyecto que estamos haciendo con unos amigo ;). bueno yo no he hecho nada...  :ph34r:
www.telepormedia.com/foro
El mensaje contiene 1 archivo adjunto. Debes ingresar o registrarte para poder verlo y descargarlo.
[size=109]Juan Carlos Ruiz Pacheco
[/size]
Microsoft Technical Evangelist
@JuanKRuiz
http://juank.io

Nagisa

  • Miembro MUY activo
  • ***
  • Mensajes: 119
  • Nacionalidad: es
    • Ver Perfil
Re: Implementacion De Una Pila
« Respuesta #16 en: Sábado 12 de Febrero de 2005, 12:44 »
0
Hola Cirrus!!  :hola: Acabo de echarle una ojeada a tu idea de las pilas y me parece que esta muy bien. La verdad que yo tambien estoy harto de usarlas en la Universidad, aunque quizas hay otras estructuras de datos que no sean tan usadas/conocidas y se podrian analizar en este topic.

Pasando al planteamiento que haces de pilas, creo que sobran algunas operaciones y falta una.  :(

En principio, en un TAD unicamente debes incluir las operaciones que permitan el trabajo basico: si hay algo que se puede hacer con lo que das, que sea el usuario quien lo programe, no el diseñador.  ;)

Asi por ejemplo, veo redundante la que te invierte una pila (sacas los elementos en una auxiliar y devuelves la auxiliar) o la que te los ordena...  :blink:

Aparte, si bien a nivel de funcionalidad estan bien, cualquier cosa que altere el orden de los elementos de la pila es algo "aberrante"  :alien:  teniendo en cuenta la filosofia de esta ED (LIFO : Last In, First Out).

Como dije, echo de menos una operacion que te diga si la pila esta vacia, ya que nunca podremos extraer cosas de esta pila (excepcion y todo eso).

Por lo tanto, y desde mi punto de vista, las operaciones que deberian de haber son:
  • Crear_Vacia</li>
  • Apilar</li>
  • Desapilar</li>
  • Primero (prescindible, dependiendo del diseño)</li>
  • Es_Vacia</li>
  • Destruir</li>
  • Recorrido (opcional) : Aplica una operacion a todos los elementos de la pila (orden superior y todo eso...)</li>
Yo tengo una implementacion muy sencillita para enteros, aunque estamos con el problema de genericidad de tipos que comentabas antes...  :(  Con los punteros a void se soluciona en parte, aunque la idea es que todos los elementos de una pila sean del mismo tipo. Aqui te la dejo por si quieres echarla un vistazo, aunque es demasiado basica...

Aunque como dije al principio, me parece muy guay lo que estas haciendo. Espero que desmenuzes otras estructuras algo mas interesantes (arboles-B o cosas asi)  :lightsabre:

Saludos!!  :hola:
   

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Re: Implementacion De Una Pila
« Respuesta #17 en: Sábado 12 de Febrero de 2005, 13:02 »
0
Hola! que alegría saber que lo que uno hace es util! ;)

Ahora mismo estoy de examenes, por eso tengo este proyecto un poco de parón (va ya un mes...). Pero en cuanto acabe quiero terminar esta implementación y comenzar otra.

De esta implementación solo me faltaba terminar la documentación, pero ahora que me comentas eso, lo estudiaré para ver si podemos refinarla un poco más. Al fin y al cabo lo que quiero hacer es una implementación lo más útil para "todo el mundo", con las funciones básicas que siempre se necesitan.

Después de la pila estoy pensando en una lista enlazada, que también es una estructura muy utilizada. Más adelante espero adentrarme en cosas "extrañas" del estilo de árboles B, tablas hash y demás. Todo a su tiempo.

Nos vemos :hola:
Core Dumped
zirrus.es

CiRRuS

  • Miembro MUY activo
  • ***
  • Mensajes: 218
    • Ver Perfil
    • http://zirrus.es
Re: Implementacion De Una Pila
« Respuesta #18 en: Domingo 13 de Marzo de 2005, 14:22 »
0
Paso la temporada de examenes, y el desastroso parcial de Redes de anteayer viernes...

Siento el gran parón que le he dado al proyecto, pero hay ciertas prioridades, y entre ellas, es sacarse la carrera ;).

Bueno, retomo el trabajo. Me quede con la documentación de la implementación. Ya la tenía empezada, así que creo que no me faltará mucho. Al final he pensado adherirla a la licencia GPL, que es la más utilizada en estos ámbitos.

Nos vemos :hola:
Core Dumped
zirrus.es