martes, 4 de enero de 2011

Kernighan_Ritchie_1.13 (Histograma horizontal)

_______________________________________________________________________________
1.13a Escriba un programa que imprima el histograma de las longitudes de las palabras de su entrada. Es fácil dibujar el histograma con las barras horizontales; la orientación vertical es un reto más interesante. (La versión de este programa con el histograma vertical aparece en la siguiente entrada).
_______________________________________________________________________________
Solución:
_______________________________________________________________________________

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Este programa imprime un histograma con las longitudes de las palabras de su +
+ entrada                                                                      + 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+                                   ALGORITMO:                                 +
+ DATOS:                                                                       +
+ Un arreglo de tamanio Tamano_Arreglo  (El numero de palabras del texto de    +
+ entrada)                                                                     +
+ Una variable llamada numero_palabra (recorrera las posiciones del arreglo)   +
+         Nota: Para evitar la posicion 0,  se incrementa en 1 el tamanio del  +
+               arreglo                                                        +
+               y la variable numero_palabra se inicia en 1                    +
+                                                                              +
+                                                                              +
+ Arreglo:                                                                     +
+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 |........|Tamano_Arreglo + 1|                    +
+       |                                                                      +
+       |                                                                      +
+  numero_palabra = 1    (El numero de palabra empieza en 1 )                  +
+                                                                              +
+  Inicialmente las posiciones del arreglo son llenadas con 0 (no hay letras   +
+  en ninguna palabra)                                                         +
+                                                                              +
+                                                                              +
+ PROCEDIMIENTO:                                                               +
+                                                                              +
+ Recibe Un caracter                                                           +
+ Mientras el caracter leido sea distinto de EOF (fin de archivo)              +
+   Si el caracter es tabulador, espacio o linea                               +
+   cambia de poscion en el arreglo (pasa a la siguiente posicion del arreglo) +
+   Si el caracter es distinto de tabulador, espacio o linea                   +
+   Incrementa el contador de palabras del arreglo                             +
+   Lee el siguiente caracter                                                  +
+                                                                              +                           
+                                                                              +
+  Imprimir:                                                                   +
+  Recorre cada localidad del arreglo                                          +
+  Impime tantos asteriscos como lo diga el número que almacena cada posicion  +
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include <stdio.h>                             
#define Tamano_Arreglo 100             
               
main()               
               
{                              
printf("\nIntroduzca una cadena y se imprimira un histograma con el numero de caracteres.\n");   
printf(" de cada palabra.\n");
               
int c; /*Se leera caracter por caracter.*/
int i; /* i es el contador del ciclo for */
int s; /* s es el contador de un ciclo for */                  
int numero_palabra = 1; /* Se asume como uno el contador de palabras   */               
int caracteres[Tamano_Arreglo + 1] = { 0, 0 }; /*Al inicializar a 0 los  
                primeros elementos del arreglo se inicializan todos. */
                              
while ( (c = getchar()) != EOF)         
{                             
if( c == '\t' || c == '\n' || c == ' ' )  
/* Si se encuentra un espacio, entonces se considera que se ha iniciado   
una nueva palabra */            
{               
numero_palabra++;               
}               
else                              
caracteres[numero_palabra]++;               
               
/* Si no es un espacio, se incrementa el numero de caracteres de la palabra 
 actual */                   
}               
                                                
printf("\n\n");               
      
for ( s = 1; s <= numero_palabra; s++ )                   
{                             
for ( i = 1; i <= caracteres[s]; i++ )      
{                              
printf("*");               
}               
               
printf("\n");               
}               
               
printf("\n");               
               
return;                              
}               

La salida de este programa, con este archivo

Puedo escribir los versos mas tristes esta noche

como entrada es:

Introduzca una cadena y se imprimira un histograma con el numero de caracteres.
de cada palabra.
Numero de palabra es: 10 

*****
********
***
******
***
*******
****
*****

_____________________________________________________________________________________
Esta entrada es parte de los problemas resueltos del libro El Lenguaje de Programación C de B. kernighan y D. Ritchie.
Entrada Anterior
Entrada Siguiente

19 comentarios:

  1. Hola, queria saber porque numero_palabras lo inicias en 1.
    Yo probe de iniciarlo en 0 tanto numero_palabras como 'S' e 'I' y lo que hace es agregar un * de mas a cada palabra introducida y tambien agrega una ultima linea con un solo *, no lo estoy entendiendo..

    Tambien queria saber porque es necesario inicializar los arreglos en 0, esto siempre se hace cuando se trabaja con arreglos?
    Porque a tamano_arreglo le sumas 1?

    Gracias.. Saludos..

    ResponderEliminar
    Respuestas
    1. Hola, Roberto. Le he agregado algunos comentarios al programa que pueden ayudar. La razón por la que empiezo en 1 es para evitar la posición 0 de arreglo.
      La razón por la que tienes que inicializar las entradas del arreglo en 0 es porque esos espacios de memoria tienenn asignados "valores basura" que no tienen que ver con el programa. Y sí, es una buena práctica inicializar a 0 tus arreglos, aún cuando vayas después a asignarles otros valores. Aquí además es necesario poner las entradas en 0 porque es el número de caracteres con el que empieza cada palabra.
      La razón por la que le arrego 1 a la variable tamano_arreglo, a la hora de definir el arreglo, es para evitar la posicion 0 del arreglo. Los informáticos cuentan siempre desde 0, algo que tiene que ver con el sistema posicional de numeración y las localidades de memoria, pero a mi no me parece muy natural iniciar ahí, así que sólo muevo una posición el tamaño.
      Saludos y ojalá te sirva un poco esta breve explicación.

      Eliminar
  2. Gracias por la explicacion.. voy a pasar de seguido por el blog :)

    Lo de [tamano_arreglo + 1] es para empezar a usar el arreglo desde la posicion 1, no es que aumentas el tamaño del arreglo en 1, osea 101.. es asi?

    gracias..

    ResponderEliminar
    Respuestas
    1. Sí empiezo en la posición 1 y sí aumento el tamaño del arreglo en 1. Si lo dejara como arreglo[tamano_arreglo], se generarían 100 localidades empezando por 0 y llegando a 99:

      |0|1|2|3|4|5|.............|99|

      pero por alguna razón ya me acostumbré a trabajar empezando desde 1, y para seguir teniendo 100 elementos disponibles, defino un arreglo de 101 elementos como arreglo[tamano_arreglo + 1], con lo cual queda:

      |0|1|2|3|4|...............|100|

      Es sólo cuestión de gustos. Puedes empezar a contar en 0, pero a mí se me hace poco natural. Muchas gracias y de nuevo saludos a Argentina.

      Eliminar
    2. mmmm...entiendo la idea pero no es mejor simplemente poner que tamaño_arreglo vale 101 en vez de colocarlo en 100 para luego sumarle 1?. o solo estas haciendo eso para mayor facilidad de lectura?

      Eliminar
  3. Mmm.. vos inicializas el arreglo en 1 con el valor que contiene numero_palabra .. No con [tamano_arreglo + 1]

    Y lo de mantener el arreglo en 100 secciones no es lo mismo hacer:

    #define tamano_arreglo 101

    Saludos..

    ResponderEliminar
    Respuestas
    1. Mira, un arreglo es un conjunto de localidades adyacentes de memoria que pueden almacenar datos de un mismo tipo (int, char, float, etc). Gráficamente podemos representarlo como una hilera de rectángulos. El arreglo como tal tiene un nombre, un tipo y un tamaño. En este caso es de tipo int, se llama caracteres y tiene 101 celdas. Cada celda tiene asociados dos datos importantes, la dirección y el contenido. Para hacer referencia a una localidad específica del arreglo, digamos la 45, usamos el nombre del arreglo y, entre corchetes, el número de elemento:

      caracteres[45]

      Cuando nosotros definimos un arreglo, las localidades tienen valores "basura" que pueden ser un número binario muy grande. Hay que inicializar las celdas para borrar esos valores. Una buena manera de hacer esto es poniendo: caracteres[UnNumeroEnteroSinSigno] = {0,0.0}, donde UnNumeroEnteroSinSigno es el tamaño del arreglo. Si se inicializan a 0 las primeras celdas, el compilador inicializa a 0 todas.

      Con respecto a este programa, inicializo a 0 todas las celdas y luego les asocio un número entero. Este número es la cantidad de caracteres que tiene cada palabra. En la ejecución de ejemplo, le doy como entrada la frase: "Puedo escribir los versos mas tristes esta noche", la primera palabra es Puedo, que tiene 5 caracteres, así que en la celda 1 almaceno el número 5. Me muevo a la siguiente posición (la 2) y leo cuántos caracteres tiene la siguiente palabra (es "escribir" y tiene 8), y asigno ese número a la segunda posición. Hago eso hasta que encuentro el carácter de fin de archivo EOF.
      Con relación a lo que preguntas acerca de si es lo mismo

      #define tamano_arreglo 101

      sí, es lo mismo y se puede hacer así.
      Con respecto a la memoria, igual y te sirve leer ésta entrada
      Saludos.

      Eliminar
  4. Hola! yo tambien estoy un poco confundido pero desde el primer ejemplo que da el libro en el punto 1.6 Arreglos; entiendo que se necesita un for y una variable para recorrer en las posiciones del vector, lo que no entiendo es como llena los valores en cada posicion. el codigo es el siguiente:

    /*Cuenta digitos, espacios en blanco y otros*/
    #include

    main()
    {

    int c, i, nwhite, nother;
    int ndigit[10];

    nwhite = nother = 0;
    for (i = 0; i<10; ++i)
    ndigit[i] = 0;

    while((c = getchar()) != EOF)
    if (c => '0' && c =<'9')
    ++ndigit[c-'0'];

    else if (c == ' ' || c = '\t' || c = '\n')
    ++nwhite;

    else
    ++ntoher;

    printf("digitos = ");
    for(i=0; i<10; ++i)
    printf("%d",ndigit[i]);

    printf(", espacios en blanco = %d, otros =%d\n",nwhite,nother);

    }

    lo que no entiendo es el:

    if (c => '0' && c =<'9')
    ++ndigit[c-'0']; porque c-'0' y el ++ del ndigit???

    ResponderEliminar
    Respuestas
    1. Mira. Lo primero es que el enunciado correcto es

      if(c >= '0' && c =<'9')
      ++ndigit[c-'0'];

      En el if aparece la condición c >= '0' && c <='9'
      '0' hace referencia a valor ascii de 0, que es 48, de la misma manera '9' hace referencia al valor ascii de 9 (57). Dado que los dígitos tienen valores ascii consecutivos, 0 = 48, 1 = 49, 2 = 50, ... 9 = 57
      Así pues, la condición selecciona sólo a los dígitos del 0 al 9. Si esta condición se cumple, se ejecuta la instrucción

      ++ndigit[c-'0'];

      Fíjate primeto en c - '0'. Supongamos que c = 0 y por lo tanto c -'0' hace referencia al valor de c (recuerda que la variable c es de tipo int, y por lo tanto almacena el valor ascii del carácter recibido) menos el valor de 0. Si c = 0, entonces 48 - 48 = 0, y por lo tanto se está haciendo referencia al elemento 0 del arreglo. Ya que se encontró un 0, se incrementa el arreglo en esa posición.
      Si c = 1 (ascii 49) entonces c - '0' = 49 - 48 = 1 y se hace referencia al elemento 1 del arreglo. Como se recibió un 1, se incrementa el contador de 1, y así sucesivamente.
      Con ésto ya sabrás lo que hace la instrucción

      for(i=0; i<10; ++i)
      printf("%d",ndigit[i]);

      imprime el número de veces que aparece 0, el número de veces que aparece 1, etc.
      Espero te haya podido explicar bien.
      Muchos saludos.

      Eliminar
    2. Ok ya entendí de nuevo mil gracias, sino fuera por tu ayuda ya hubiera dejado esto por la paz. Solo una cosa mas, las instrucciones se ejecutan de derecha a izq. por eso es >= (mayor o igual) y no => (igual o mayor) o solo es por mera sintaxis? Y por ultimo ya entendí que la condición if por ser leido c como un caracter del codigo ascii y ser valores consecutivos puede tomar una posicion del arreglo e incrementar ndigit en la poscion a uno y si se repite lo incrementa de nuevo en la misma posicion del arreglo asi sabemos cuantas veces se dio la ocurrencia de dicho digito. Amigo Hitmontop eres grande thxs again.

      Eliminar
    3. Los operadores de relación se escriben así: >= ó <=. Si los escribes al revés se produce un error de sintaxis que te lo indicará el compilador. Y por otra parte, qué bueno que quedaron claras las secciones del programa por las que me preguntaste. El libro de Kernighan y Ritchie es difícil, pero los beneficios de este primer capítulo son enormes. Saludos y gracias por el comentario.

      Eliminar
  5. una duda sencilla porque solo printf("*"); para imprimir el valor de cada posición del arreglo? Con esta instrucción no imprime solo un * ?.

    ResponderEliminar
  6. Ya entendi el ejercicio discula me voy a esforzar mas antes de preguntar. :P el mismo ciclo imprime un asterisco por cada cada vez que se ejecuta el ciclo for dependiendo el valor que tiene el arreglo en esa posición. Tengo que razonar mejor.

    ResponderEliminar
  7. #include
    #include

    int main(int argc, char *argv[])
    {
    printf("Grafico de Numero de letras por palabra\n");
    int c;
    while((c = getchar()) != EOF)
    {
    if(c == ' '||c == '\n'|| c == '\t')
    {
    printf("\n");
    }
    else
    {
    printf("#");
    }

    }

    return 0;
    }


    Yo lo hice asi, cuando termine vi tu codigo y dije... a cierto que habia que hacerlo con arreglos... mas tarde lo hago con arreglos, pero en verdad asi es bastante mas facil...

    ResponderEliminar
  8. me podrian explicar esta parte del codigo que no le entiendo
    for ( s = 1; s <= numero_palabra; s++ )
    {
    for ( i = 1; i <= caracteres[s]; i++ )

    ResponderEliminar
    Respuestas
    1. el 1º es si s es menor al numero de palabras se hara lo de abajo
      que es el que pondra los asteriscos cuando termine s sera 2 (s=2) y escribira un intro

      Eliminar
  9. a mi no me funciona y lo copie tal cual escribo y nada

    ResponderEliminar
  10. tengo una duda: como hace se ejecuta el programa?? osea el lee primero todos conteos y operaciones en el while y luego realiza la parte de imprimir con el for o por cada caracter que yo introduzca realiza while primero e inmediatamente imprime mediante el for?

    ResponderEliminar

Related Posts Plugin for WordPress, Blogger...