1. The purpose of this question
was to check that you can write a complete working program in C or C++.
A number is considered “big” if it is greater than 100, “medium” if it is between 20 and 100, or “little” if it is less than 20. Write a program that reads a sequence of numbers typed by the user, for each number the program must print a simple report indicating whether it is big, medium, or little, and whether it is odd or even. The program should continue until the user enters a negative number.
Enter numbers, terminated by a negative...
? 37
37 is a medium odd number.
? 246
246 is a big even number.
? 7
7 is a little odd number.
? 14
14 is a little even number.
? –1
First I’ll make a general plan of the program, then fill in the details. After printing the initial explanation “Enter numbers…”, the program goes into a loop, repeatedly accepting a numeric input, and processing it in some way. Just having that much of a plan allows us to start writing the program:
#include <iostream>
void main(void)
{ cout << “Enter numbers, terminated by
a negative...\n”;
while ( some condition )
{
int num;
cout << “? “;
cin >> num;
process num in some way } }
This just leaves two parts to be worked out: “some condition” and “process num in some way”. I’ll start with the latter, but either would do.
To process the input value, we can see
there is a simple pattern to follow. First the number itself is printed,
followed by “is a”. Then we print “big”, “medium”, or “little” depending on the
size of the number, then we print “even” or “odd” depending on the parity of
the number, and finally we print “number.” And that’s it. Four simple phases.
The first is easier than falling off a
bicycle:
cout << num << “ is a“;
The second has three possible cases, each
of which can be chosen with a simple condition, so an “if … else …” construct
is ideal:
if (num<20)
cout
<< “ little”;
else if (num<=100)
cout
<< “ medium”;
else
cout
<< “ big”;
Be
very careful with the placement of semicolons, and remember that when you have
a number of mutually exclusive possibilities, it is almost always best
to use a sequence of if … else if … else if … else … statements, and there
isn’t much that can go wrong.
The third phase is just as simple, but
requires you to be able to tell whether a number is odd or even. Divide a
number by two and look at the remainder, 1 means odd and 0 means even. Remember
that in C, the % operator gives the remainder after a division, and you’ve got
it:
if (num%2==1)
cout
<< “ odd”;
else
cout
<< “ even”;
If you can’t think how to do something like that (checking for oddness or evenness) don’t panic, pretend there is a function that does the job, and use it:
if (isOdd(num))
cout
<< “ odd”;
else
cout
<< “ even”;
That way, at least you have a fundamentally correct program that shows you know how to do the important things. You can add a note saying that you are aware that there is no “isOdd” function in C. Later, if you work out how to do it, you can easily add a definition of such a function to your program without disrupting what you’ve already done.
The fourth phase is of course absolutely
trivial:
cout << “ number.\n”;
Now all we have to think about is how to
end the loop. Whenever num is –1, the loop should stop. We could make the loop
say “while (num!=-1)”, but that would require a lot of other modifications: the
variable “num” is only declared inside the loop, so we would have to move its
declaration, and make sure it has an initial value that is not –1. We would
also have to make sure that “process num in some way” (the stuff we just worked
out) does absolutely nothing when num is –1. That is a lot of changing.
It would be much easier to make the loop
naturally run for ever “while (1)”, and put a conditional break inside the
loop, just after reading the value of num:
if (num==-1) break;
A lot of programmers strongly prefer having a loop condition that does the job, and avoiding breaks. It is largely a matter of taste, but once you have a good working design it is a shame to have to change it all.
A nice clear simple program is likely to be a nice clear correct program.
After all that, the complete program would
be:
#include <iostream>
void main(void)
{ cout << “Enter numbers, terminated by
a negative...\n”;
while (1)
{
int num;
cout << “? “;
cin >> num;
if
(num==-1)
break;
cout << num << “ is a“;
if
(num<20)
cout << “ little”;
else if (num<=100)
cout << “ medium”;
else
cout << “ big”;
if
(num%2==1)
cout << “ odd”;
else
cout << “ even”;
cout << “ number.\n”; } }
There are an enormous number of other
correct answers that would be equally acceptable. You could have checked the
conditions differently, instead of a three-way test for size followed by a
two-way test for parity, you could have
had a six-way test that checks both at once. Usually it is best to program for
separate things separately, but that is the sort of thing you learn by
experience.
Here is how the program would look if you
avoided using the break statement:
#include <iostream>
void main(void)
{ cout << “Enter numbers, terminated by
a negative...\n”;
int
num=0;
while (num!=-1)
{
cout << “? “;
cin >> num;
if
(num!=-1)
{
cout << num << “ is a“;
if (num<20)
cout << “ little”;
else if (num<=100)
cout << “ medium”;
else
cout << “ big”;
if (num%2==1)
cout << “ odd”;
else
cout << “ even”;
cout << “ number.\n”; } } }
I would say that’s more complex, and therefore less desirable.
2. The purpose of this question
was to check that you can define and properly use functions.
Define a function that takes two integers, and returns the sum of (i.e. adds up) all the integers between those two numbers.
addup(1,4) should
produce 10 because 1+2+3+4=10
addup(5,10) should
produce 45 because 5+6+7+8+9+10=45
addup(7,5) should
produce 18 because 7+6+5=18
The question clearly calls for a function
that has two parameters, both ints, and returns a result that is also an int.
Perhaps just writing down a template for such a function would be a good start:
int addup(int a, int b)
{ work out the answer;
return the answer; }
so again, if you run out of time, at least you’ve shown that you know how to declare a function even if you can’t fill in the works.
Now, how do we add up all the numbers between a and b? A simple loop that runs a variable through each number between a and b is an obvious idea; each time round the loop, add that variable to the running total. We must make sure that the running total starts out at zero, and that should do the trick:
int total=0;
for (int i=a; i<=b; i+=1)
total+=i;
then “return the answer” becomes the single statement “return total”.
A few things to note: be careful with the
condition part of a for statement. It should be a condition for continuing
to execute the loop. Saying “i<b” would not execute the loop when i is equal
to b, the the last value would fail to be added. Also, “i+=1” and “total+=i”
are just C shorthand for “i=i+1” and “total=total+i”; you could just as well
use “i++” for the first one.
Before we feel too pleased with ourselves
at getting the answer so quickly, let’s make sure it is right. The third
example addup(7,5)=18 points out that we can not rely on the arguments being
“in order”, the first might be the biggest. What would happen to out loop if
a=7 and b=5?
Initially i is set to 7, then the test
i<=b is equivalent to 7<=5, which is false, so the loop terminates
immediately, and zero is returned incorrectly as the result.
There are a million ways to fix this
problem. The easiest is to say “if the arguments a and b are in the wrong
order, swap them round”. Fixing bugs isn’t always so easy, but it is worth
spending a little time looking for a really simple answer.
So, the whole function definition could be:
int addup(int a, int b)
{ int total=0;
if
(a>b)
{
int temp; temp=a; a=b; b=temp; }
for
(int i=a; i<=b; i+=1)
total+=i;
return total; }
And the little bit of program showing how to call your function could simply be this:
void main(void)
{ cout << “The sum of the numbers
between 1 and 10 is “;
cout
<< addup(1,10);
cout
<< endl; }
Things to make sure you understand:
When swapping the values of two variables
(a and b above) a temporary variable is required. I’m sure you we already aware
of that. Many programmers would have declared this variable temp at the same
time as total: “int total=0,
temp;”. However, it is best to make your temporary
variables as temporary as possible. When I include the declaration of temp
inside the curly brackets surrounding the exchange: { int temp; temp=a; a=b; b=temp; } that new
variable called temp only exists inside those curly brackets. In the rest of
the program it is as though the name had never been mentioned before. You can
reuse temp as a variable name or even as a function name without any risk of
interference or confusion.
The same
applies to the declaration of the loop variable “i” inside the for statement.
When you write something like this:
for
(int i=a; i<=b; i+=1)
total+=i;
The variable “i” only exists for the loop
itself, by the time you get to the next statement (the return), it is as though
“i” had never been mentioned, so there is no risk of you accidentally re-using
it (“i” is a very common variable name), or relying on it having some
particular value.
Making
variables as local as you possibly can is nearly always a good thing. The
general claim that global variables are bad is just a generalization of this
principle.
3. The purpose of this question
was to check that you can use arrays correctly.
Write a program that allows the user to enter a very large number of integers, all in the range 0 to 100. When all the numbers have been entered, the program should report how many times each different integer appeared in the input. It should only report numbers that appeared at least once. The user will indicate the end of the inputs by entering –1.
A lot of people, as I expected, really
over-complicated this one. That is not a terrible thing, you were only required
to show that you can use arrays correctly, and it gives me a nice opportunity
to point out the value of just stopping to think for a few minutes before
diving into an exam question.
You may well complain that you don’t have
enough time to think before starting, but as you’ll see, the solution you can
produce if you sit back and think for a while is so much simpler and smaller
than the solution most people came up with, that the total time for thinking
plus writing would be a lot less.
The thing to notice is that you don’t need
to store all the numbers that the user inputs in an array. You do need an array
to count the number of times each input appears, but the individual inputs do
not need to be kept. This is fortunate, because we have no idea how many
numbers the user will type. Even if you give your array 100000 elements, it is
possible that your program will be fed automatically generated numbers, and
there could potentially be millions of them.
So this is the plan. We have an array with
just 101 entries (numbered 0 to 100) in which we count how many times each
number has appeared. Let’s call the array “count”. So count[0] tells us how
many times we’ve seen the number 0, count[1] tells us how many times we’ve seen
the number 1, and so on, all the way up to count[100] telling us how many times
we’ve seen the number 100. Luckily the question told us that all the inputs
will be in the range 0 to 100, so we know how big to make the count array.
This means that all we have to do is set
all the entries in count to 0, then start reading the inputs. Every time we
read the number n we increment the value of count[n]. At the end we just print
out the entries in count that are not still zero.
Remember, in C (and of course C++) when you
declare an array you specify how many elements it has, and those elements are
numbered from zero up. If you declare an array like this “int A[4];”, the four
elements of A will be called A[0], A[1], A[2], and A[3]. There is no A[4]. The
number that appears in the array declaration can not be used as an array index.
This is counter-intuitive, and the cause of many errors.
Here is the whole program:
#include <iostream>
void main(void)
{ int count[101];
for
(int i=0; i<=100; i+=1)
count[i]=0;
while (1)
{ int
n;
cin >> n;
if
(n==-1)
break;
count[n]+=1; }
for
(int i=0; i<=100; i+=1)
if
(count[i]!=0)
cout << i << “ appeared “ << count[i] <<
“times\n”; }
There is one very minor fault, hardly worth mentioning, but I will. If a number x appeared exactly once in the input, count[x] will be 1, and the program will print “… 1 times”. When of course it should print “… 1 time”. There are many ways to fix this, just as there are many ways to write the whole program. I’ll show two of them. For each, I’ll just rewrite the last 3 lines.
1. Expand the “if” to check for ==1 and >1, instead of just !=0
(also using a temporary variable “times” to avoid saying count[i] so many times)
for
(int i=0; i<=100; i+=1)
{
int times=count[i];
if
(times==1)
cout << i << “ appeared “ << “1 time\n”;
else if (times>1)
cout << i << “ appeared “ << times << “times\n”;
} }
2. Use a conditional expression to choose whether “time” or “times” is printed
(also using a temporary variable “times” to avoid saying count[i] so many times)
for
(int i=0; i<=100; i+=1)
{
int times=count[i];
if
(times!=0)
cout << i
<< “ appeared “
<< times
<< ( times==1 ? “time\n” : “times\n”) ; } }