{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes you just want to repeat yourself. Sometimes you just need to repeat yourself. All programming languages have a concept of loops and C# is no different. \n", "\n", "We have the following keywords available to deliver looping capabilities:\n", "- `for`\n", "- `while`\n", "- `do`\n", "\n", "In this lesson, we'll discuss the different ways to loop over data. This time, we will use the example of dealing cards to players in a game. This is a repeated action that you really wouldn't want to write over and over again:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var cardsInPlayer1Hand = 0;\n", "var cardsInPlayer2Hand = 0;\n", "\n", "var cardsInDeck = 52;\n", "\n", "cardsInDeck--;\n", "cardsInPlayer1Hand++;\n", "\n", "cardsInDeck--;\n", "cardsInPlayer2Hand++;\n", "\n", "cardsInDeck--;\n", "cardsInPlayer1Hand++;\n", "\n", "cardsInDeck--;\n", "cardsInPlayer2Hand++;\n", "\n", "display($\"Cards left in deck: {cardsInDeck}\");\n", "display($\"Cards in player 1's hand: {cardsInPlayer1Hand}\");\n", "display($\"Cards in player 2's hand: {cardsInPlayer2Hand}\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's going to be rough to maintain if we have more than 2 players or if we want to deal more than 2 cards to each player. Let's introduce a `for` loop\n", "\n", "## For Loops\n", "[For loops](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/for?WT.mc_id=visualstudio-twitch-jefritz) are a looping statement that allow you to repeat a block of code depending on a counter expression.  The for statement takes the general form:\n", "\n", "`for (Initializer; Condition; Iterator) { CODE TO EXECUTE }`\n", "\n", "The `Initializer` typically initializes a counter variable to be worked with.  \n", "\n", "The `Condition` is a test to be executed at the beginning of each attempt to execute the code block.  If the `Condition` evaluates to `true` then the code block will be executed.\n", "\n", "The optional `Iterator` code executes after each loop and typically increments the initialized variable , stepping towards the end value.\n", "\n", "Let's write our first `for` loop to deal 5 cards to each of 2 players:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var cardsInPlayer1Hand = 0;\n", "var cardsInPlayer2Hand = 0;\n", "var cardsInDeck = 52;\n", "\n", "for (var cardsDealt=0; cardsDealt<5; cardsDealt++) {\n", "\n", "\tcardsInDeck--;\n", "\tcardsInPlayer1Hand++;\n", "\n", "\tcardsInDeck--;\n", "\tcardsInPlayer2Hand++;\n", "\n", "}\n", "\n", "display($\"Cards left in deck: {cardsInDeck}\");\n", "display($\"Cards in player 1's hand: {cardsInPlayer1Hand}\");\n", "display($\"Cards in player 2's hand: {cardsInPlayer2Hand}\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's MUCH easier to read and to work with. Why don't you try these two test scenarios to see if you understand this code:\n", "- Deal 3 cards to each player\n", "- Introduce a variable for the number of cards to deal and set that value to 7\n", "\n", "You can even run `for` loops inside `for` loops. Let's update our sample code to introduce a `for` loop for a different number of players:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var cardsInPlayer1Hand = 0;\n", "var cardsInPlayer2Hand = 0;\n", "var cardsInPlayer3Hand = 0;\n", "var cardsInPlayer4Hand = 0;\n", "var cardsInDeck = 52;\n", "\n", "for (var cardsDealt=0; cardsDealt<5; cardsDealt++) {\n", "\n", "\tfor (var player=0; player<4; player++) {\n", "\n", "\t\tcardsInDeck--;\n", "\t\tif (player == 0) cardsInPlayer1Hand++;\n", "\t\telse if (player == 1) cardsInPlayer2Hand++;\n", "\t\telse if (player == 2) cardsInPlayer3Hand++;\n", "\t\telse cardsInPlayer4Hand++;\n", "\t\n", "\t}\n", "\n", "}\n", "\n", "display($\"Cards left in deck: {cardsInDeck}\");\n", "display($\"Cards in player 1's hand: {cardsInPlayer1Hand}\");\n", "display($\"Cards in player 2's hand: {cardsInPlayer2Hand}\");\n", "display($\"Cards in player 3's hand: {cardsInPlayer3Hand}\");\n", "display($\"Cards in player 4's hand: {cardsInPlayer4Hand}\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pretty neat! Notice how we can also use the counter variable `player` to inspect and make decisions about how we walk through the loop and what values we want to update.\n", "\n", "
\n", "\n", "WARNING: You can write infinite loops using the `for` statement.  The following block contains an infinite loop.  Be prepared to stop the Jupyter notebook and restart it after you remove the loop.\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "display(\"this is an infinite loop... be ready to kill your notebook with the STOP button above\");\n", "\n", "/*\n", "\n", "for (var counter=1; counter>0; counter++) {\n", "\n", "\tdisplay(\"Counting \" + counter);\n", "\n", "}\n", "\n", "*/" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Stopping loops with Break\n", "If you need to exit a loop you can execute the `break` statement.  \n", "\n", "Let's go back to our dealing example, and let's deal 7 cards to each of four players but we only have 20 cards in the deck to start. This would be a problem because we are 8 cards short. We can introduce a `break` statement to stop attempting to deal cards if we run out of cards:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var cardsInPlayer1Hand = 0;\n", "var cardsInPlayer2Hand = 0;\n", "var cardsInPlayer3Hand = 0;\n", "var cardsInPlayer4Hand = 0;\n", "var cardsInDeck = 20;\n", "\n", "for (var cardsDealt=0; cardsDealt<7; cardsDealt++) {\n", "\n", "\tfor (var player=0; player<4; player++) {\n", "\n", "\t\tcardsInDeck--;\n", "\t\tif (player == 0) cardsInPlayer1Hand++;\n", "\t\telse if (player == 1) cardsInPlayer2Hand++;\n", "\t\telse if (player == 2) cardsInPlayer3Hand++;\n", "\t\telse cardsInPlayer4Hand++;\n", "\t\n", "\t}\n", "\n", "\tif (cardsInDeck < 1) \n", "\t{\n", "\n", "\t\tdisplay(\"We've dealt all of the cards!\");\n", "\t\tbreak;\n", "\t\n", "\t}\n", "\n", "}\n", "\n", "display($\"Cards left in deck: {cardsInDeck}\");\n", "display($\"Cards in player 1's hand: {cardsInPlayer1Hand}\");\n", "display($\"Cards in player 2's hand: {cardsInPlayer2Hand}\");\n", "display($\"Cards in player 3's hand: {cardsInPlayer3Hand}\");\n", "display($\"Cards in player 4's hand: {cardsInPlayer4Hand}\");\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`break` is a simple keyword that doesn't take any arguments and just stops the execution of the loop immediately and moves outside the loop.\n", "\n", "There's a slight bug in this code... we deal cards to each player and then check if we have cards left in the deck. As an exercise for you, consider the following:\n", "- We only have 23 cards left in the deck\n", "- How should we modify this code to ensure there are always cards left in the deck when dealing to a player?\n", "- What changes would you make to ensure that we always deal the same number of cards to each player?\n", "\n", "### Continuing processing with Continue\n", "We can also turn this logic around and instead of testing and stopping the loop, we can continue the loop. Continue keyword allows us to stop processing any further statements in this block for the loop and resume at the beginning of the block. Let's rewrite that last example so that it continues looping even if we've run out of cards, but doesn't actually deal to the players." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var cardsInPlayer1Hand = 0;\n", "var cardsInPlayer2Hand = 0;\n", "var cardsInPlayer3Hand = 0;\n", "var cardsInPlayer4Hand = 0;\n", "var cardsInDeck = 20;\n", "\n", "for (var cardsDealt=0; cardsDealt<7; cardsDealt++) {\n", "\n", "\tif (cardsInDeck < 1) \n", "\t{\n", "\n", "\t\tdisplay(\"We've dealt all of the cards!\");\n", "\t\tcontinue;\n", "\t\n", "\t}\n", "\n", "\tfor (var player=0; player<4; player++) {\n", "\n", "\t\tcardsInDeck--;\n", "\t\tif (player == 0) cardsInPlayer1Hand++;\n", "\t\telse if (player == 1) cardsInPlayer2Hand++;\n", "\t\telse if (player == 2) cardsInPlayer3Hand++;\n", "\t\telse cardsInPlayer4Hand++;\n", "\t\n", "\t}\n", "\n", "}\n", "\n", "display($\"Cards left in deck: {cardsInDeck}\");\n", "display($\"Cards in player 1's hand: {cardsInPlayer1Hand}\");\n", "display($\"Cards in player 2's hand: {cardsInPlayer2Hand}\");\n", "display($\"Cards in player 3's hand: {cardsInPlayer3Hand}\");\n", "display($\"Cards in player 4's hand: {cardsInPlayer4Hand}\");\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the 'we've dealt all the cards' message is displayed twice because it detected that we were out of cards twice and continued the loop. This isn't an efficient use of resources for this sample, but shows that you can continue looping if you need to.\n", "## While and Do Loops\n", "\n", "`while` and `do` loops have almost identical structure and perform the same task.  You provide a test condition over which the contents of the loop should continue to be executed.  The [`while` loop](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/while?WT.mc_id=visualstudio-twitch-jefritz) executes the test _FIRST_ before the loop statements, and the [`do` loop](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/do?WT.mc_id=visualstudio-twitch-jefritz) executes the test _AFTER_ the statements.\n", "\n", "Let's revisit our card dealing scenario and this time lets deal out ALL of the cards in the deck. If we use a `while` loop, we can check if there are any cards left before we attempt to deal to the players:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var cardsInPlayer1Hand = 0;\n", "var cardsInPlayer2Hand = 0;\n", "var cardsInPlayer3Hand = 0;\n", "var cardsInPlayer4Hand = 0;\n", "var cardsInDeck = 52;\n", "\n", "while (cardsInDeck > 0)\n", "{\n", "\n", "\tfor (var player=0; player<4; player++) {\n", "\n", "\t\tcardsInDeck--;\n", "\t\tif (player == 0) cardsInPlayer1Hand++;\n", "\t\telse if (player == 1) cardsInPlayer2Hand++;\n", "\t\telse if (player == 2) cardsInPlayer3Hand++;\n", "\t\telse cardsInPlayer4Hand++;\n", "\t\n", "\t}\n", "\n", "}\n", "\n", "display($\"Cards left in deck: {cardsInDeck}\");\n", "display($\"Cards in player 1's hand: {cardsInPlayer1Hand}\");\n", "display($\"Cards in player 2's hand: {cardsInPlayer2Hand}\");\n", "display($\"Cards in player 3's hand: {cardsInPlayer3Hand}\");\n", "display($\"Cards in player 4's hand: {cardsInPlayer4Hand}\");\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could structure this so that we test if we have any cards remaining in the deck by using a `do...while` loop expression like this:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "dotnet_interactive": { "language": "csharp" }, "polyglot_notebook": { "kernelName": "csharp" } }, "outputs": [], "source": [ "var cardsInPlayer1Hand = 0;\n", "var cardsInPlayer2Hand = 0;\n", "var cardsInPlayer3Hand = 0;\n", "var cardsInPlayer4Hand = 0;\n", "var cardsInDeck = 52;\n", "\n", "do {\n", "\n", "\tfor (var player=0; player<4; player++) {\n", "\n", "\t\tcardsInDeck--;\n", "\t\tif (player == 0) cardsInPlayer1Hand++;\n", "\t\telse if (player == 1) cardsInPlayer2Hand++;\n", "\t\telse if (player == 2) cardsInPlayer3Hand++;\n", "\t\telse cardsInPlayer4Hand++;\n", "\t\n", "\t}\n", "\n", "} while (cardsInDeck > 0);\n", "\n", "display($\"Cards left in deck: {cardsInDeck}\");\n", "display($\"Cards in player 1's hand: {cardsInPlayer1Hand}\");\n", "display($\"Cards in player 2's hand: {cardsInPlayer2Hand}\");\n", "display($\"Cards in player 3's hand: {cardsInPlayer3Hand}\");\n", "display($\"Cards in player 4's hand: {cardsInPlayer4Hand}\");" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This APPEARS similar and in this case runs identically, but there are scenarios where you do want to test at the end of the loop instead of testing at the beginning. \n", "\n", "The `break` and `continue` keywords work the same in `while` and `do...while` loops." ] } ], "metadata": { "kernelspec": { "display_name": ".NET (C#)", "language": "C#", "name": ".net-csharp" }, "language_info": { "name": "polyglot-notebook" }, "polyglot_notebook": { "kernelInfo": { "defaultKernelName": "csharp", "items": [ { "aliases": [], "languageName": "csharp", "name": "csharp" } ] } } }, "nbformat": 4, "nbformat_minor": 2 }