[C] Decimal a base n, con n<10

Iniciado por edr89, 20 Octubre 2013, 22:51 PM

0 Miembros y 3 Visitantes están viendo este tema.

edr89

Hola, de nuevo posteando códigos de nivel básico-intermedio, uso una lista simple para ir almacenando cada digito del numero convertido:

Comentarios y sugerencias bienvenidas! :)
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXBASE 9
struct numeroB10
{
    int num;
    struct numeroB10 *next;
};
typedef struct numeroB10 BASE;
typedef BASE *BASE_PTR;

int input_numero_natural();
int input_mayor_base();
BASE_PTR add_to_list(int residuo, BASE_PTR prev_rec);
char input_error_sino();
void liberar_espacio(BASE_PTR ultimo);

int main()
{
    int numero=0,aux=0,cociente=0,residuo=0,base=0;
    char bucle='0';
    BASE_PTR digit_addr=NULL;
    BASE_PTR impresor=NULL;
    do
    {
        system("CLS");
        printf("Conversor de base 10 a base n\n\nInstrucciones: Ingresa un numero natural en base 10: ");
        numero = input_numero_natural(); //Se valida que sea un dato entero y positivo
        printf("\nAhora elige una base entre 1 y 9 para convertir el numero: ");
        base = input_mayor_base(); //Validacion de base
            if(numero<base)
                puts("\nEl numero a convetir es el mismo!");
            else
            {
                aux = numero;
                    while(aux>=base)
                    {
                        cociente = aux/base;
                        residuo = aux%base;
                        digit_addr = add_to_list(residuo,digit_addr);
                        aux = cociente;
                    }
                digit_addr = add_to_list(aux,digit_addr);
                impresor = digit_addr;
                printf("\nEl numero %d en base 10 es igual a ",numero);
                    while(impresor!=NULL)
                    {
                        printf("%d",impresor->num);
                        impresor = impresor->next;
                    }
                printf(" en base %d",base);
                liberar_espacio(digit_addr);
                digit_addr=NULL;
                impresor=NULL;
            }
        printf("\n\nQuieres convertir otro numero? ( S/N ): ");
        bucle = input_error_sino();
    }while(bucle=='S');
    return (0);
}

/*Funcion: input_mayor_base
  Proposito: Valida que los datos de entrada del usuario sean numeros y evita que se procesen letras, signos o
             numeros negativos, se usa al pedir la base a convertir.*/
int input_mayor_base()
{
    int respuesta;
    while((scanf("%d",&respuesta) != 1)||respuesta<=1||respuesta>MAXBASE) //en caso de ñ y datos raros.
        {
            while (getchar() != '\n');
            if(respuesta>MAXBASE)
            {
                printf("\nSolo puedes convertir hasta base 9: ");
            }
            else
            {
                printf ("\nDato no valido intenta de nuevo: ");
            }
        }
    return respuesta;
}
/*Funcion: input_numero_natural()
  Proposito: Valida que los datos de entrada del usuario sean numeros y evita que se procesen letras, signos o
             numeros negativos, se usa al pedir la base a convertir.*/
int input_numero_natural()
{
    int respuesta;
    while((scanf("%d",&respuesta) != 1)||respuesta<=0) //en caso de ñ y datos raros.
        {
            while (getchar() != '\n');
            if(respuesta==0)
            {
                printf("\nEl numero 0 es el mismo para todas las bases: ");
            }
            else
            {
                printf ("\nDato no valido intenta de nuevo: ");
            }
        }
    return respuesta;
}
/*Funcion: BASE_PTR add_to_list()
  Proposito: Crear un bloque en memoria para el numero a convertir*/
BASE_PTR add_to_list(int residuo, BASE_PTR prev_rec)
{
    BASE_PTR new_rec = NULL;
    new_rec = (BASE_PTR) malloc(sizeof(BASE)); // Crear jugador
    if(!new_rec)
    {   //Validacion de malloc
        printf("Error al reservar memoria");
        exit(1);
    }
    new_rec->num= residuo;
    new_rec->next = NULL;
    if(prev_rec) //Si hay un elemento antes
    {
        new_rec->next = prev_rec;

    }
    return(new_rec);
}
/*Funcion:input_error_sino
  Proposito: Valida que la respuesta sea el caracter S o N,
  se usa en la ultima pregunta para volver a correr el programa */
char input_error_sino()
{
    char respuesta;
    respuesta = getche();
    respuesta = toupper(respuesta);
    printf("\n\n");
        while(respuesta!='S'&&respuesta!='N') //Error si teclea otra cosa
            {
                printf("Opcion invalida, intenta de nuevo: ");
                respuesta = getche();
                puts("");
                respuesta = toupper(respuesta);
            }//Fin mensaje error
    return (respuesta);
}
/*Funcion:liberar_espacio()
  Proposito: borrar numeros generados.*/
void liberar_espacio(BASE_PTR ultimo)
  {
      BASE_PTR aux = NULL;
      aux = ultimo;
      while(aux->next!=NULL)
      {
          ultimo = ultimo->next;
          free(aux);
          aux = ultimo;
      }
      free(aux);
  }

eferion

No se si te has dado cuenta que la usabilidad del código fuera de tu programa es 0.

El núcleo de tu código, es decir, el algoritmo de conversión, está incluido íntegramente en el main. Diseñar soluciones reutilizables implicaría al menos refactorizar ese código para que, por ejemplo para este caso concreto, se pudiese hacer la conversión con un main propio sin tener que "copiar y pegar".

Además, tu programa está limitado a un uso con consola de comandos... portarla para ser utilizada en una aplicación con interfaz gráfica requeriría otro esfuerzo adicional.

A mi, personalmente, me parece estupendo que se publiquen códigos como este, ya que los que están aprendiendo pueden ver códigos de gente más madura. Pero creo que si se hacen cosas así hay que hacerlo con un poco más de cariño.

Un código con casi la misma cantidad de comentarios que un programa desensamblado y que encima plantea una solución no reutilizable deja que desear. Vale que el algoritmo puede ser más o menos curioso e incluso hasta útil... pero no de la forma en la que está presentado.


edr89

Cita de: eferion en 21 Octubre 2013, 09:06 AM
No se si te has dado cuenta que la usabilidad del código fuera de tu programa es 0.
No se exactamente a que te refieres pero yo tampoco le veo gran uso a este código, nunca he trabajado con otras bases y en todo caso las direcciones de memoria estan en hexadecimal, que este programa no convierte El objetivo principal es implementar una lista ligada, el caso particular es convertir un numero de una base a otra.

Cita de: eferion en 21 Octubre 2013, 09:06 AM
para este caso concreto, se pudiese hacer la conversión con un main propio sin tener que "copiar y pegar".
Entonces la conversion debe estar fuera de main? imagino que la impresion de digitos tambien así la estructura de main puede cambiar dando el mismo resultado, o no?

Cita de: eferion en 21 Octubre 2013, 09:06 AM
A mi, personalmente, me parece estupendo que se publiquen códigos como este, ya que los que están aprendiendo pueden ver códigos de gente más madura. Pero creo que si se hacen cosas así hay que hacerlo con un poco más de cariño.
Te entiendo, trato de hacer las cosas lo mejor posible aunque el código refleje lo contrario para usuarios avanzados, en todo caso revela que el proceso de aprendizaje no ha concluido y falta mucho por aprender.

Cita de: eferion en 21 Octubre 2013, 09:06 AM
Además, tu programa está limitado a un uso con consola de comandos... portarla para ser utilizada en una aplicación con interfaz gráfica requeriría otro esfuerzo adicional.
He abogado mucho por los temas de interfaz gráfica pero los profesores insisten que el verdadero poder lo tiene la consola y el GUI es solo la cara bonita del programa que carece de importancia para el curso.  :-\

Saludos!

eferion

Cita de: edr89 en 21 Octubre 2013, 18:46 PM
No se exactamente a que te refieres pero yo tampoco le veo gran uso a este código, nunca he trabajado con otras bases y en todo caso las direcciones de memoria estan en hexadecimal, que este programa no convierte El objetivo principal es implementar una lista ligada, el caso particular es convertir un numero de una base a otra.

El verdadero arte a la hora de diseñar aplicaciones no es que el código funcione como se espera de el... eso es como pedirle a un albañil que levante una pared... lo sabe hacer casi cualquiera. La gracia, y lo complicado, está en conseguir que ese algoritmo que diseñas pueda ser reutilizado y/o mejorado con un esfuerzo mínimo ( entiéndase que este esfuerzo dependerá de la complejidad intrínseca en el propio algoritmo ).

Si tu diseño acaba generando un código que solo es válido para la aplicación actual entonces podrías considerar que estás haciendo mal las cosas.

Ahora te parece que un algoritmo que sea capaz de convertir números entre diferentes bases es algo absurdo que solo te sirve para aprender... y puede que tengas razón... pero llegará un momento en el que el te encuentres en la necesidad de reutilizar código que ya has hecho en otro momento... si no has aprendido las habilidades necesarias para conseguir buen código reutilizable te tocará reescribir el código y/o sudar tinta para encontrar una solución factible.

Se que los profesores no le dan importancia a este aspecto, pero también es cierto que pocos profesores se han movido en el mundo real en el que la forma teórica e ideal de trabajar poco tiene que ver con lo que te vas a encontrar.

Cita de: edr89 en 21 Octubre 2013, 18:46 PM
Entonces la conversion debe estar fuera de main? imagino que la impresion de digitos tambien así la estructura de main puede cambiar dando el mismo resultado, o no?

Todo diseño que hagas debería tener como uno de sus fundamentos la reutilización del código. Si el núcleo de un algoritmo está ubicado en el main mal empezamos.

La representación de los datos, así como el proceso para que el usuario introduzca la información pedida, deberían estar también aislados del núcleo del algoritmo, de esa forma te va a resultar bastante más sencillo reutilizar el algoritmo y adaptarlo a diferentes situaciones.

Cita de: edr89 en 21 Octubre 2013, 18:46 PM
Te entiendo, trato de hacer las cosas lo mejor posible aunque el código refleje lo contrario para usuarios avanzados, en todo caso revela que el proceso de aprendizaje no ha concluido y falta mucho por aprender.

Tengo 30 años, llevo tocando código desde los 5. He programado en basic, pascal, visual basic, php, asp, asp.net, c#, c, c++, ActionScript, perl, bash, ... aún no pasa una semana sin que aprenda una cosa nueva o descubra una manera de mejorar algún aspecto de mi perfil.

Cita de: edr89 en 21 Octubre 2013, 18:46 PM
He abogado mucho por los temas de interfaz gráfica pero los profesores insisten que el verdadero poder lo tiene la consola y el GUI es solo la cara bonita del programa que carece de importancia para el curso.  :-\

Está claro que para aprender a programar, el entorno de consola te permite centrarte más en el objetivo de la aplicación. Desarrollar aplicaciones con interfaz gráfica al final te obligan a estar muy pendiente de los detalles de las ventanas... lo que resta recursos al fin último que es aprender a programar.

Lo que sucede es que los profesores normalmente dejan de lado el tema del diseño del software para permitir, por ejemplo, su reutilización... y eso es un problema.

edr89

Cita de: eferion en 21 Octubre 2013, 22:20 PM
Todo diseño que hagas debería tener como uno de sus fundamentos la reutilización del código. Si el núcleo de un algoritmo está ubicado en el main mal empezamos.

La representación de los datos, así como el proceso para que el usuario introduzca la información pedida, deberían estar también aislados del núcleo del algoritmo, de esa forma te va a resultar bastante más sencillo reutilizar el algoritmo y adaptarlo a diferentes situaciones.

Gracias, nunca habia pensado en esto, ahora que lo mencionas entiendo la portabilidad de un programa. (y revisando varios de mis códigos el nucleo nunca esta bien definido o se encuentra disperso en main)

Saludos!

eferion

Prácticamente cualquiera puede poner una pared ... otra cosa es que luego la pared quede recta, bien nivelada y aguante o que se raje al intentar ponerle el primer clavo.

Llevando el caso a este mundillo puedo afirmar que prácticamente cualquiera es capaz de tirar código... pero luego otra cosa es que el código resultante sea robusto,  reutilizable y fácil de mantener.

ivancea96

Hay muchas funciones, pero como bien te han dicho, la función "primordial" e importante de este programa, está escrita en la función main. Si separas la función, quedaría bastante mejor.

Además, al menos en mi opinión personal, deberías quitar las funciones "input". No quitarlas, sino meterlas todas en una. Yo lo vería más legible.
Y ánimo, a agregarle a ese code hasta la base... 35? Hasta que se acaben las letras inglesas jaja

edr89

Cita de: eferion en 22 Octubre 2013, 11:47 AM
... otra cosa es que el código resultante sea robusto,  reutilizable y fácil de mantener.

entiendo, entonces esto se resuelve en el diseño del algoritmo, no en la códificación  :o

Cita de: ivancea96 en 22 Octubre 2013, 21:15 PM
deberías quitar las funciones "input". No quitarlas, sino meterlas todas en una.

Se me ocurren condiciones para los tres casos
     1) ( numero a convertir > 0 )
     2) ( 1 < base tecleada < 10 )
     3) ( respuesta para repetir el programa == 'S' || 'N' )

Si la tercera funcion regresa dato tipo entero puedo usar los numeros 110, 115 para las letras N, S?
es decir leo un caracter (%c) en scanf() pero lo trato como entero en las condiciones

int input_general(int condicion)
{
    int respuesta;
    switch(condicion)
    {
    case 1:
        {
            // Entrada de numero a convertir
            break;
        }
    case 2:
        {
            //Entrada de base
            break;
        }
    case 3:
        {
            //Quieres repetir el programa: SI, NO?
        }
    }
    return (respuesta);
}


es solo una dea, no la he probado y no se si funcione la parte de tener una variable tipo entero y leer en scanf (%c).

rir3760

Cita de: edr89 en 24 Octubre 2013, 00:22 AMentiendo, entonces esto se resuelve en el diseño del algoritmo, no en la códificación
Al cambiar el algoritmo por fuerza vas a cambiar el código fuente.

Las sugerencias van en la linea del desarrollo de funciones que cumplan una sola tarea, si tienes entrada, salida y la conversión de base ello resultara en tres funciones, una para cada operación.

Tu función principal debe terminar mas o menos así:
char *base10_to_N(int numero, unsigned base)
{
   char *rv;
   
   /* ... */
   
   return rv;
}

En ella realizas la conversión, solo eso. En un caso ideal si quieres reutilizar esa función en otro programa solo necesitas incluir la declaración de la interfaz y su implementación.

Un saludo
C retains the basic philosophy that programmers know what they are doing; it only requires that they state their intentions explicitly.
--
Kernighan & Ritchie, The C programming language