5.7. Lab: for Loops#
Note that the course policy is that you should not use generative AI without authorization. If you are suspected to have used generative AI and not able to explain/reproduce your work when requested, all your related assignments throughout the semester will be regraded as 0.
Create a dotnet console app project (Creating a C# Project), if you have not, in your USERNAME/workspace/introcscs directory; call it Ch05ForLoop.
Prepare your code in VS Code.
Use the file Program.cs to code.
The namespace of this project is IntroCSCS.
The class name of this project is Ch05ForLoop.
When executing code, you will only use the Main() method in class Ch05ForLoop.
You will prepare methods in the same class to be called from the Main() method.
Use a Word document to prepare your assignment.
Number the questions and annotate your answers (using // in code when appropriate) to show your understanding.
For coding questions, screenshot and paste 1) your code in VS Code and 2) the results of the code’s execution (command prompt and username are part of the execution).
Goals for this lab:
Practice with loops. You are encouraged to use a
forloop where appropriate.Use nested loops where appropriate.
5.7.1. For Loop Lab#
This part of the lab comes from loop_lab_stub/loop_lab.cs. You may refer to the code while answering the questions here.
From the terminal, go to your Ch05ForLoop project folder.
code .+ Enter from within the Ch05ForLoop directory.Start working on the methods.
Your task is to Fill in method bodies for each part below:
Complete method
PrintRepsto be called from Main() like:/// Print n copies of s, end to end. /// For example PrintReps("Ok", 9) prints: OkOkOkOkOkOkOkOkOk static void PrintReps(string s, int n)
Hint: How would you do something like the example
PrintReps("Ok", 9)or with a higher count by hand? Probably count under your breath as you write:1 2 3 4 5 6 7 8 9 OkOkOkOkOkOkOkOkOk
This is a counting loop. Use a C-style for loop.
Complete method
StringOfReps/// Return a string containing n copies of s, end to end. /// For example StringOfReps("Ok", 9) returns: "OkOkOkOkOkOkOkOkOk" static string StringOfReps(string s, int n)
Note the distinction from the previous part: Here the method prints nothing. Its work is returned as a single string. You have call the method like:
Console.WriteLine(StringOfReps("Ok", 9));Complete method
Factorial: (A factorial, in mathematics, is the product of all positive integers less than or equal to a given positive integer and denoted by that integer and an exclamation point.)/// Return n! (n factorial: 1*2*3 *...* n if n >=1; /// 0! is 1.). For example Factorial(4) returns 1*2*3*4 = 24. static int Factorial(int n)
It is useful to think of the sequence of steps to calculate a concrete example of a factorial, say 6!:
Start with 1 2 * 1 = 2 3 * 2 = 6 4 * 6 = 24 5 * 24 = 120 6 * 120 = 720
ALSO find the largest value of
nfor which the method works. (You might want to add a bit of code further testing Factorial, to make this easier.) Caution: although a negative result from the product of two positive numbers is clearly wrong, only half of the allowed values are negative, so the first wrong answer could equally well be positive. Explain how you find the largest value of ``n`` that works in this method.
5.7.2. Head or Tails#
Write a method Flip(),
that will just randomly print Heads or Tails once.
Accomplish this by choosing 0 or 1 arbitrarily with a random
number generator. More details follow.
Use a Random object, and make the Random object a local variable inside
the Flip method.
It is generally a good idea to only create a single ``Random`` object that stays in scope for the whole program so it can be used by various programs. One way to do that is to make it static. For our purpose here, just place the random object declaration in your Flip() method:
Random r = new Random();
Note again that in the future you would place the object inside your class but outside of any method, so you can use ``r`` in any method in your class.
For int variables low and higher, with low < higher:
int n = r.Next(low, higher);
returns a (pseudo) random int, satisfying low <= n < higher.
If you select low and higher as 0 and 2,
so there are only two possible values for n,
then you can choose to print Heads or Tails with an
if-else statement based on the result.
In your Flip method, create a for loop so you generate a random sequence of
10 heads and/or tails. Print out the heads/tails like:
Tails Tails Tails Tails Heads Tails Heads Tails Tails Tails
Place return "" in your else block to avoid a “not all paths return value”
warning if you use an else if statement (or simply put the return line at the
end of the code block).
We have discovered some problems with the Next() implementation
that sometimes results in random values not
being generated. This is likely a bug that will be fixed. If you
experience any problems with Next(), the following is for you!
5.7.3. Group Flips#
Write a method GroupFlips() using the Flip() method
from the last question to:
/// Print out the results from the total number of random flips of a coin.
/// Group them groupSize per line, each followed by a space.
/// The last line may contain fewer than groupSize flips
/// if total is not a multiple of groupSize. The last line
/// should be followed by exactly one newline in all cases.
/// For example, GroupFlips(10, 4) *could* produce:
/// Heads Heads Tails Heads
/// Heads Tails Heads Tails
/// Tails Tails
static void GroupFlips(int total, int groupSize)
Complete this method definition and test
with a variety of calls to GroupFlips in Main. The output from the
previous exercise would be produced by the call:
GroupFlips(10, 1);
5.7.4. Reverse String foreach#
It used a for loop to go through the characters in
reverse order. Write a version with the only loop heading:
foreach(char ch in s) {
and no reference to indices in s.
5.7.5. Only Letters#
Write a program that defines and tests a method with description and heading:
/// Return s with all non-letters removed.
/// For example OnlyLetters("Hello, World!") returns "HelloWorld".
static string OnlyLetters(string s)
Assume the English alphabet.
5.7.6. Palindrome Exercise#
Write a program palindrome.cs that defines and tests a method with
description and heading:
/// Return true when s is a palindrome.
/// For example IsPalindrome("A Toyota!") returns true.
static bool IsPalindrome(string s)
A palindrome is a string that contains the same sequence of letters, ignoring capitalization, forward and backward. Non-letters are ignored. Examples are “Madam, I’m Adam.” and “Able was I ’ere I saw Elba.”
IsPalindrome can be written very concisely by copying and using
methods from previous exercises.
Predict what these code fragments print. Then check yourself in csharp:
for (int i = 3; i > 0; i--) {
for (int j = i; j < 4; j++) {
Console.Write(j);
}
Console.WriteLine();
}
string s = "abcdef";
for (int i = 1; i < s.Length; i += 2) {
for (int k = 0; k < i; k++) {
Console.Write(s[i]);
}
}
5.7.7. Power Table#
Write a method that completes and tests with this heading. Be sure your program tests with several values for each parameter:
/// Print a table of powers of positive integers. /// Assume 1 <= nMax <= 12, 1 <= powerMax <= 7. /// Example: output of PowerTable(3, 4) /// n^1 n^2 n^3 n^4 /// 1 1 1 1 /// 2 4 8 16 /// 3 9 27 81 /// public static void PowerTable(int nMax, int powerMax)
Make sure the table always ends up with right-justified columns.
Make the table have columns all the same width, but make the width be as small as possible for the parameters provided, leaving a minimal one space (but not less!) between columns somewhere in the table. Consider heading widths, too.
5.7.8. Reverse String Practice#
The following reverse-string examples were moved from worked examples as lab-style practice.
5.7.9. 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
stris 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]);
}
One of the errors I see is the famous Index-Out-Of-Range error:
┌──────────IndexOutOfRangeException──────────┐
│ Index was outside the bounds of the array. │
└────────────────────────────────────────────┘
5.7.10. 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.
5.7.11. Additional While/Do-While Lab Exercises#
The following activities are consolidated from the previous standalone while-loop lab section.
5.7.12. Easy while#
Write a program with a while loop to print:
10
9
8
7
6
5
4
3
2
1
Blastoff!
5.7.13. Number in Range [1…100]#
Enter an integer in the range [1 … 100]. If the entered number is invalid, enter it again. In this case, an invalid number will be any number that is not within the specified range. [1]
To solve the problem, we can use the following algorithm:
We create a num variable to which we assign the integer value obtained from the console input.
For a loop condition, we put an expression that is true if the number of the input is not in the range specified in the problem’s description.
In the body of the loop: we print a message “Invalid number!” on the console, then we assign a new value to num from the console input.
Once we have validated the entered number, we print the value of the number outside the body of the loop.
Here’s a implementation of the algorithm using a while loop and there are places that need to be fixed in the code:
var num = int.Parse(Console.WiteLine());
while (num < 1 && num > 100)
{
Console.WriteLine("Invalid number!");
num = int.Parse(Console.WriteLine());
}
Console.WriteLine("The number is: {{10}", num);
Fix the preceding code and make sure the execution performs correctly.
5.7.14. Number Guessing Game#
This lab is inspired by a famous children’s game known as the number-guessing game. We suppose two people are playing. The rules are:
Person A chooses a positive integer less than N and keeps it in his or her head.
Person B makes repeated guesses to determine the number. Person A must indicate whether the guess is higher or lower.
Person A must tell the truth.
So as an example:
George and Andy play the game.
George chooses a positive number less than 100 (29) and keeps it in his head.
Andy guesses 50. George says “Lower”. Andy now knows that $1 \leq number < 50$.
Andy guesses 25. George says “Higher”. Andy now knows that $26 \leq number < 50$.
Andy guesses 30. George says “Lower”. Andy now knows that the $26 \leq number < 30$.
Andy starts thinking that he is close to knowing the correct answer. He decides to guess 29. Andy guesses the correct number. So George says, “Good job! You win!”
The computer code for the game is going to be acting like Person A and should be configured and behave as follows.
Make sure your program has
namespace IntroCSCS;to match the UI class.Put the code for playing the number game in a method called
Game:
static void Game()
You call
Game()from theMainmethod.Prompt the player for a guess. Use
UI.PromptInt.When the player guesses the right number, print “Good job! You win!”
When the player is incorrect, print “Lower!” or “Higher!” as appropriate.
Have the Game method print “In this game you guess a positive number less than 100.” It is best if you have the printing statement reference the variable
big, rather than the literal100.Have the game generate a random number. For your convenience, use the C# code below to generate the random number. Assuming we want a
secretnumber so $1 \leq secret < big$, we can use the code:
Random r = new Random();
int secret = r.Next(1, big);
Note
In case you are wondering, we are creating a *new object*
of the *class* `Random`, which serves as the
*random number generator*. We'll cover this in more detail when we
get to the {ref}`classes` chapter.
:::
- Here is some illustration using a `Random` object in `csharprepl`.
Your answers will not be the same:
```
> Random r = new Random();
> r.Next(1, 100)
68
> r.Next(1, 100)
48
> r.Next(1, 100)
30
> r.Next(1, 100)
70
> r.Next(1, 100)
67
>
```
- Note that, the **minimum** possible value of the number returned by `r.Next`
is the first parameter, and the value returned is always *less* than
the second parameter, *never equal*.
As an extra challenge, when the player finally wins, print the number of guesses the player made.
When run, the program should work like (where
secretended up as 68):Guess a number less than 100!
Guess the number:
60
Higher!
Guess the number:
72
Lower!
Guess the number:
66
Higher!
Guess the number:
68
Good job! You win on guess 3!
5.7.15. Sum To n#
This lab gives detailed description about the process of arriving at a solution to the problem. Please read the explanations if you are new to coding and while loops.
Let us write a method to sum the numbers from 1 to n:
/// Return the sum of the numbers from 1 through n.
static int SumToN(int n)
{
...
}
For instance, SumToN(5) calculates 1 + 2 + 3 + 4 + 5 and returns 15. You know how to generate a sequence of integers (using loop header). To see how this works in steps, let us take a concrete example like the one above for SumToN(5), and write out a detailed sequence of steps like:
3 = 1 + 2
6 = 3 + 3
10 = 6 + 4
15 = 10 + 5
Since n is general, we need a loop, and hence we must see a pattern in code that we can repeat:
In each calculation the second term in the additions is a successive integer, that we can generate.
Starting in the second line, the first number in each addition is the sum from the previous line.
The next integer and the next partial sum change from step to step, so in order to use the same code over and over we will need changeable variables, with names. We can make the partial sum be
sumand we can call the next integeri. Each addition can be in the form:sum + i
We need to remember that result, the new sum. You might first think to introduce such a name:
newSum = sum + i;
This will work. We can go through the Loop Planning Rubric:
The variables are sum, newSum and i.
To evaluate
newSum = sum + i;
the first time in the loop, we need initial values for sum and i. Our concrete example leads the way:
int sum = 1, i = 2;
We need a while loop heading with a continuation condition. How
long do we want to add the next i? That is for all the value up to and
including n:
while (i <= n) {
There is one more important piece - making sure the same code
newSum = sum + i;
works for the next time through the loop. We have dealt before with the idea of the next number in sequence:
i = i + 1;
What about sum? What was the newSum
on one time through the loop becomes the old or
just plain sum the next time through, so we can make an assignment:
sum = newSum:
All together we calculate the sum with:
int sum = 1, i = 2;
while (i <= n) {
int newSum = sum + i;
sum = newSum:
i = i + 1;
}
We can condense it in this case: Since newSum is only used
once, we can do away with this extra variable name,
and directly change the value of sum:
int sum = 1, i = 2;
while (i <= n) {
sum = sum + i;
i = i + 1;
}
Finally this was supposed to fit in a method. The ultimate purpose
was to return the sum, which is the final value of the
variable sum, so the whole method is:
/// Return the sum of the numbers from 1 through n.
static int SumToN(int n) // line 1
{
int sum = 1, i = 2; // 2
while (i <= n) { // 3
sum = sum + i; // 4
i = i + 1; // 5
}
return sum; // 6
}
Also you should check the program in a more general situation, say with n
being 4. You should be able to play computer and generate this table,
using the line numbers shown in comments at the end of lines,
and following one statement of execution at a time. We only
make entries where variables change value.
Line |
i |
sum |
Comment |
|---|---|---|---|
1 |
assume 4 is passed for n |
||
2 |
2 |
1 |
|
3 |
2<=4: true, enter loop |
||
4 |
3 |
1+2=3 |
|
5 |
3 |
2+1=3, bottom of loop |
|
3 |
3<=4: true |
||
4 |
6 |
3+3=6 |
|
5 |
4 |
3+1=4, bottom of loop |
|
3 |
4<=4: true |
||
4 |
10 |
6+4=10 |
|
5 |
5 |
4+1=5, bottom of loop |
|
3 |
5<=4: false, skip loop |
||
6 |
return 10 |
5.7.16. Loan Table#
This exercise is an extension of the Long Term Investment. Different
forms of iteration may make sense to you but you are encouraged to use the while or do loop.
Loans are common with a specified interest rate and with a fixed periodic payment. Interest is charged at a fixed rate on the amount left in the loan after the last periodic payment (or start of the loan for the first payment).
For example, if an initial $100 loan is made with 10% interest per pay period, and a regular $20 payment each pay period: At the time of the first payment interest of $100*.10 = $10 is accrued, so the total owed is $110. Right after the payment of $20, $110 - $20 = $90 remains. That $90 gains interest of $90*.10 = $9 up to the next payment, when $90 + $9 = $99 is owed. After the regular payment of $20, $99 - $20 = $79 is left, and so on. When a payment of at most $20 brings the amount owed to 0, the loan is done.
We can make a table showing
Payment number (starting from 1)
The principal amount after the previous payment (or the beginning of the loan for the first payment)
The interest on that principal up until the next periodic payment
The payment made as a result.
Continuing the example above, the whole table would look like:
Number Principal Interest Payment
1 100.00 10.00 20.00
2 90.00 9.00 20.00
3 79.00 7.90 20.00
4 66.90 6.69 20.00
5 53.59 5.36 20.00
6 38.95 3.90 20.00
7 22.85 2.29 20.00
8 5.14 0.51 5.65
In the final line, the principal plus interest equal the payment, finishing off the loan.
Similarly, with a $1000.00 starting loan, 5% interest per pay period, and $196 payments due, you would get
Number Principal Interest Payment
1 1000.00 50.00 196.00
2 854.00 42.70 196.00
3 700.70 35.04 196.00
4 539.74 26.99 196.00
5 370.73 18.54 196.00
6 193.27 9.66 196.00
7 6.93 0.35 7.28
If a $46 payment were specified, the principal would not decrease from the initial amount, and the loan would never be paid off.
There are a couple of wrinkles here: double values do not hold decimal
values exactly. The decimal type does hold decimal numbers exactly
and
hence are better for monetary calculations. Decimal literals end with m, like
34.56m for exactly 34.56.
Though decimals are exact, money only has two decimal places. We make the
assumption that interest will always be calculated as current
principal*rate, rounded
to two decimal places: Math.Round(principal*rate, 2).
Write the LoanTable method and run it from Main:
/// Print a loan table, showing payment number, principal at the
/// beginning of the payment period, interest over the period, and
/// payment at the end of the period.
/// The principal is the initial amount of the loan.
/// The rate is fraction representing the rate of interest per PAYMENT.
/// The periodic regular payment is also specified.
public static void LoanTable(decimal principal, decimal rate,
decimal payment)
Note that the rate, too, is a decimal,
even though it does not represent money.
That is important, because arithmetic with a decimal and a double is
forbidden: A double would have to be explicitly cast to a decimal.
Insisting on decimal parameter simplifies the method code.
Footnotes