Reputation: 617
Originally I used switch/case, but the condition had to be a constant value that the variable would match, versus a boolean of whether the variable was within the range.
Instead, I have this monstrosity:
if ( data[y] > 91 ) {
grades[9] = grades[9] + 1;
}
else if ( data[y] > 88 && data[y] < 92 ) {
grades[8] = grades[8] + 1;
}
else if ( data[y] > 84 && data[y] < 89 ) {
grades[7] = grades[7] + 1;
}
else if ( data[y] > 81 && data[y] < 85) {
grades[6] = grades[6] + 1;
}
else if ( data[y] > 79 && data[y] < 82) {
grades[5] = grades[5] + 1;
}
else if ( data[y] > 74 && data[y] < 79 ) {
grades[4] = grades[4] + 1;
}
else if ( data[y] > 71 && data[y] < 75 ) {
grades[3] = grades[3] + 1;
}
else if ( data[y] > 68 && data[y] < 72 ) {
grades[2] = grades[2] + 1;
}
else if ( data[y] > 59 && data[y] < 69 ) {
grades[1] = grades[1] + 1;
else {
//data[y] < 60:
grades[0] = grades[0] + 1;
}
Does anybody know a nicer way to handle this block of code, since my switch/case idea can't apply? Surely there has to be a better way to do this.
Upvotes: 7
Views: 9450
Reputation: 753455
For a radical variation on the theme, assuming marks can only be in the range 0..100, consider creating an array which contains the grade number for the score:
enum { MAX_SCORE = 100 };
static const int upper_bound[] = { 60, 69, 72, 75, 80, 82, 85, 89, 92, MAX_SCORE+1 };
enum { N_BOUNDS = sizeof(upper_bound) / sizeof(upper_bound[0]) };
int grade_for_score[MAX_SCORE + 1];
int score = 0;
for (int i = 0; i < N_BOUNDS; i++)
{
while (score < upper_bound[i])
grade_for_score[score++] = i;
}
The compensation for the setup work is that processing a score is trivial:
assert(data[y] >= 0 && data[y] <= MAX_SCORE);
grades[grade_for_score[data[y]]++;
This might be a benefit because it doesn't require any conditionals to process the grades as they come in, beyond the basic validation that the score is in the range [0..MAX_SCORE]. It does use some data space, of course. Of course, if the data is being read from a file (or a database), the I/O interaction time completely swamps the grading time, and the optimization is essentially pointless.
Working code:
#include <assert.h>
#include <stdio.h>
int main(void)
{
/* Setup */
enum { MAX_SCORE = 100 };
static const int upper_bound[] = { 60, 69, 72, 75, 80, 82, 85, 89, 92, MAX_SCORE+1 };
enum { N_BOUNDS = sizeof(upper_bound) / sizeof(upper_bound[0]) };
int grade_for_score[MAX_SCORE + 1];
int score = 0;
for (int i = 0; i < N_BOUNDS; i++)
{
while (score < upper_bound[i])
grade_for_score[score++] = i;
}
/* Scoring - test each score from [0..MAX_SCORE] */
int grades[N_BOUNDS] = { 0, };
int data[1];
int y = 0;
for (int i = 0; i <= MAX_SCORE; i++)
{
data[y] = i;
assert(data[y] >= 0 && data[y] <= MAX_SCORE);
grades[grade_for_score[data[y]]]++;
}
/* Print grades */
int sum = 0;
for (int i = 0; i < N_BOUNDS; i++)
{
sum += grades[i];
printf("Grade %d: %3d (cumulative %3d)\n", i, grades[i], sum);
}
return 0;
}
Note that it would be possible to initialize the grade_for_score
table at compile time instead of doing it at run time:
/* Manually generated; not formally checked for correctness */
int grade_for_score[] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 2, 3, 3, 3, 4, 4, 4, 4, 4,
5, 5, 6, 6, 6, 7, 7, 7, 7, 8,
8, 8, 9, 9, 9, 9, 9, 9, 9, 9,
9
};
However, the table is easily computed from the table of upper bounds, and computers are better at getting the counting right than humans, so it is better to create the table at runtime.
Example run:
Grade 0: 60 (cumulative 60)
Grade 1: 9 (cumulative 69)
Grade 2: 3 (cumulative 72)
Grade 3: 3 (cumulative 75)
Grade 4: 5 (cumulative 80)
Grade 5: 2 (cumulative 82)
Grade 6: 3 (cumulative 85)
Grade 7: 4 (cumulative 89)
Grade 8: 3 (cumulative 92)
Grade 9: 9 (cumulative 101)
Upvotes: 0
Reputation: 3301
int const datum = data[y];
int const index = (datum > 59) + (datum > 68) + (datum > 71)
+ (datum > 74) + (datum > 79) + (datum > 81)
+ (datum > 84) + (datum > 88) + (datum > 91);
++ grades[index];
Just calculate the index.
Upvotes: 0
Reputation: 10708
Use a loop. You'll also need an array of the minimum data value for each grade. Here's some untested code as an example:
for (int i = 9; i >= 0; i--)
{
if (data[y] >= minDataValueForGrade[i])
{
grades[i]++;
break;
}
}
It's short, easy to read, and makes it really easy to change the values that correspond to each grade.
Upvotes: 5
Reputation: 145829
If you want to avoid the long serie of if .. else
what about:
int arr[] = {60, 69, 72, 75, 79, 82, 85, 89, 92};
int i = 0;
while (i < sizeof arr/ sizeof *arr && data[y] < arr[i] && i++);
grades[i]++;
Upvotes: 2
Reputation: 476930
You might use a simple lookup table:
const unsigned int index[] = { 1, 1, /* ... */, 2, 2, /* ... */ };
++grades[data[y] < 60 ? 0 : index[data[y]]];
Upvotes: 2
Reputation: 239652
This seems like a sensible data-driven approach:
int grade_cutoff[] = { 59, 68, 71, 74, 79, 81, 84, 88, 91, INT_MAX };
int grade_bucket;
for (grade_bucket = 0; data[y] > grade_cutoff[grade_bucket]; grade_bucket++) {
/* nothing */
}
grades[grade_bucket]++;
Upvotes: 6
Reputation: 25419
My C
is rusty, to be truthful, however, you might consider looking at the problem from a different perspective than a conditional.
You should (I think) be able to restructure your data so that you could implement the decision logic without (or with much less) conditionals. I'm thinking something like a 2 dimensional array or a lookup table. Something along those lines....
Upvotes: 0
Reputation: 15501
The most obvious to shorten your code is to get rid of the unnecessary second tests:
if (data[y] >= 92) ++grades[9];
else if (data[y] >= 89) ++grades[8];
else if (data[y] >= 85) ++grades[7];
else if (data[y] >= 82) ++grades[6];
else if (data[y] >= 80) ++grades[5];
else if (data[y] >= 75) ++grades[4];
else if (data[y] >= 72) ++grades[3];
else if (data[y] >= 69) ++grades[2];
else if (data[y] >= 60) ++grades[1];
else ++grades[0];
Upvotes: 15