Thursday, 20 January 2011

The C Programming Language (K&R) 01x12—Power Function—Section 1.7


Section 1.7 of “The C Programming Language” by Brian Kernighan and Dennis Ritchie aka K&R introduces user-defined functions. They first example they give is their power function. Time to test out their code.


Sample Code.

I am using Visual C++ 2010 and created the sample code as a console application.

// Function prototype.
int power(int m, int n);

// The standard library includes the system function.
#include <cstdlib>

// Standard I/O library.
#include <cstdio>

int main()
{
     int i;

     printf("K&R Section 1.7 Power Function.\n\n");

     for (i = 0; i < 10; ++i)
           printf("%d %d %d\n", i, power(2,i), power(-3,i));

     // Keep console window open.
     system("pause");

     // Return some value.
     return 0;

} // end main

/* power: raise base to n-th power; n >= 0 */
int power(int base, int n)
{
     int i, p;

     p = 1;

     for (i = 1; i <= n; ++i)
           p = p * base;

     return p;
}


Output.


Wednesday, 19 January 2011

The C Programming Language (K&R) 01x11—Func for Temp Convert—Exercise 1-15


Exercise 1-15 from “The C Programming Language” by Brian Kernighan and Dennis Ritchie aka K&R.

Rewrite the temperature conversion program of Section 1.2 to use a function for conversion.

The first code for section 1.2 is here.

Adding a function to do the conversion calculation is straightforward. I added an argument to choose between converting from C to F or F to C.


Sample Code.

I am using Visual C++ 2010 and created the sample code as a console application.

// Function prototype.
float TempConv(int temp, bool direction);

// The standard library includes the system function.
#include <cstdlib>

// Standard I/O library.
#include <cstdio>

// Constants.
#define LOWER 0 /* lower limit of table */
#define UPPER 300 /* upper limit */
#define STEP 20 /* step size */

int main()
{
     int temp;
    
     // Create table of F to C.
     printf("  F\t    C\n");
     for (temp = LOWER; temp <= UPPER; temp = temp + STEP)
           printf("%3d\t%6.1f\n", temp, TempConv(temp, true));

     // Create table of C to F.
     printf("\n\n  C\t    F\n");
     for (temp = -20; temp <= 50; temp = temp + 5)
           printf("%3d\t%6.1f\n", temp, TempConv(temp, false));

     // Keep console window open.
     system("pause");

     // Return some value.
     return 0;

} // end main
 

float TempConv(int temp, bool direction)
{
     if (direction)
           // Convert Farhenheit to Celcius/
           return (5.0/9.0)*(temp-32.0);
     else
           // Convert Celcius to Farhenheit.
           return temp * (9.0/5.0) + 32.0;
}

Output.


Tuesday, 18 January 2011

The C Programming Language (K&R) 01x10—Histogram of Words—Exercise 1-13


I’m making my way through “The C Programming Language” by Brian Kernighan and Dennis Ritchie aka K&R.

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.

My previous entry (here) was one answer to this exercise. This entry takes the previous code and makes two changes.
 
1. Display the histogram vertically instead of horizontally.
2. Fix a bug in the word parsing.

Displaying the histogram vertically is more difficult. In the previous code, you only looked at one length of word at a time. Start with the count for 1 letter words and print as many asterisks as needed. Then go to the 2 letter words and so on. And there were 14 such categories so you loop 14 times for each length. Straightforward. Not so with a vertical display.

Here you have to add each row for all the word lengths which may have a value or not. That means choosing to print an asterisk or space. So row by row you check each word length to see if there’s an asterisk to add. But how long do you keep this up? It’s not the size of the array. It’s the maximum value in the any one of the elements. There may be 30 three-letter words and 20 four-letter words. You don’t stop at 20. To figure that out I introduced a variable, maxrows, and looped through the array to find the largest count value. If that’s 20, then there has to be one column with 20 asterisks.

As for the bug, the previous code focused on whitespace. Anything that wasn’t whitespace formed a word or part of a word. It shouldn’t. I wanted to change the code so all punctuation and the like were also ignored. So my IsWhitespace function became IsNoneWordChar. But instead of testing for exclusions like say, a period, I tested for valid word characters (i.e., a-z, A-Z, 0-9). Same effect, but different approach.

One of the toughest parts was getting the histogram to line up in the right columns. It took some doing, but I finally figured what was needed.


Sample Code.

I am using Visual C++ 2010 and created the sample code as a console application.

// Function prototypes
bool IsNoneWordChar(char);

// The standard library includes the system function.
#include <cstdlib>

// C++ standard I/O library.
#include <cstdio>

int main()
{
     // Character input.
     char c = 0;
     // Previous character.
     char prevChar = 0;
     // Word Length.
     int WordLength = 0;
     int WordLengthCount[15];

     // Initialize array with zero.
     for (c = 0; c < 15; ++c)
           WordLengthCount[c] = 0;

     // Process user input from keyboard.
     while ((c = getchar()) != EOF) {
           if (IsNoneWordChar(c))
                if (IsNoneWordChar(prevChar))
                     // Ignore extra non-word char.
                     ; // Null stmt.
                else {
                     // Prev char word char.
                     // Means end of word.
                     // Store length in array.
                     if (WordLength < 14)
                           ++WordLengthCount[WordLength];
                     else
                           // Words 14 chars or longer.
                           ++WordLengthCount[14];
                     // Reset for new word.
                     WordLength = 0;
                }
           else {
                // Is a word char, therefore not end of word.
                // Increment counter for word length.
                ++WordLength;
           }

           // Set prev char to current.
           prevChar = c;

     } // end while

     // Display histogram.
     printf("\n\nHISTOGRAM - Word length - Bottom to Top\n\n");

     // Counters.
     int i, col;
     int maxrows = 0;

     // Find max rows.
     for (i = 1; i < 15; ++i) {
           if (WordLengthCount[i] > maxrows)
                maxrows = WordLengthCount[i];
     }

     // Bottom to top approach.
     // Loop through the 14 word length categories
     // and display the number as label.
     for (i = 1; i < 15; ++i) {
           // Label
           if (i < 10)
                printf("  %d", i);
           else
                printf(" %d", i);
     }
     printf("\n\n");

     // Loop row by row, count 1, 2, 3...max
     // One column for each length of word.
     for (i= 1; i <= maxrows; ++i) {
           for (col = 1; col < 15; ++col) {
                if (WordLengthCount[col] >= i)
                     printf("  *");
                else
                     printf("   ");
           }
           printf("\n");
     }

     printf("\n\n");

     // Top to bottom approach.

     // Header.
     printf("\n\nHISTOGRAM - Word length - Top to Bottom\n\n");

     // Loop row by row, count max to 1.
     // One column for each length of word.
     for (i= maxrows; i > 0; --i) {
           for (col = 1; col < 15; ++col) {
                if (WordLengthCount[col] >= i)
                     printf("  *");
                else
                     printf("   ");
           }
           printf("\n");
     }

     // Loop through the 14 word length categories
     // and display the number as label.
     for (i = 1; i < 15; ++i) {
           // Label
           if (i < 10)
                printf("  %d", i);
           else
                printf(" %d", i);
     }
     printf("\n\n");

     // Keep console window open.
     system("pause");

     // Return some value.
     return 0;

} // end main


Output.


To show the word parsing has been fixed.


 
I wrote the code starting from the top down because it was easier to see if it was working. I then reversed the print loop to display it in a more traditional way. You can compare each method.






Monday, 17 January 2011

The C Programming Language (K&R) 01x0F—Histogram of Words—Exercise 1-13


I’m well past chapter 4 in “The C Programming Language” by Brian Kernighan and Dennis Ritchie aka K&R. I making notes as I go and reread chapters and sections at least twice, but it’s a challenge to keep pace with this blog. I’m lagging behind. No surprise, but I will make it.


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.

This program will build off two programs I’ve already written. Entry 01x0C and 01x0E.

In order to count the length of words, you have to parse words. That will be done based on the code in 01x0C. The histogram will be based on 01x0E. In that one I grouped characters into broad categories. Word lengths can be grouped: 1 to 3; 4 to 6; 7 to 9; >10.

The Pseudocode.

While not EOF
get character from keyboard

if character is not whitespace, increment word length counter

if character is whitespace and previous character is not whitespace, end of word, add count to array otherwise ignore it

Display histogram

Somehow I will cut and paste code form the previous two projects to make this one.

*    *    *

A couple of issues.

First, I tried to initialize an array with this code:

     int WordLengthCount[15];

     // Initialize with zero.
     for (c = 0; c < 16; ++c)
           WordLengthCount[c] = 0;


When I ran the code, the program crashed. I hadn’t seen that behaviour before and wasn’t certain what was causing it. I added debug break points and found that the loop was causing the problem. But why? The index counter. It was trying to place a value in WordLengthCount[15] which is not in the array. It’s outside. Change the limit from 16 to 15 to make it work. I should have known better because I do know the indices of the array go from 0 to 14 for a count of 15.

Also, from the debugging environment, it was clear the array had garbage values. Assigning zero for each element was needed. I can’t find a short cut to initializing values. You can declare the array with a set of {0, 0, …, 0} but you have to get the count right and it’s east to be wrong with a long string of zeroes.

Second, instead of grouping the word length, I could use the word length as the index for the counter. It makes for easier coding. The only issue is to have the last category be for words with 14 or more characters and therefore must test the length before storing any values in the array otherwise it will probably crash.

Third, the code to display the histogram, uses a for loop inside a for loop. It’s easy to get the counters mixed up.

There is another way to write this program. Find each word and call a function to return its length and store that value. There’s usually more than one way to write code.


Sample Code.

I am using Visual C++ 2010 and created the sample code as a console application.

// Function prototypes
bool IsWhitespace(char);

// The standard library includes the system function.
#include <cstdlib>

// C++ standard I/O library.
#include <cstdio>

int main()
{
     // Character input.
     char c = 0;
     // Previous character.
     char prevChar = 0;
     // Word Length.
     int WordLength = 0;
     int WordLengthCount[15];

     // Initialize with zero.
     for (c = 0; c < 15; ++c)
           WordLengthCount[c] = 0;

     while ((c = getchar()) != EOF) {
           if (IsWhitespace(c))
                if (IsWhitespace(prevChar))
                     // Ignore extra whitespace.
                     ; // Null stmt.
                else {
                     // Prev char not whitespace.
                     // Means end of word.
                     // Store length in array.
                     if (WordLength < 14)
                           ++WordLengthCount[WordLength];
                     else
                           // Words 14 chars or longer.
                           ++WordLengthCount[14];
                     // Reset for new word.
                     WordLength = 0;
                }
           else {
                // Not whitespace, therefore not end of word.
                // Increment counter for word length.
                ++WordLength;
           }

           // Set prev char to current.
           prevChar = c;

     } // end while

     // Display histogram.
     printf("\n\nHISTOGRAM - Word length\n");

     // Counter.
     int i;

     // Loop through the 14 word length categories.
     for (c = 1; c < 15; ++c) {
           // Label
           printf("\n%3d  ", c);
           // No. of occurrences for this word length.
           for (i = 1; i <= WordLengthCount[c]; ++i)
                printf("*");
     }   
     printf("\n\n");

     // Keep console window open.
     system("pause");

     // Return some value.
     return 0;

} // end main

bool IsWhitespace(char t)
{
     // Return true for whitespace chars.

     // Space.
     if (t == 32)
     return true;

     // Newline.
     if (t == 10)
           return true;

     // Tab.
     if (t == 9)
           return true;

     // If we get here, no whitespace.
     return false;
} // IsWhitespace


Output.




The C Programming Language (K&R) 01x0E—Histogram of Chars—Exercise 1-14


I am reading “The C Programming Language” by Brian Kernighan and Dennis Ritchie aka K&R. Making notes. Running their sample code. Writing code, programs, based on their exercises. Blogging about it here. I wouldn’t normally do these exercises, but it is beneficial and it’s satisfying to see the code work.

Exercise 1-14.

Write a program to print a histogram of the frequencies of different characters in its input.

A histogram is a graph that shows the frequency or count of some variable over a range. In this exercise it’s the frequency of characters entered by the user at the keyboard. Since the authors say “different characters,” it’s ambiguous. If I wrote code to account of every possible character, the graph would be too big. So, I will create groups of characters: whitespace, lowercase alphabet, uppercase alphabet, digits, other. This means every character typed by the user will be counted and analyzed, by group, not individually.

The Pseudocode.

While not EOF
            get input from user through keyboard
            increment counter for character by grouping
                        1. lowercase alpha
                        2. whitespace
                        3. uppercase alpha
                        4. digit
                        5. other
Display histogram

We don’t need to keep the actual data, just counts of them.

In an ASCII environment the asterisk (*) is often used to graph the count. A one-to-one ratio seems as if it would be too big. Try five-to-one.

In testing the input to see what group it falls into, I will use a series of if-else if-else statements. This code tests until it finds a true condition. Once true is found, it bypasses all the remaining tests. This means to make the code more efficient, the most likely character input should be tested first (lowercase) following in order with the last being the least common.

Time to write the code.

*    *    *

No surprises in coding. The only question is the ratio to use to condense the count. 5 to 1 is good for a lot of data, but with a small sample I changed it to 2 to 1 and used a variable to hold this value.

I also used functions for the tests because I already had a IsWhitespace function and reused it. I find it easier to read using functions. It makes it clear what’s being tested.

In testing a character it is possible to use a literal number or a character in single quotes. For example, to test if c is a space you can write: c = 32 or c = ‘ ’. Either work and they both work with other logical operators like >= or <=.

In looking at the code, it would be possible to use an array for all counts and one for loop to display the histogram with a cascading if statement to display the category headings.


Sample Code.

I am using Visual C++ 2010 and created the sample code as a console application.

// Function prototypes
bool IsLowercase(char);
bool IsWhitespace(char);
bool IsUppercase(char);
bool IsDigit(char);

// The standard library includes the system function.
#include <cstdlib>

// C++ standard I/O library.
#include <cstdio>

int main()
{
     // Character input.
     char c;
     // Counters.
     int WhitespaceCount = 0;
     int DigitCount = 0;
     int LowercaseCount = 0;
     int UppercaseCount = 0;
     int OtherCount = 0;

     // Get user input and count.
     while ((c = getchar()) != EOF) {
           if (IsLowercase(c))
                ++LowercaseCount;
           else if (IsWhitespace(c))
                ++WhitespaceCount;
           else if (IsUppercase(c))
                ++UppercaseCount;
           else if (IsDigit(c))
                ++DigitCount;
           else
                ++OtherCount;
     }
     // Display histogram.
     printf("\n\nHISTOGRAM\n");

     // Ratio.
     // Compact count: n to 1.
     int ratio = 2;

     printf("\n Lowercase:\t");
     for (c = 1; c < LowercaseCount; c += ratio)
           printf("*");
     printf("\nWhitespace:\t");
     for (c = 1; c < WhitespaceCount; c += ratio)
           printf("*");
     printf("\n Uppercase:\t");
     for (c = 1; c < UppercaseCount; c += ratio)
           printf("*");
     printf("\n    Digits:\t");
     for (c = 1; c < DigitCount; c += ratio)
           printf("*");
     printf("\n     Other:\t");
     for (c = 1; c < OtherCount; c += ratio)
           printf("*");

     printf("\n\n");

     // Keep console window open.
     system("pause");

     // Return some value.
     return 0;

} // end main

bool IsLowercase(char t)
{
     // Return true for lowercase alpha chars.

     // a to z
     // ASCII 97 to 122
     if (t >= 97 && t <= 122)
           return true;

     // If we get here, not lowercase.
     return false;
} // IsLowercase

bool IsWhitespace(char t)
{
     // Return true for whitespace chars.

     // Space.
     if (t == 32)
     return true;

     // Newline.
     if (t == 10)
           return true;

     // Tab.
     if (t == 9)
           return true;

     // If we get here, no whitespace.
     return false;
} // IsWhitespace

bool IsUppercase(char t)
{
     // Return true for uppercase chars.

     // A to Z
     // ASCII 65 to 90
     if (t >= 65 && t <= 90)
     return true;

     // If we get here, not uppercase.
     return false;
} // IsUppercase

bool IsDigit(char t)
{
     // Return true for whitespace chars.

     // 0 to 9
     // ASCII 48 to 57
     if (t >= 48 && t <= 57)
           return true;

     // If we get here, no whitespace.
     return false;
} // IsDigit


Output.