3
\$\begingroup\$

I'm going through the K&R book (2nd edition, ANSI C ver.) and want to get the most from it. How does the following solution look to you? Note that, for the sake of exercise, I don't want to use techniques not introduced yet in the book. Also, I'm trying to reuse whatever code/philosophy already presented in the book.

Related: K&R Exercise 1-13. Printing histogram of word lengths (horizontal variant)

Program code

/* Exercise 1-13. Write a program to print a histogram of the lengths of
 * words in its input. It is easy to draw the histogram with the bars
 * horizontal; a vertical orientation is more challenging.
 * */
/* Solution 2: Vertical Bars
 * */
#include <stdio.h>

#define MAXLEN      20  /* Any word longer than this will get counted in
                         * the >MAXLEN histogram. */
#define MAXHEIGHT   20  /* Height for chart printout.
                         * The whole chart will be scaled to fit. */

int main()
{
    int c;
    int readEOF;
    int alnumCount;
    int i, j, k, n, s;
    int wordCount[MAXLEN+1];    /* Last element will count the
                                 * over-length words. */
    int wordCountD[MAXLEN+1];
    int maxWordCount;
    int maxWordCountD;
    int labelD[MAXLEN+1];
    float scale;
    int npow;

    for (i = 0; i <= MAXLEN; ++i)
        wordCount[i] = 0;

    /* Perform the counting */
    alnumCount = 0;
    readEOF = 0;    /* State to avoid having to repeat code just for the
                     * last word, should it be terminated with EOF. */
    c = getchar();
    while (readEOF == 0) {
        if ((c >= '0' && c <= '9') || 
            (c >= 'a' && c <= 'z') ||
            (c >= 'A' && c <= 'Z'))
            ++alnumCount;
        else {
            if (alnumCount > 0) {
                if (alnumCount <= MAXLEN)
                    ++wordCount[alnumCount-1];
                else
                    ++wordCount[MAXLEN];
                alnumCount = 0;
            }
        }
        if ((readEOF = (c == EOF)) == 0)    /* ! operator not introduced
                                             * yet */
            c = getchar();
    }

    /* Chart Printing Section
     * */
    /* The chart will look like:
     *          N      
     *      1 1 * 1 1  
     *      * * * * * 0
     *      - - - - - -
     *      1 2 3 4 M >
     *                M
     * showing both the label and the histogram value, so we can display
     * accurate information in addition to scaled graphical
     * representation.
     * */

    /* Calculate number of digits for each histogram count,
     * and find the maximums for both the count & number of digits */
    maxWordCount = 0;
    maxWordCountD = 0;
    for (i = 0; i <= MAXLEN; ++i) {
        wordCountD[i] = 1;
        for (j = wordCount[i];  j >= 10;  j = j/10)
            ++wordCountD[i];
        if (maxWordCount < wordCount[i])
            maxWordCount = wordCount[i];
        if (maxWordCountD < wordCountD[i])
            maxWordCountD = wordCountD[i];
    }

    /* Calculate number of digits for each label */
    for (i = 0; i < MAXLEN; ++i) {
        labelD[i] = 1;
        for (j = i + 1;  j >= 10;  j = j/10)
            ++labelD[i];
    }
    labelD[MAXLEN] = labelD[MAXLEN-1];

    /* Calculate the scale factor */
    scale = 1.0*(MAXHEIGHT - maxWordCountD - 1 - 1 - labelD[MAXLEN]) / 
            maxWordCount;

    /* Fix a scale rounding bug resulting in chart being 1 line
     * shorter for some cases */
    if (scale * maxWordCount < MAXHEIGHT - maxWordCountD - 1 - 1 - 
        labelD[MAXLEN])
        scale = scale + 0.000001;

    /* Print the chart only if MAXHEIGHT is sufficient to print at least
     * the values */
    if (maxWordCount * scale >= 0) {
        /* Print vertical histogram. */

        /* Row loop, plot count & histograms */
        for (i = MAXHEIGHT - 1 - 1 - labelD[MAXLEN]; i > 0; --i) {
            /* Column loop */
            for (j = 0; j <= MAXLEN; ++j) {
                if (i <= wordCount[j] * scale)
                    printf("* ");
                else {
                    /* What digit do we have to print? */
                    n = wordCountD[j] - (i - wordCount[j] * scale) + 1;
                    if (n > 0) {
                        n = wordCountD[j] + 1 - n;
                        npow = 1;
                        for (k = n; k > 1; --k)
                            npow = npow * 10;
                        /* Calculate & print the required digit.
                         * Note: % operator not yet introduced. */
                        printf("%d ", (wordCount[j]/npow) - 
                        (wordCount[j]/npow)/10*10);
                    }
                    else
                        printf("  ");
                }
            }
        putchar('\n');
        }
        /* Column loop, plot axis */
        for (j = 0; j <= MAXLEN; ++j)
            printf("--");
        putchar('\n');
        /* Row loop, plot labels */
        for (i = 0; i <= labelD[MAXLEN]; ++i) {
            /* Column loop */
            for (j = 0; j <= MAXLEN; ++j) {
                /* Tweak for the last label, print '>' and "virtually"
                 * shift the row further on. */
                if ((s = (j == MAXLEN)) && i == 0)
                    printf("> ");
                /* What digit do we have to print? */
                n = labelD[j] - (i-s);
                if (n > 0 && n <= labelD[j]) {
                    npow = 1;
                    for (k = n; k > 1; --k)
                        npow = npow * 10;
                    /* Calculate & print the required digit.
                     * Note: % operator not yet introduced. */
                    printf("%d ", ((j+1-s) / npow) - 
                        ((j+1-s) / npow)/10*10);
                }
                else
                    printf("  ");
            }
            putchar('\n');
        }
    }
    else
        printf("Error: insufficient screen width for print-out.\n");
}

Output when run on the program code

$ ./ch1-ex-1-13-02 < ch1-ex-1-13-02.c
1                                         
6                                         
8                                         
*                                         
*                                         
*                                         
*                                         
*                                         
*                                         
*   7                                     
* 5 3 6 5 6                               
* 9 * 4 5 3                               
* * * * * *     3                         
* * * * * * 2 1 0 2                       
* * * * * * 0 7 * 1                       
* * * * * * * * * * 3 8 6 1 0 0 0 0 0 0 0 
------------------------------------------
1 2 3 4 5 6 7 8 9 1 1 1 1 1 1 1 1 1 1 2 >   
                  0 1 2 3 4 5 6 7 8 9 0 2 
                                        0 
\$\endgroup\$
  • \$\begingroup\$ I take it you haven't got to isalnum() yet? Comparing character values (apart from the digits, which are specified to be consecutive) is fragile! \$\endgroup\$ – Toby Speight Oct 18 '18 at 17:18
  • \$\begingroup\$ I'm aware of it, but it isn't yet introduced at this point in the book. \$\endgroup\$ – div0man Oct 18 '18 at 17:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.