Switch statement or array

N

Nachy

Good (evening, morning, afternoon)

I have the following construct in my program. Some may remember this
from the first time I posted on C.L.C. a coupla years ago.
Effectively, it's a calendar program. This function takes as arguments
the day of the week of the beginning of the year - in my case, the
upcoming Jewish New Year - and has to add a certain number of days to
get the day of the week on which the first day of any other month
falls. (Sorry, I know that's a bit garbled.)

I have two ways of doing it.

1) A switch statement:
int new_month(int month, int new_year, int leap)
{
switch (month) {
case DECEMBER: new_year += 2; /* Fall through all */
case NOVEMBER: new_year += 3;
case OCTOBER: new_year += 2;
case SEPTEMBER: new_year += 3;
case AUGUST: new_year += 3;
case JULY: new_year += 2;
case JUNE: new_year += 3;
case MAY: new_year += 2;
case APRIL: new_year += 3;
case MARCH: new_year += 0;
case FEBRUARY: new_year += 3;
case JANUARY : new_year += 0;
}
if (month > FEBRUARY && leap == true) new_year++;
return new_year % 7;
}

2) An array:
int new_month(int month, int new_year, int leap)
{
int mo_array[12] = {0, 3, 3, 6, 8, 11, 13, 16, 19, 21, 24, 26} ;
new_year += mo_array[month]);
if (month > FEBRUARY && leap == true) new_year++;
return new_year % 7;
}

The first example may cause larger code (or will it?) but doesn't have
the same kind of magic numbers the second example has. Also, the
second example has local data - the array is automatic, and must be
copied from data each time the function is run, I think, which may be
slower. On the other hand, a switch statement can be slow if it
compiles as an if/else construct, or can a call table be used?

My question, in effect, is: which would you advise? Especially taking
into account (those that do understand) that for a Jewish calendar the
leap year and months work somewhat differently - but that is beyond
the scope of the question here.

For those seeking to pick nits: imaging an enum months {JANUARY, ...
DECEMBER} in scope.

Thanks, and I hope I wasn't too unclear.

-- Nachy

"Conspiracy Creationist Insists Everything Part of God's Plot" -- The
Onion
 
I

Ike Naar

int new_month(int month, int new_year, int leap)
{
int mo_array[12] = {0, 3, 3, 6, 8, 11, 13, 16, 19, 21, 24, 26} ;
new_year += mo_array[month]);
if (month > FEBRUARY && leap == true) new_year++;
return new_year % 7;
}
[...] Also, the
second example has local data - the array is automatic, and must be
copied from data each time the function is run, I think, which may be
slower. On the other hand, a switch statement can be slow if it
compiles as an if/else construct, or can a call table be used?

Declare the array ``static'' and it will be initialized once at program startup.
And since the data is readonly, make it ``const'' as well:

static int const mo_array[12] = {0, 3, 3, 6, 8, 11, 13, 16, 19, 21, 24, 26};
 
P

Paul N

Good (evening, morning, afternoon)

I have the following construct in my program. Some may remember this
from the first time I posted on C.L.C. a coupla years ago.
Effectively, it's a calendar program. This function takes as arguments
the day of the week of the beginning of the year - in my case, the
upcoming Jewish New Year - and has to add a certain number of days to
get the day of the week on which the first day of any other month
falls. (Sorry, I know that's a bit garbled.)

I have two ways of doing it.

1) A switch statement:
int new_month(int month, int new_year, int leap)
{
  switch (month) {
    case DECEMBER: new_year += 2;    /* Fall through all */
    case NOVEMBER: new_year += 3;
    case OCTOBER: new_year += 2;
    case SEPTEMBER: new_year += 3;
    case AUGUST: new_year += 3;
    case JULY: new_year += 2;
    case JUNE: new_year += 3;
    case MAY: new_year += 2;
    case APRIL: new_year += 3;
    case MARCH: new_year += 0;
    case FEBRUARY: new_year += 3;
    case JANUARY : new_year += 0;
  }
  if (month > FEBRUARY && leap == true) new_year++;
  return new_year % 7;

}

2) An array:
int new_month(int month, int new_year, int leap)
{
  int mo_array[12] = {0, 3, 3, 6, 8, 11, 13, 16, 19, 21, 24, 26} ;
  new_year += mo_array[month]);
  if (month > FEBRUARY && leap == true) new_year++;
  return new_year % 7;

}

The first way looks much clearer, and this consideration seems to
outweigh any possible downsides.
 
K

Keith Thompson

Paul N said:
Good (evening, morning, afternoon)

I have the following construct in my program. Some may remember this
from the first time I posted on C.L.C. a coupla years ago.
Effectively, it's a calendar program. This function takes as arguments
the day of the week of the beginning of the year - in my case, the
upcoming Jewish New Year - and has to add a certain number of days to
get the day of the week on which the first day of any other month
falls. (Sorry, I know that's a bit garbled.)

I have two ways of doing it.

1) A switch statement:
int new_month(int month, int new_year, int leap)
{
  switch (month) {
    case DECEMBER: new_year += 2;    /* Fall through all */
    case NOVEMBER: new_year += 3;
    case OCTOBER: new_year += 2;
    case SEPTEMBER: new_year += 3;
    case AUGUST: new_year += 3;
    case JULY: new_year += 2;
    case JUNE: new_year += 3;
    case MAY: new_year += 2;
    case APRIL: new_year += 3;
    case MARCH: new_year += 0;
    case FEBRUARY: new_year += 3;
    case JANUARY : new_year += 0;
  }
  if (month > FEBRUARY && leap == true) new_year++;
  return new_year % 7;

}

2) An array:
int new_month(int month, int new_year, int leap)
{
  int mo_array[12] = {0, 3, 3, 6, 8, 11, 13, 16, 19, 21, 24, 26} ;
  new_year += mo_array[month]);
  if (month > FEBRUARY && leap == true) new_year++;
  return new_year % 7;

}

The first way looks much clearer, and this consideration seems to
outweigh any possible downsides.

Really? I'd say the second looks clearer. The first uses
fall-through in a rather unusual manner, requiring just a little
extra thought to understand what's going on. (That's not a fatal
flaw.) In both cases, the numbers being used aren't obvious,
so I'd say that's a wash. The first requires repeated additions;
the performance hit isn't likely to be significant, but IMHO the
mental hit is.

As others have suggested, make mo_array "static const".

For greater clarity, make each element of mo_array something
meaningful, the number of days from the first of January to the
first of $MONTH:

static const int mo_array[12]
= { 0, 31, 59, 89, 119, 150, 180, 211, 242, 272, 303, 334 };

You're modding the result by 7 anyway.

Finally, I'd definitely change "leap == true" to just "leap":

if (month > FEBRUARY && leap) new_year++;

and possibly change the name from "leap" to "is_leap". Comparing
boolean values for equality to false or true is unnecessary and
error-prone. Any non-zero value is considered true; if leap
== 42, the test will fail. And if "leap == true" is better
than "leap", surely "(leap == true) == true" is better still.
I think the section on Boolean expressions in the comp.lang.c FAQ,
<http://www.c-faq.org>, covers this, but the site is currently down.
 
G

Gene

Paul N said:
Good (evening, morning, afternoon)
I have the following construct in my program. Some may remember this
from the first time I posted on C.L.C. a coupla years ago.
Effectively, it's a calendar program. This function takes as arguments
the day of the week of the beginning of the year - in my case, the
upcoming Jewish New Year - and has to add a certain number of days to
get the day of the week on which the first day of any other month
falls. (Sorry, I know that's a bit garbled.)
I have two ways of doing it.
1) A switch statement:
int new_month(int month, int new_year, int leap)
{
  switch (month) {
    case DECEMBER: new_year += 2;    /* Fall through all */
    case NOVEMBER: new_year += 3;
    case OCTOBER: new_year += 2;
    case SEPTEMBER: new_year += 3;
    case AUGUST: new_year += 3;
    case JULY: new_year += 2;
    case JUNE: new_year += 3;
    case MAY: new_year += 2;
    case APRIL: new_year += 3;
    case MARCH: new_year += 0;
    case FEBRUARY: new_year += 3;
    case JANUARY : new_year += 0;
  }
  if (month > FEBRUARY && leap == true) new_year++;
  return new_year % 7;
}
2) An array:
int new_month(int month, int new_year, int leap)
{
  int mo_array[12] = {0, 3, 3, 6, 8, 11, 13, 16, 19, 21, 24, 26} ;
  new_year += mo_array[month]);
  if (month > FEBRUARY && leap == true) new_year++;
  return new_year % 7;
}
The first way looks much clearer, and this consideration seems to
outweigh any possible downsides.

Really?  I'd say the second looks clearer.  The first uses
fall-through in a rather unusual manner, requiring just a little
extra thought to understand what's going on.  (That's not a fatal
flaw.)  In both cases, the numbers being used aren't obvious,
so I'd say that's a wash.  The first requires repeated additions;
the performance hit isn't likely to be significant, but IMHO the
mental hit is.

As others have suggested, make mo_array "static const".

For greater clarity, make each element of mo_array something
meaningful, the number of days from the first of January to the
first of $MONTH:

    static const int mo_array[12]
        = { 0, 31, 59, 89, 119, 150, 180, 211, 242, 272, 303, 334 };

I believe you can even make the compiler do the little remaining head
work. Probably worth doing as here shouldn't the 4th element be 90?

enum month_starts_e {
Jan = 0,
Feb = Jan + 31,
Mar = Feb + 28,
Apr = Mar + 31
....
} MONTH_STAR;

static const int mo_array[12]
= { Jan, Feb, Mar, Apr, ... };
 
K

Keith Thompson

Gene said:
For greater clarity, make each element of mo_array something
meaningful, the number of days from the first of January to the
first of $MONTH:

    static const int mo_array[12]
        = { 0, 31, 59, 89, 119, 150, 180, 211, 242, 272, 303, 334 };

I believe you can even make the compiler do the little remaining head
work. Probably worth doing as here shouldn't the 4th element be 90?

No, it's 59 (31 days for January plus 28 days for February; leap years
are accounted for later).
enum month_starts_e {
Jan = 0,
Feb = Jan + 31,
Mar = Feb + 28,
Apr = Mar + 31
...
} MONTH_STAR;

static const int mo_array[12]
= { Jan, Feb, Mar, Apr, ... };

Clever. Determining whether or not I mean that in a good way is
left as an exercise for the reader. :cool:}

You could also change [12] to [] if you were so inclined, but it's
a nice error check.
 
B

BartC

Keith Thompson said:
Gene said:
For greater clarity, make each element of mo_array something
meaningful, the number of days from the first of January to the
first of $MONTH:

static const int mo_array[12]
= { 0, 31, 59, 89, 119, 150, 180, 211, 242, 272, 303, 334 };

I believe you can even make the compiler do the little remaining head
work. Probably worth doing as here shouldn't the 4th element be 90?

No, it's 59 (31 days for January plus 28 days for February; leap years
are accounted for later).

He might have been talking about the 89 (59 + 31 for March). That 334 might
be suspect too.
 
K

Keith Thompson

BartC said:
Keith Thompson said:
Gene said:
For greater clarity, make each element of mo_array something
meaningful, the number of days from the first of January to the
first of $MONTH:

static const int mo_array[12]
= { 0, 31, 59, 89, 119, 150, 180, 211, 242, 272, 303, 334 };


I believe you can even make the compiler do the little remaining head
work. Probably worth doing as here shouldn't the 4th element be 90?

No, it's 59 (31 days for January plus 28 days for February; leap years
are accounted for later).

He might have been talking about the 89 (59 + 31 for March). That 334 might
be suspect too.

Right, "4th"; I should pay more attention. Yes, the 89 should be 90.
(I think I know what caused the problem; the rather ugly method I used
to generate the values is probably susceptible to daylight saving time
bugs.)
 
S

Shao Miller

Nachy said:
Good (evening, morning, afternoon)

I have the following construct in my program. Some may remember this
from the first time I posted on C.L.C. a coupla years ago.
Effectively, it's a calendar program. This function takes as arguments
the day of the week of the beginning of the year - in my case, the
upcoming Jewish New Year - and has to add a certain number of days to
get the day of the week on which the first day of any other month
falls. (Sorry, I know that's a bit garbled.)

... ... ...
Good [mor|noo|eve]ning to you, as well. That's right. Nooning.

For sheer fun (?!), here's something using x-macros. Its wild
complexity might come in handy if the calendar used is ever reworked...

Also note that months were arbitrarily chosen to be counted from 1
instead of 0; no reason at all.

#ifdef X
#error X already #defined.
#endif

#ifdef MONTHS
#error MONTHS already #defined.
#endif
/* For x-macro productions */
#define MONTHS \
X(january, 31) \
X(february, 28) \
X(march, 31) \
X(april, 30) \
X(may, 31) \
X(june, 30) \
X(july, 31) \
X(august, 31) \
X(september, 30) \
X(october, 31) \
X(november, 30) \
X(december, 31)

/* The order of the months */
#ifdef ENUM_MONTH
#error ENUM_MONTH already #defined.
#endif
#define ENUM_MONTH(month) month_ ## month
#define X(month, days) ENUM_MONTH(month),
enum month {
month_none,
MONTHS
month_last_po,
month_total = month_last_po - 1
};
#undef X
#undef ENUM_MONTH

/* Days in each month */
#ifdef ENUM_DAYS_IN
#error ENUM_DAYS_IN already #defined.
#endif
#define ENUM_DAYS_IN(month) days_in_ ## month
#define X(month, days) ENUM_DAYS_IN(month) = days,
enum days_in {
MONTHS
days_in_dummy = 0
};
#undef X
/* ENUM_DAYS_IN() used below */

/* Days before the start of each month */
#ifdef ENUM_DAYS_BEFORE_PO
#error ENUM_DAYS_BEFORE_PO already #defined.
#endif
#define ENUM_DAYS_BEFORE_PO(month) days_before_ ## month ## _po
#ifdef ENUM_DAYS_BEFORE
#error ENUM_DAYS_BEFORE already #defined.
#endif
#define ENUM_DAYS_BEFORE(month) days_before_ ## month
#ifdef ENUM_DAYS_ENDOF
#error ENUM_DAYS_ENDOF already #defined.
#endif
#define ENUM_DAYS_ENDOF(month) days_endof_ ## month
#define X(month, days) \
ENUM_DAYS_BEFORE_PO(month), \
ENUM_DAYS_BEFORE(month) = ENUM_DAYS_BEFORE_PO(month) - 1, \
ENUM_DAYS_ENDOF(month) = ENUM_DAYS_BEFORE(month) + ENUM_DAYS_IN(month),
enum days_before {
days_before_dummy,
MONTHS
ENUM_DAYS_ENDOF(year_po),
ENUM_DAYS_ENDOF(year) = ENUM_DAYS_ENDOF(year_po) - 1
};
#undef X
#undef ENUM_DAYS_ENDOF
/* ENUM_DAYS_BEFORE used below */
#undef ENUM_DAYS_BEFORE_PO
#undef ENUM_DAYS_IN
#define X(month, days) ENUM_DAYS_BEFORE(month),
static const enum days_before days_before_a[month_total + 2] = {
0, /* No days in month 0. We count from 1. */
MONTHS
0 /* No days in months after the last month. */
};
#undef X
#undef ENUM_DAYS_BEFORE
#undef MONTHS

static int new_month(int month, int new_year, int leap) {
if (month < 1 || month > month_total)
return -1;
return
(new_year + days_before_a[month] + (month > month_february && leap))
% 7;
}

/* Handy for arrays */
#define NUM_OF_ELEMENTS(array_) \
(sizeof (array_) / sizeof *(array_))
#define FOR_EACH_ELEMENT(index_, array_) \
for ((index_) = 0; (index_) < NUM_OF_ELEMENTS(array_); (index_)++)

#include <stdio.h>

int main(void) {
int i;

FOR_EACH_ELEMENT(i, days_before_a)
printf("Days before month #%d: %d\n", i, days_before_a);
printf("Days in a year: %d\n", days_endof_year);
printf("January 1st, 2010 was a Friday\n "
"new_month(month_october, 5, 0) == %d\n",
new_month(month_october, 5, 0));
printf("January 1st, 2009 was a Thursday\n "
"new_month(month_june, 4, 0) == %d\n",
new_month(month_june, 4, 0));
return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top