5.3. for Statement Examples

5.3.1. Format Output: Console.Write()

Thus far all of our for loops have used a sequence of successive integers. You can do a quick test in csharprepl like:

> for (int i = 1; i < 5; i++)
 {
    Console.WriteLine(i);
 }
1
2
3
4

Now let us make the output neater by using the format string (Composite formatting) and the Console.Write() method:

> for (int i = 1; i <= 5; i++)
  {
    Console.WriteLine("{0}, ", i);
  }
1 2 3 4 5

5.3.2. Step in loop header

i++ is a common pattern and it increments by 1. Also common in the iterator/step section of the loop header is using assignment statement to control stepping:

i = i + k;

or the equivalent short-hand compound assignment operator:

i += k;

This means to increment the variable i by k.

Most C# binary operations have a similar variation. For instance if op is +, -, *, / or %,

variable op= expression

means the same as

variable = variable op expression

For example

x *= 5;

is the same as

x = x * 5;

5.3.3. Formatting Tables

Reports commonly include tables, often with successive lines generated by a consistent formula and therefore a good example for coding. As a simple first table, we can show the square, cube, and square root of numbers 1 through 10. The Math class has a method Sqrt, so we take the square root with the Math.Sqrt method. The pattern is consistent, so we can loop easily:

for ( int n = 1; n <= 10; n++)
{
    Console.WriteLine("{0} {1} {2} {3}", n, n*n, n*n*n, Math.Sqrt(n));
}

The numbers will be there, but the output is not pretty:

1 1 1 1
2 4 8 1.4142135623731
3 9 27 1.73205080756888
4 16 64 2
5 25 125 2.23606797749979
6 36 216 2.44948974278318
7 49 343 2.64575131106459
8 64 512 2.82842712474619
9 81 729 3
10 100 1000 3.16227766016838

First we might not need all those digits in the square root approximations. We can replace {3} by {3:F4} to just show 4 decimal places.

We can adjust the spacing to make nice columns by using a further formatting option. The longest entries are all in the last row, where they take up, 2, 3, 4, and 6 columns (for 3.1623). Change the format string:

for ( int n = 1; n <= 10; n++) {
    Console.WriteLine("{0,2} {1,3} {2,4} {3,6:F4}",
                       n, n*n, n*n*n, Math.Sqrt(n));
}

and we generate the neater output:

 1   1    1 1.0000
 2   4    8 1.4142
 3   9   27 1.7321
 4  16   64 2.0000
 5  25  125 2.2361
 6  36  216 2.4495
 7  49  343 2.6458
 8  64  512 2.8284
 9  81  729 3.0000
10 100 1000 3.1623

We are using two new formatting forms:

{index,fieldWidth} and
{index,fieldWidth:F#}

where index, fieldWidth, and # are replaces by specific literal integers. The new part with the comma (not colon) and fieldWidth, sets the minimum number of columns used for the substituted string, padding with blanks as needed.

If the string to be inserted is wider than the fieldWidth, then the whole string is inserted, ignoring the fieldWidth. For example:

string s = "stuff";
Console.WriteLine("123456789");
Console.WriteLine("{0,9}\n{0,7}\n{0,5}\n{0,3}", s);

generates:

123456789
    stuff
  stuff
stuff
stuff

filling 9, 7, and then 5 columns, by padding with 4, 2, and 0 blanks. The last line sticks out past the proposed 3-column fieldWidth.

One more thing to add to our power table is a heading. We might want:

n   square    cube    root

To make the data line up with the heading titles, we can expand the columns, with code in example:

Console.WriteLine("{0,2}{1,7}{2,5}{3,7}",
              "n", "square", "cube", "root");
for ( int n = 1; n <= 10; n++) {
Console.WriteLine("{0,2}{1,7}{2,5}{3,7:F4}",
                    n, n*n, n*n*n, Math.Sqrt(n));
}

generating the output:

 n  square    cube    root
 1       1       1  1.0000
 2       4       8  1.4142
 3       9      27  1.7321
 4      16      64  2.0000
 5      25     125  2.2361
 6      36     216  2.4495
 7      49     343  2.6458
 8      64     512  2.8284
 9      81     729  3.0000
10     100    1000  3.1623

Note how we make sure the columns are consistent in the heading and further rows: We used a format string for the headings with the same field widths as in the body of the table. A separate variation: We also reduced the length of the format string by putting all the substitution expressions in braces right beside each other, and generate the space between columns with a larger field width.

5.3.4. Reversed String Print

Create a method, call it ReverseStringPrint, that, when called with a string, will print the string reversed.

We know we could use a for loop to print the characters in a string reversely. Let us consider the following tools we have:

  • A string is an array of combined characters with indices.

  • An array is 0-based. The first character of a string str can be accessed as str[0]

  • To reverse a string, we need to start from the last indexed character.

  • The last index number of string str is related to the length of the string: str.Length.

  • To process the whole string, we can use the decrement assignment operator (--) to start the printing from the end of the string.

I managed to come up with some variation of code as below but am running into some errors:

> string s = "drab";
  for (int i = s.Length; i >= 0; i++)
  {
    Console.Write(s[i]);
  }
bard
>

One of the errors I see is the famous Index-Out-Of-Range error:

┌──────────IndexOutOfRangeException──────────┐
│ Index was outside the bounds of the array. │
└────────────────────────────────────────────┘

5.3.5. Reversed String Return

reversed. Logically, you will perform two separate ideas: reversing a string and printing it. Now consider the first part as its own method:

// this is a method return a string `s` in reverse order.
// say, if s is "drab", return "bard".
// below is the possible form of the header for the method
static string Reverse (string s)

In the Reverse method, use a for loop:

for (int i = s.Length - 1; i >= 0; i--) {

A significant idea here is that, instead of immediately printing the char’s, you need to return a reversed string. That means, you need to create a single string, with all the characters, before returning the result.

Let us think about this: If you start with s as "drab", and you go through the letters one at a time in reverse order, b a r d, you build up successively:

b
ba
bar
bard

You need a loop with variables and operations. The sequence of reversed letters, s[i], are the last character on the end of each line above.

We need a name for the initial part. I used the name rev. Combining with a string is done with the + operator. Then when rev is "ba" and s[i] is 'r', the combination, using the variable names, is

rev + s[i]

We want this in our loop, so we must be able to use that expression each time through the loop, so rev changes each time through the loop. In the next iteration rev is the result of the previous expression. The assignment statement to give us the next version of rev can just be:

rev = rev + s[i];

That gives us the general rule. Pay attention now to the beginning and end: The end is simple: The last value for rev is the complete reversed string, so that is what we return.

How do we initialize rev? You could imagine rev starting as "b", but the the first character that we add is 'a', and we would not be going through all the characters in our loop. It is better to go all the way back to the beginning: If we use the general form with the first letter in the reversed sequence,

rev = rev + s[i];

then the result of the initial rev + 'b' should just be "b". So what would rev be?

Remember the empty string: initialize rev to be "".

The result is:

/// Return s in reverse order.
/// If s is "drab", return "bard".
static string Reverse (string s)
{
   string rev = "";
   for (int i = s.Length - 1; i >= 0; i--) {
      rev += s[i];
   }
   return rev;
}

We used our new operator += to be more concise.

This function and a Main used to demonstrate it are in here.