CSC161 2011S Imperative Problem Solving
[Skip to Body]
Primary:
[Front Door]
[Schedule]
[Piazzza]
-
[Academic Honesty]
[Instructions]
Current:
[Current Outline]
[Current EBoard]
-
[Current Assignment]
[Current Lab]
[Current Reading]
Groupings:
[Assignments]
[Documents]
[EBoards]
[Examples]
[Exams]
[Handouts]
[Labs]
[Outlines]
[Readings]
[Reference]
Related Courses:
[CSC195 2003S (Rebelsky)]
[CSC161 2009F (Coahran)]
[CSC161 2010S (Walker)]
[CSC161 2010F (Rebelsky)]
Misc:
[SamR]
[ISO]
[GNU Coding Standards]
These are my rough notes on examination 1, prepared before and while grading the examination.
I realize that I did not give the explanation of the academic honesty
statements. The primary reason I ask for the statements is to remind you
that I place high import on academic honesty. The second statement is
there to help address the moral dilemma of I saw my classmates
cheat. What do I do now?
You will be unable to sign the second
statement, but you need not give me the details.
I had more There's more to life
examinations than expected (and
many more than I would have liked). I do my best to make myself available,
so please come see me when you're having difficulty, rather than working
to the time limites. I expect everyone who invoked this clause to
see me soon, and then visit me again when the next exam is assigned.
In general, I'm happy to see you (or to answer email questions) when you're having trouble on the exam. At my discretion, I often provide the assistance in
I primarily work from the printed copies of your examinations. Hence, it's important that you present readable printed copies. In some cases, they were almost completely unreadable because of missing text or line wraps.
While the computer is one audience of your code, human beings are another. I expect you to choose intelligible variable names and to comment on the purposes of those variables. It is also useful to comment sections of code.
Code that is not formatted consistently is very difficult to read. Indentation shows block structure (what code is part of what loop). I very much care that you follow appropriate formatting standards to make it easier not only for me, but for others, to read your code.
In many cases, I was able to identify errors with your code by doing some straightforward testing. I expect you to test your code, and one test is usually insufficient.
Topics: Linux commands, Pipes
Suppose Professor L. stores information for his classes in the following form:
FirstName:LastName:Assignment:Grade:Notes
Write a Linux command (a pipe between various programs) that prints out the records for the five highest grades on HW1, sorted alphabetically by name (e.g., John Doe comes before Jane Smith but after Jen Doe).
You can assume that no two students have the same grade on the assignment.
First, we need to extract all the lines for HW1. We use
grep HW1 file to get those lines.
Next, we need them sorted from highest to lowest. The sort
command sorts.
-t: flag.
-k4 flag.
-n flag.
-r
flag.
Our Linux instruction so far is
$ grep HW1 file | sort -t: -k4 -n -r
We want only the five highest grades. Those are the first five lines of
the output. The head command works well for grabbing the
first five lines.
$ grep HW1 file | sort -t: -k4 -n -r | head -5
If we just wanted to sort by last name, we'd add the command to sort by last name.
$ grep HW1 file | sort -t: -k4 -n -r | head -5 | sort -t: -k2,2
Many of you stopped your answer there. But that shouldn't be enough, since it does not specify sorting by first name. The traditional ways to sort by two fields are either (a) add another key to the sort or (b) sort by the secondary key first.
So, either
$ grep HW1 file | sort -t: -k4 -n -r | head -5 | sort -t: -k2,2 -k1,1
or
$ grep HW1 file | sort -t: -k4 -n -r | head -5 | sort -t: -k1,1 | \ sort -t: -k2,2 -s
Times reported: 15, 20, 20, 30, 30, 35, 45, 60, 60, 90, 90, 120, 120, 150
The most prominent issue that I saw in this problem was a failure to test. Many of you saw that your instructions worked correctly for the sample input, and didn't go any further. (Because computers are sentient and malicious, the seemingly incomplete instructions seemed to work on all the inputs I gave them, so I ended up changing a lot of grades.)
Topics: Input processing, Numbers, Histograms, Loops
Write a C program, numbers, that extracts the numbers from
input and prints them out. For example, if the input was
The 411 on 3.1415 is that 11x11 is 121.
then the output should be
411 3.1415 11 11 121
Note: You should use getchar to read values, and not
scanf.
Times reported: 60, 60, 60, 70, 90, 120, 120, 120, 180, 180, 210, 225, 270, 450
Topics: Strings, Characters, Output, Arithmetic Operations
In our initial explorations of output, we built a simple coding program
by adding 1 to each character. Let's develop a more complex version of
that program. Instead of using a single number, we'll use a code
word
. To encode an input, you add characters from the code word.
E.g.,
This is the text to be encoded + WordWordWordWordWordWordWordWo --------------------------------
So, the first letter of output will be the result of adding 'T'
and 'W'. The second will be the result of adding 'h'
and 'o'. The third will be the result of adding 'i'
and 'r'. And so on and so forth.
Of course, this strategy is a bit harder in C, since we need to deal with the limitations of ASCII representation. In particular, only characters with values between 32 (space) and 126 (tilde) are printable. So, one has to play a bit with the computation using modulo and other operations.
a. Write a C program, encode, that encodes its input using
a hard-coded code word. (You can use the code word of your choice.)
b. Write a C program, decode, that decodes its input
using the same hard-coded code word.
/**
* encode codeword - Encode standard input using the given codeword.
*/
// +---------+--------------------------------------------------------
// | Headers |
// +---------+
#include <stdio.h> // For getchar and printf
#include <string.h> // For strlen
// +-----------+------------------------------------------------------
// | Constants |
// +-----------+
/**
* The minimum and maximum printable characters.
*/
#define MIN_CHAR 32
#define MAX_CHAR 127
/**
* The default codeword (in case one does not get specified on the
* command line.
*/
#define DEFAULT_CODEWORD "CSC161-01"
// +------+-----------------------------------------------------------
// | Main |
// +------+
int
main (int argc, char *argv[])
{
char *codeword; // The codeword we're using
int ch; // The input character
int k; // The character in the codeword
int e; // The encoded character
int i=0; // The index in the codeword
int len; // The length of the codeword
int printable; // The number of printable characters
// Set up information on the codeword
if (argc < 2)
codeword = DEFAULT_CODEWORD;
else
codeword = argv[1];
len = strlen (codeword);
// Determine the number of printable characters
printable = 1 + MAX_CHAR - MIN_CHAR;
// Process the input
while ((ch = getchar ()) != EOF)
{
// Echo non-printable characters verbatim
if ((ch < MIN_CHAR) || (ch > MAX_CHAR))
printf ("%c", ch);
else
{
// Get the next character in the code word.
k = codeword[i];
i = (i + 1) % len;
// printf ("ch=%d(%c), k=%d(%c), ", ch, ch, k, k); // TESTING
// Convert each to a reasonable range
ch -= MIN_CHAR;
k -= MIN_CHAR;
// Add them together
e = (ch + k) % printable;
// Return to a reasonable range
e += MIN_CHAR;
// And print it out
// printf ("e=%d(%c)\n", e, e); // TESTING
printf ("%c", e);
}
} // while
// And we're done
return 0;
} // main
/**
* decode codeword - Decode standard input using the given codeword.
*/
// +---------+--------------------------------------------------------
// | Headers |
// +---------+
#include <stdio.h> // For getchar and printf
#include <string.h> // For strlen
// +-----------+------------------------------------------------------
// | Constants |
// +-----------+
/**
* The minimum and maximum printable characters.
*/
#define MIN_CHAR 32
#define MAX_CHAR 127
/**
* The default codeword (in case one does not get specified on the
* command line.
*/
#define DEFAULT_CODEWORD "CSC161-01"
// +------+-----------------------------------------------------------
// | Main |
// +------+
int
main (int argc, char *argv[])
{
char *codeword; // The codeword we're using
int e; // The encoded character
int k; // The character in the codeword
int ch; // The output character
int i=0; // The index in the codeword
int len; // The length of the codeword
int printable; // The number of printable characters
// Set up information on the codeword
if (argc < 2)
codeword = DEFAULT_CODEWORD;
else
codeword = argv[1];
len = strlen (codeword);
// Determine the number of printable characters
printable = 1 + MAX_CHAR - MIN_CHAR;
// Process the input
while ((e = getchar ()) != EOF)
{
// Echo non-printable characters verbatim
if ((e < MIN_CHAR) || (e > MAX_CHAR))
printf ("%c", e);
else
{
// Get the next character in the code word.
k = codeword[i];
i = (i + 1) % len;
// printf ("e=%d(%c), k=%d(%c), ", e, e, k, k); // TESTING
// Convert each to a reasonable range
e -= MIN_CHAR;
k -= MIN_CHAR;
// Undo the addition from encoding
ch = e - k;
if (ch < 0)
ch += printable;
// Convert back to a reasonable range
ch += MIN_CHAR;
// And print it out
// printf ("ch=%d(%c)\n", ch, ch); // TESTING
printf ("%c", ch);
}
} // while
// And we're done
return 0;
} // main
Times reported: 0, 90, 105, 120, 150, 180, 180, 240, 240, 240, 300, 300, 480, 480
This problem ended up being the hardest one on the exam. It appears
a lot of small errors easily creep in to solutions, and are hard to
debug. It's also clear that working out the math
of the problem
was hard for many of you.
One big off by 1
problem happened with the modulus. Many of you
used a modulus of 94. However, that modulus ensures that ' '
(32) and '~' (126) map to the same value, making decoding
impossible.
There were a number of constants
in the program: The smallest
valid character, the largest valid character, the number of valid
characters, the length of your string, etc. Rather than writing them
throughout the code, it's much clearer if you declare mnemonic constants.
Some of you decided to shove the input into an array. That's a bad idea, since it limits the amount of text you can process. It is much better to just do a character at a time. (More clever encryption algorithms might do a block of characters at a time, but still allow arbitrarily long input.)
Many of you needed to explore a larger set of input characters. One could generate those input characters with a program, or one could simply type the full set of printable characters, as in
`1234567890-=
qwertyuiop[]\
asdfghjkl;'
zxcvbnm,./
~!@#$%^&*()_+
QWERTYUIOP{a|}
ASDFGHJKL:"
ZXCVBNM<>?
[ ]
>
Topics: Output, Loops, Error Checking
Write a concise C program, diamond, that reads in an integer
and prints out a diamond made out of asterisks, such as the following.
* *** ***** ******* ***** *** *
The input integer represents the size of the edges. For example, the diamond given above has sides of size 4.
Your program should generate an appropriate error message if the user does not enter a valid side length.
We start by analyzing the problem to figure out how many spaces and stars are printed in the rows on the top half of the diamond.
We'll do a diamond with side length 5.
ROW SPACES STARS | 0 4 1 | * 1 3 3 | *** 2 2 5 | ***** 3 1 7 | ******* 4 0 9 |*********
Is there a pattern? We see the number of spaces starting at
side-1, decreasing by 1 each time, and ending at 0.
We see the number of stars (asterisks) starting at 1 and increasing
by 2 each time. We can use that strategy. We can also work out a
formula (something I'm more inclined to do).
For the bottom half, we just count down.
Putting it all together, we get
/**
* diamond - Make and print a simple diamond from asterisks.
*/
// +---------+--------------------------------------------------------
// | Headers |
// +---------+
#include <stdio.h>
#include <stdlib.h>
// +------+-----------------------------------------------------------
// | Main |
// +------+
int
main (int argc, char *argv[])
{
int side; // The length of the side
int row; // A count of the rows we're printing
int i; // A counter variable
// Verify that there are enough arguments
if (argc != 2)
{
printf ("Usage: diamond SIDE\n");
return 1;
}
// Determine the side length
side = atoi (argv[1]);
// Validate the side legth
if (side <= 0)
{
printf ("Invalid side length: %s\n", argv[1]);
return 2;
}
// Print the top half
for (row = 0; row < side; row++)
{
// Print the spaces in the row
for (i = 0; i < side-1-row; i++)
putchar (' ');
// Print the asterisks
for (i = 0; i < 2*row+1; i++)
putchar ('*');
// That's it for the line
printf ("\n");
} // for lines in top half
// Print the bottom half
for (row = side-2; row >= 0; row--)
{
// Print the spaces in the row
for (i = 0; i < side-1-row; i++)
putchar (' ');
// Print the asterisks
for (i = 0; i < 2*row+1; i++)
putchar ('*');
// That's it for the line
printf ("\n");
} // for lines in bottom half
// And we're done
return 0;
} // main
But that's not an ideal solution. In particular, the code is very
repetitious. We should first factor out the bodies of the for loops.
Then we should factor out the print N copies of a character
code.
There may even be more to do.
Times reported: 30, 30, 60, 60, 60, 90, 120, 120, 120, 150, 150, 180, 180, 180
I saw some very creative solutions that involved drawing an appropriate-sized grid and figuring out for each space on the grid whether it was a space or an asterisk.
Wednesday, 9 February 2011 [Samuel A. Rebelsky]
Saturday, 19 February 2011 [Samuel A. Rebelsky]
Sunday, 20 February 2011 [Samuel A. Rebelsky]
Monday, 21 February 2011 [Samuel A. Rebelsky]
http://www.cs.grinnell.edu/~rebelsky/Courses/CSC161/2011S/Exams/exam.01.html.
[Skip to Body]
Primary:
[Front Door]
[Schedule]
[Piazzza]
-
[Academic Honesty]
[Instructions]
Current:
[Current Outline]
[Current EBoard]
-
[Current Assignment]
[Current Lab]
[Current Reading]
Groupings:
[Assignments]
[Documents]
[EBoards]
[Examples]
[Exams]
[Handouts]
[Labs]
[Outlines]
[Readings]
[Reference]
Related Courses:
[CSC195 2003S (Rebelsky)]
[CSC161 2009F (Coahran)]
[CSC161 2010S (Walker)]
[CSC161 2010F (Rebelsky)]
Misc:
[SamR]
[ISO]
[GNU Coding Standards]
Disclaimer:
I usually create these pages on the fly
, which means that I rarely
proofread them and they may contain bad grammar and incorrect details.
It also means that I tend to update them regularly (see the history for
more details). Feel free to contact me with any suggestions for changes.
This document was generated by
Siteweaver on Mon Feb 21 10:52:33 2011.
The source to the document was last modified on Mon Feb 21 10:52:30 2011.
This document may be found at http://www.cs.grinnell.edu/~rebelsky/Courses/CSC161/2011S/Exams/notes.01.html.