104

I have a few loops that I need in my program. I can write out the pseudo code, but I'm not entirely sure how to write them logically.

I need -

if (num is a multiple of 10) { do this }

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90

This is for a snakes and ladders board game, if it makes any more sense for my question.

I imagine the first if statement I'll need to use modulus. Would if (num == 100%10) be correct?

The second one I have no idea. I can write it out like if (num > 10 && num is < 21 || etc.), but there has to be something smarter than that.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3419168
  • 1,135
  • 2
  • 8
  • 16
  • 18
    Generally, the length of good code is proportional to the length of the english describing what it does. So when your "specification" says 11-20, 31-40, 51-60, 71-80, 91-100, you can expect your code to mention those numbers also. If those numbers come from somewhere or were generated by some reason, see if you can code the reason rather than the numbers. – luqui Apr 27 '14 at 23:10
  • 41
    @user3419168: The compiler cares not a bit how readable your code is; it will compile it in a fraction of a second. But for the humans reading your code, the choices you make can cause it to be comprehended in seconds, minutes, hours, or never. This imposes a cost; people get paid to read and understand code, so make it easy on them. Always write production code to maximize readability, and remember that concision does not necessarily make code perform better. – Eric Lippert Apr 28 '14 at 03:15
  • The titles on your other two questions are really good titles; they describe precisely what the question is about technically. I've edited this one because it was vague, and more about you than the coding problem. – jscs Apr 28 '14 at 19:13
  • I can't think why, in a standard game of snakes and ladders, it would ever be necessary to discover this information. Could you elaborate on how this code is likely to be used? I only ask because it doesn't strike me as a very object-oriented design and there might be a better way. – eggyal Apr 29 '14 at 14:39
  • 1
    @EricLippert your point is extremely valid. I too see people writing "clever" code, oblivious to the fact that the only person who might care about their cleverness (the next person reading the code) will hate them for it, and with no visible performance benefit. Makes you weep. – Floris Apr 29 '14 at 22:05
  • 1
    @Floris In those situations I sometimes leave the original, un-optimized code in, but commented out. I think it helps the reader figure out what's going on. – Chris Middleton Apr 29 '14 at 22:10
  • 2
    @AmadeusDrZaius - I have VERY RARELY done the same, but only for performance critical sections. The tightest loop that is called 100M times qualifies - the if statement in a game of snakes and ladders does not. Where you draw the line between them is a personal choice. – Floris Apr 29 '14 at 22:34
  • 2
    I hate to say it, but having done enough corporate work, with beginners writing real code, I'd have to recommend brute forcing it. Because then the new guys will understand it and will not break it. sad but true - in some cases its smart to be dumb. – Richard Le Mesurier Apr 30 '14 at 14:44
  • 22
    This is a decent question, and I don't want to take anything away from the poster at all, but this does not deserve 500+ points. This is how we end up with some of the nonsense we do with people with thousands of points appearing to be authorities here. (Feel free to move this comment if it belongs elsewhere.) – GaTechThomas May 01 '14 at 21:08
  • 1
    @floris ... and where you draw the line in Snakes and Ladders is of the utmost importance >;-) – smirkingman May 01 '14 at 21:27
  • @eggyal It tells you the direction to the next square. 1-10 go left-to-right along the bottom of the board. Then 11 is above 10 and 11-20 go right-to-left along the second row up; and so on. So the OP is trying to work out whether to move the counter upwards (if n is a multiple of 10), to the right (if n is in 1-9, 21-29 etc) or to the left (if n is in 11-19, 31-39 etc). – Dawood ibn Kareem May 09 '14 at 08:29

13 Answers13

90

For the first one, to check if a number is a multiple of use:

if (num % 10 == 0) // It's divisible by 10

For the second one:

if(((num - 1) / 10) % 2 == 1 && num <= 100)

But that's rather dense, and you might be better off just listing the options explicitly.


Now that you've given a better idea of what you are doing, I'd write the second one as:
   int getRow(int num) {
      return (num - 1) / 10;
   }

   if (getRow(num) % 2 == 0) {
   }

It's the same logic, but by using the function we get a clearer idea of what it means.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Winston Ewert
  • 44,070
  • 10
  • 68
  • 83
  • 81
    `if((num - 1) / 10) % 2 == 1 && num < 100)` - I'd cry if I saw that. – Daniel Kamil Kozar Apr 27 '14 at 23:05
  • 33
    @DanielKamilKozar, as you should. – Winston Ewert Apr 27 '14 at 23:08
  • One more thing: Sorry for nitpicking here, but it should be `num <= 100` to include 100 (or however it would be written in c++). – Anonymous Apr 27 '14 at 23:13
  • 1
    Thanks! The second solution is clever I think, but why would it be bad practice? I assume it is because of Daniel's response. Because of readability? – user3419168 Apr 27 '14 at 23:13
  • 2
    @user3419168, just by itself it leaves one to wonder what in the world it means. It gives no hints as to what in the world its trying to do. That's why in the edit I showed a version that splits the logic into a function which makes it clearer what the calculations are actually doing. – Winston Ewert Apr 27 '14 at 23:14
  • 3
    It may be prudent to also assert `num >= 11` as (1) that lower limit is proscribed, and (2) `%` on a negative number returns a negative number as well. (I must admit using `& 1` here is "safer" but also assumes additional knowledge.) – Jongware Apr 27 '14 at 23:26
  • 1
    It might also be wise to add the modulo definition and WHY `x % 10 == 0` works. No one has felt the need to cover that minor detail. – WernerCD Apr 28 '14 at 01:33
  • 2
    +1 for the Edit, it gets into the why of the list of ranges and presents it in a readable manner. IMO, one step up would be to wrap the `getRow(num) % 2 == 0` in a function as well to make it crystal clear what the intent is. `bool inEvenRow(int num){ return getRow(num) % 2 ==0;}` – Mr.Mindor Apr 28 '14 at 14:43
  • @AmadeusDrZaius, while I find that clever, I don't actually find it gives me any more idea of what the calculations mean. – Winston Ewert Apr 29 '14 at 22:30
  • @WinstonEwert To me, the difference is that yours consists of two statements, one of which is very complex, whereas this consists of three statements, all of which are simple. Having the three operations `-, /, %` all together makes my brain hang if I can't make sense of it right away, but maybe that's just me. – Chris Middleton Apr 29 '14 at 22:32
  • Couldn't you just use `x & 1` instead of `x % 2 == 1`? – Cole Tobin Apr 30 '14 at 01:23
  • 2
    @user3419168: In some guidelines it is said that clever code, while clever, should be avoided, because it is clever. Think about some constructs in maths, which are clever, but which are unobvious. In programming, obviousness is worth a lot of money when it comes to changes and fixes. – Sebastian Mach Apr 30 '14 at 10:46
  • Surely somebody must be able to rewrite this as one-liner, preferably with shift operators and a combination of several pre and post-increments, to ensure that the intent is utterly opaque? – smirkingman May 01 '14 at 21:30
  • @AmadeusDrZaius Why would you include `(num % 20) <= 20`? This is true for all values of `num`. You actually wanted to write `num % 20 == 0 || num % 20 >= 11`, but in my opinion, a good alternative is `(num - 1) % 20 >= 10` – Dawood ibn Kareem May 08 '14 at 11:09
  • @DavidWallace You're right, don't know what I was thinking. Removed it since it was incorrect. – Chris Middleton May 08 '14 at 15:43
  • The `% 2 == 1` became `% 2 == 0` after the rewriting; you may want to fix it. :) – ShreevatsaR Apr 13 '16 at 18:58
  • Minor addition - a function for the second condition would be slightly clearer. `bool oddRow(int num) { return 1 & getRow(num); }` – Preston L. Bannister Aug 02 '16 at 23:03
40

if (num is a multiple of 10) { do this }

if (num % 10 == 0) {
  // Do something
}

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }

The trick here is to look for some sort of commonality among the ranges. Of course, you can always use the "brute force" method:

if ((num > 10 && num <= 20) ||
    (num > 30 && num <= 40) ||
    (num > 50 && num <= 60) ||
    (num > 70 && num <= 80) ||
    (num > 90 && num <= 100)) {
  // Do something
}

But you might notice that, if you subtract 1 from num, you'll have the ranges:

10-19, 30-39, 50-59, 70-79, 90-99

In other words, all two-digit numbers whose first digit is odd. Next, you need to come up with a formula that expresses this. You can get the first digit by dividing by 10, and you can test that it's odd by checking for a remainder of 1 when you divide by 2. Putting that all together:

if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
  // Do something
}

Given the trade-off between longer but maintainable code and shorter "clever" code, I'd pick longer and clearer every time. At the very least, if you try to be clever, please, please include a comment that explains exactly what you're trying to accomplish.

It helps to assume the next developer to work on the code is armed and knows where you live. :-)

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • 7
    I'd still go for the clever code, but turn it into maintainable code by extracting functions. It would be just as readable if that last bit said `&& isTensDigitOdd(num)`, perhaps with a comment before the function definition explaining what it does. If such a pattern exists, a comment explaining the reasoning for the pattern is enlightening for maintainability imo. – chris Apr 28 '14 at 02:39
  • 3
    Chris, that's a great strategy when the "cleverness" has a clear advantage: much shorter code (which means less chance of a typo, especially if it changes), or a large improvement in efficiency. There's almost always a tradeoff between brevity, clarity, and efficiency, and finding a good compromise is a great skill to develop. (See http://stackoverflow.com/a/2151844/29157 for a snicker.) – Adam Liss Apr 28 '14 at 12:55
  • 1
    This is a much better approach. So much easier to understand than the 'clever code' and the performance difference is probably negligible. – user1477388 Apr 28 '14 at 13:19
  • @AdamLiss, Yes, my opinion is of little value as I haven't had enough experience to see the repercussions of these decisions. I'm sure I will soon enough, and I'll be sure to get a second opinion if needed. – chris Apr 29 '14 at 05:40
  • 1
    Don't sell yourself short. Your instincts are very sensible, and you seem eager to keep learning. Every opinion is valuable if there's a good reason behind it ... and sometimes even if there isn't. I'd bet money that you'll go far. – Adam Liss Apr 29 '14 at 10:46
  • +1 for the brute force - because it can be understood by anyone who comes around, no matter how stressed or tired. Of course this comment really only applies to the corporate world - if its your own code, feel free to use whichever ninja shorthand you enjoy most on the day – Richard Le Mesurier Apr 30 '14 at 14:50
30

If you are using GCC or any compiler that supports case ranges you can do this, but your code will not be portable.

switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
    // Do something
    break;
default:
    // Do something else
    break;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • 1
    Can you please tell me why this code is not portable ? – M Sharath Hegde Apr 28 '14 at 05:29
  • 8
    @MSharathHegde because it require GCC extension, which is not part of standard and some compiler won't support it – Bryan Chen Apr 28 '14 at 05:31
  • 5
    This is the right answer, because it's immediately apparent what the intent is. All those 'clever' answers with modulo are a maintenance nightmare, even with comments. – smirkingman Apr 28 '14 at 09:59
  • @smirkingman Indeed that's what I said in my comment to the main question. It just takes some experience of new coders in a corporate job to realise that the obvious way is often way better than the smart-ninja way. – Richard Le Mesurier Apr 30 '14 at 14:46
15

This is for future visitors more so than a beginner. For a more general, algorithm-like solution, you can take a list of starting and ending values and check if a passed value is within one of them:

template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
    return std::any_of(first, last, [&val](const auto &p) {
        return p.first <= val && val <= p.second;
    });
}

For simplicity, I used a polymorphic lambda (C++14) instead of an explicit pair argument. This should also probably stick to using < and == to be consistent with the standard algorithms, but it works like this as long as Elem has <= defined for it. Anyway, it can be used like this:

std::pair<int, int> intervals[]{
    {11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};

const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);

There's a live example here.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Neat solution. I would probably have used a single array, since you can format it with 2 numbers per line to represent pairs. – Kevin Lam Apr 27 '14 at 23:56
  • @HunterGuy2, Very good point. I'm actually going to change it to operate on pairs because I was only thinking about zip iterators for some reason. – chris Apr 28 '14 at 02:24
  • Really nice stl approach! Love it! – higuaro Apr 30 '14 at 02:53
5

The first one is easy. You just need to apply the modulo operator to your num value:

if ( ( num % 10 ) == 0)

Since C++ is evaluating every number that is not 0 as true, you could also write:

if ( ! ( num % 10 ) )  // Does not have a residue when divided by 10

For the second one, I think this is cleaner to understand:

The pattern repeats every 20, so you can calculate modulo 20. All elements you want will be in a row except the ones that are dividable by 20.

To get those too, just use num-1 or better num+19 to avoid dealing with negative numbers.

if ( ( ( num + 19 ) % 20 ) > 9 )

This is assuming the pattern repeats forever, so for 111-120 it would apply again, and so on. Otherwise you need to limit the numbers to 100:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kasimir
  • 51
  • 1
5

With a couple of good comments in the code, it can be written quite concisely and readably.

// Check if it's a multiple of 10
if (num % 10 == 0) { ... }

// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
La-comadreja
  • 5,627
  • 11
  • 36
  • 64
  • 2
    The first comment is unneeded. Any programmer with a little experience will know that `num % 10 == 0` is the same thing as `num` is a multiple of 10. – Justin Apr 28 '14 at 17:39
  • 7
    yes but beginners read this site too. I wouldn't normally use that comment in my own code but it does make the answer clearer to the beginners who would benefit from this beginner question. – La-comadreja Apr 28 '14 at 17:43
  • 2
    Please don't ever do this. It actually reduces readability, by slowing down the reader and forcing them to read everything twice. Any programmer who doesn't understand that `if (num % 10 == 0)` means the same as `// Check if it's a multiple of 10` should not be maintaining _your_ code. This is a well-known anti-pattern. – Dawood ibn Kareem May 08 '14 at 19:17
  • 1
    @DavidWallace see comment above. We can't guarantee that the readers of this post will know this anti-pattern. – La-comadreja May 09 '14 at 15:34
  • 2
    No, I mean that commenting each line to say what it does is an anti-pattern. I don't mean that using `%` is an anti-pattern; obviously it is not. Really, assuming that many of the readers of this post will be beginners, teaching them this style of writing comments is making a negative contribution to their development as programmers. – Dawood ibn Kareem May 09 '14 at 19:55
4

You basically explained the answer yourself, but here's the code just in case.

if((x % 10) == 0) {
  // Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
  // Do this
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Henry Harris
  • 161
  • 1
  • 11
  • 2
    Correct `x < 41 x > 50` and put parentheses. – 101010 Apr 27 '14 at 23:08
  • 1
    @40two, Technically, `operator&&` has a higher precedence than `operator||`, so it's fine, but I'm pretty sure GCC warns about it anyway. – chris Apr 27 '14 at 23:26
  • 18
    Consider representing the inequality `10 < x < 21` as `10 < x && x < 21` rather than `x > 10 && x < 21`. It's easier to read the inequality when it is in the same order as you'd write it mathematically. – Eric Lippert Apr 28 '14 at 03:17
  • 5
    This code is fairly unreadable and says little about actual logic. I dislike this answer. – Dariusz Apr 28 '14 at 08:38
  • 3
    I am downvoting this because you answered exactly what the OP did. – Bruno Ferreira Apr 28 '14 at 19:19
3

You might be overthinking this.

if (x % 10)
{
   .. code for 1..9 ..
} else
{
   .. code for 0, 10, 20 etc.
}

The first line if (x % 10) works because (a) a value that is a multiple of 10 calculates as '0', other numbers result in their remainer, (b) a value of 0 in an if is considered false, any other value is true.

Edit:

To toggle back-and-forth in twenties, use the same trick. This time, the pivotal number is 10:

if (((x-1)/10) & 1)
{
  .. code for 10, 30, ..
} else
{
   .. code for 20, 40, etc.
}

x/10 returns any number from 0 to 9 as 0, 10 to 19 as 1 and so on. Testing on even or odd -- the & 1 -- tells you if it's even or odd. Since your ranges are actually "11 to 20", subtract 1 before testing.

Jongware
  • 22,200
  • 8
  • 54
  • 100
1

A plea for readability

While you already have some good answers, I would like to recommend a programming technique that will make your code more readable for some future reader - that can be you in six months, a colleague asked to perform a code review, your successor, ...

This is to wrap any "clever" statements into a function that shows exactly (with its name) what it is doing. While there is a miniscule impact on performance (from "function calling overhead") this is truly negligible in a game situation like this.

Along the way you can sanitize your inputs - for example, test for "illegal" values. Thus you might end up with code like this - see how much more readable it is? The "helper functions" can be hidden away somewhere (the don't need to be in the main module: it is clear from their name what they do):

#include <stdio.h>

enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};

int notInRange(int square) {
  return(square < 1 || square > 100)?YES:NO;
}

int isEndOfRow(int square) {
  if (notInRange(square)) return OUT_OF_RANGE;
  if (square == 100) return WINNER; // I am making this up...
  return (square % 10 == 0)? YES:NO;
}

int rowType(unsigned int square) {
  // return 1 if square is in odd row (going to the right)
  // and 0 if square is in even row (going to the left)
  if (notInRange(square)) return OUT_OF_RANGE; // trap this error
  int rowNum = (square - 1) / 10;
  return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
                                       // and 1 (EVEN) for 11-20, 31-40, ...
}

int main(void) {
  int a = 12;
  int rt;
  rt = rowType(a); // this replaces your obscure if statement

  // and here is how you handle the possible return values:
  switch(rt) {
  case ODD:
    printf("It is an odd row\n");
    break;
  case EVEN:
    printf("It is an even row\n");
    break;
  case OUT_OF_RANGE:
    printf("It is out of range\n");
    break;
  default:
    printf("Unexpected return value from rowType!\n");
  }

  if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
  if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}
Floris
  • 45,857
  • 6
  • 70
  • 122
  • 3
    Isn't it trying to push it too far with `YES` and `NO`? – rmobis Apr 30 '14 at 03:16
  • @Raphael_ - it might well be: I was just showing a "for instance". Many people use true/false, obviously. But I can never remember (because different languages use different conventions): is is `TRUE`, `True`, or `true`? And what, if any, header files would I need to include in ordinary C? So I rolled my own. Wonder if that's what got a downvote... – Floris Apr 30 '14 at 11:22
1

As others have pointed out, making the conditions more concise won't speed up the compilation or the execution, and it doesn't necessarily help with readability either.

It can help in making your program more flexible, in case you decide later that you want a toddler's version of the game on a 6 x 6 board, or an advanced version (that you can play all night long) on a 40 x 50 board.

So I would code it as follows:

// What is the size of the game board?
#define ROWS            10
#define COLUMNS         10

// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare     1
#define lastSquare      (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num)   (num == lastSquare)
#define overShot(num)   (num > lastSquare)

// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
//  have simplified these expressions)
#define getRow(num)   (((num - 1) / COLUMNS) + 1)
#define getCol(num)   (((num - 1) % COLUMNS) + 1)

// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num)    ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num)    ((getRow(num) % 2) == 0)

// Are we on the last square in the row?
#define isLastInRow(num)    (getCol(num) == COLUMNS)

// And finally we can get onto the code

if (notStarted(mySquare))
{
  // Some code for when we haven't got our piece on the board yet
}
else
{
  if (isLastInRow(mySquare))
  {
    // Some code for when we're on the last square in a row
  }


  if (isRightToLeftRow(mySquare))
  {
    // Some code for when we're travelling from right to left
  }
  else
  {
    // Some code for when we're travelling from left to right
  }
}

Yes, it's verbose, but it makes it clear exactly what's happening on the game board.

If I was developing this game to display on a phone or tablet, I'd make ROWS and COLUMNS variables instead of constants, so they can be set dynamically (at the start of a game) to match the screen size and orientation.

I'd also allow the screen orientation to be changed at any time, mid-game - all you need to do is switch the values of ROWS and COLUMNS, while leaving everything else (the current square number that each player is on, and the start/end squares of all the snakes and ladders) unchanged. Then you 'just' have to draw the board nicely, and write code for your animations (I assume that was the purpose of your if statements) ...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • I've also up-voted Floris' answer - a different style of achieving a similar result, that I didn't see before I wrote my answer – Laurence Renshaw Apr 30 '14 at 01:45
  • 2
    you should use inline function instead of `#define` – Bryan Chen Apr 30 '14 at 02:38
  • It's good practice when you use function-like `#define` instructions, to put parentheses around the arguments, where they appear in the expansion. So instead of `#define finished(num) (num == lastSquare)` you should write `#define finished(num) ((num) == lastSquare)`. The reason is that if you use such an instruction with an expression containing an operator with a low enough precedence, you won't get the answer you expect. In this case, if you don't use the extra parentheses, then `finished(a & b)` expands into `(a & b == lastSquare)` which is almost certainly not what you want. – Dawood ibn Kareem May 09 '14 at 08:54
1

For the first one:

if (x % 10 == 0)

will apply to:

10, 20, 30, .. 100 .. 1000 ...

For the second one:

if (((x-1) / 10) % 2 == 1)

will apply for:

11-20, 31-40, 51-60, ..

We basically first do x-1 to get:

10-19, 30-39, 50-59, ..

Then we divide them by 10 to get:

1, 3, 5, ..

So we check if this result is odd.

Khaled.K
  • 5,828
  • 1
  • 33
  • 51
1

You can try the following:

// Multiple of 10
if ((num % 10) == 0)
{
   // Do something
}
else if (((num / 10) % 2) != 0)
{
    // 11-20, 31-40, 51-60, 71-80, 91-100
}
 else
{
    // Other case
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ShalakaV
  • 25
  • 1
  • 6
  • In the OP question the check for the multiple of 10 is not related to the range check, and in the range check 20 need to be on the same range of 11, with your code ((20 / 10) % 2) -> (2 % 2) -> 0 – Serpiton Apr 30 '14 at 20:01
0

I know that this question has so many answers, but I will thrown mine here anyway...

Taken from Steve McConnell's Code Complete, 2nd Edition: "Stair-Step Access Tables:

Yet another kind of table access is the stair-step method. This access method isn’t as direct as an index structure, but it doesn’t waste as much data space. The general idea of stair-step structures, illustrated in Figure 18-5, is that entries in a table are valid for ranges of data rather than for distinct data points.

Enter image description here

Figure 18-5 The stair-step approach categorizes each entry by determining the level at which it hits a “staircase.” The “step” it hits determines its category.

For example, if you’re writing a grading program, the “B” entry range might be from 75 percent to 90 percent. Here’s a range of grades you might have to program someday:

Enter image description here

To use the stair-step method, you put the upper end of each range into a table and then write a loop to check a score against the upper end of each range. When you find the point at which the score first exceeds the top of a range, you know what the grade is. With the stair-step technique, you have to be careful to handle the endpoints of the ranges properly. Here’s the code in Visual Basic that assigns grades to a group of students based on this example:

Enter image description here

Although this is a simple example, you can easily generalize it to handle multiple students, multiple grading schemes (for example, different grades for different point levels on different assignments), and changes in the grading scheme."

Code Complete, 2nd Edition, pages 426 - 428 (Chapter 18).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
lauCosma
  • 154
  • 10