{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "l04-c000",
   "metadata": {},
   "source": [
    "# Lecture 4 - Repetition 1: Loops\n",
    "\n",
    "**Authors:** Fiona McNeill, Matteo Degiacomi, Rayo Verweij, and Nigel Topham\n",
    "\n",
    "## Introduction\n",
    "\n",
    "Now that we can branch our code in different directions, we're going to take it up a notch and look at control flow structures that allow us to repeat pieces of code.\n",
    "\n",
    "## Learning outcomes\n",
    "\n",
    "In this notebook, we'll discuss:\n",
    "\n",
    "- repeating code with `while` and `for` loops;\n",
    "- generating sequences of integers with `range()`;\n",
    "- building totals and counts with accumulators;\n",
    "- altering loop flow with `break` and `continue`;\n",
    "- using `pass` as a placeholder for an unfinished body; and\n",
    "- tracing nested loops.\n",
    "\n",
    "### Using the notebook\n",
    "\n",
    "Remember, this notebook is _yours_! You can edit each cell, for example by adding new examples or changing values to see what happens. Feel free to experiment!\n",
    "\n",
    "A common approach for learning how to read and write code in a structured way is called **PRIMM**:\n",
    "1. **Predict**: before running the code, look at it - do you understand each line? What do you think will happen?\n",
    "2. **Run**: now run the code and see what output you get.\n",
    "3. **Investigate**: did it go as you thought it would? If not, can you figure out why?\n",
    "4. **Modify**: now change the code and once again try to predict the output. If you got it wrong the first time, did you get it right now?\n",
    "5. **Make**: finally, try to write your own program from scratch using the skills you learned.\n",
    "\n",
    "You can apply steps 1-4 to each code cell in the notebook and many will have prediction prompts to help you get started. In addition, there will be challenges that ask you to put everything together yourself and execute step 5. There will often be multiple ways to solve these, but each challenge has a potential answer listed.\n",
    "\n",
    "### Navigating the notebook\n",
    "\n",
    "A Jupyter notebook can be operated with your keyboard. Here is a **cheat sheet**:\n",
    "\n",
    "- To run the selected cell and move to the next cell, press <kbd>&#x21E7; Shift</kbd> + <kbd>&#x23ce; Enter</kbd>.\n",
    "- To run the selected cell and keep focus there, press <kbd>⌃ Ctrl</kbd> + <kbd>&#x23ce; Enter</kbd>.\n",
    "- To create a cell under the one currently highlighted, press <kbd>B</kbd>;\n",
    "- To delete the currently highlighted cell, press <kbd>D</kbd> twice (be careful with this one!) or press <kbd>X</kbd> to cut it;\n",
    "- To get help for a specific function, place the cursor within the function's brackets, hold <kbd>&#x21E7; Shift</kbd>, and press <kbd>&#x21E5; Tab</kbd>.\n",
    "\n",
    "Watch out: **code cells remember what happened in cells before**. So, especially in the more complicated notebooks later down the line, make sure to always run **every** cell from top to bottom, as one might rely on a piece of code that came before it! If your results become confusing, use **Kernel > Restart Kernel** and then rerun all the completed cells in order.\n",
    "\n",
    "### Further reading and practice\n",
    "\n",
    "- Official Python tutorial: [`for` and `range()`](https://docs.python.org/3/tutorial/controlflow.html#for-statements) and [`break` and `continue`](https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements).\n",
    "- W3Schools: [`while` loops](https://www.w3schools.com/python/python_while_loops.asp), [`for` loops](https://www.w3schools.com/python/python_for_loops.asp) and [`range`](https://www.w3schools.com/python/python_range.asp).\n",
    "- Helsinki Python MOOC: [Simple loops](https://programming-26.mooc.fi/part-2/4-simple-loops), [Loops with conditions](https://programming-26.mooc.fi/part-3/1-loops-with-conditions) and [More loops](https://programming-26.mooc.fi/part-3/3-more-loops).\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "772b0cc9-5eee-4a48-9c73-c1bc9ecb3789",
   "metadata": {},
   "source": [
    "## 1. Repeating `while` a condition is true"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c001",
   "metadata": {},
   "source": [
    "If there's one thing computers do well, it's running the same program over and over. To repeat pieces of code, we use structures called **loops**.\n",
    "\n",
    "The first type of loop we'll discuss is called a **`while` loop**. It allows you to define a piece of code that should be repeated as long as a particular condition stays `True`.\n",
    "\n",
    "**Predict:** Trace the `count` variable on paper. How many lines of output will be printed, and what will the value of `count` be after the loop finishes?\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c002",
   "metadata": {},
   "outputs": [],
   "source": [
    "count = 1\n",
    "while count <= 4:\n",
    "    print(f\"Count is {count}\")\n",
    "    count += 1\n",
    "\n",
    "print(f\"Finished at {count}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c003",
   "metadata": {},
   "source": [
    "As you can see, `while` loops look quite similar to `if` statements. Each loop's header contains the keyword and condition, and ends with a colon. Its body is indented.\n",
    "\n",
    "The body of a `while` loop only runs when the condition in the head is `True`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "591a7545-d993-4bb8-abc8-4dbb562808fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "is_ready = False\n",
    "\n",
    "while is_ready:\n",
    "    print(\"This line is part of the loop's body, so will only run if the condition is True\")\n",
    "\n",
    "print(\"This line is not indented, so will be printed regardless\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b3e936d-4d6e-41e5-9f14-48066460d49c",
   "metadata": {},
   "source": [
    "A `while` loop **must** allow its condition to, at some point, become `False`. If not, the `while` loop runs forever. This is called an **infinite loop** and as it will keep your computer busy, **Python will get stuck running the loop forever and ever and ever and ever and ever...**\n",
    "\n",
    "<a id=\"infinite-loop\"></a>\n",
    "\n",
    "<div class=\"alert alert-warning\">\n",
    "<b>Watch out: infinite loops</b>\n",
    "\n",
    "The next cell shows an example of an infinite loop. ***Edit it at your own risk!***\n",
    "If you are running on Noteable, the execution will be stopped after a certain number of iterations to prevent crashing the server.\n",
    "You can always stop the program yourself by clicking on Kernel > Interrupt Kernel in the menu bar at the top of the screen.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ad8c25a6-66df-46a8-8f95-21155b257dd8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# If you want to see an infinite loop,\n",
    "# first make sure you have read the warning cell above,\n",
    "# then unindent the last line of the loop body below\n",
    "count = 1\n",
    "while count < 10:\n",
    "    print(f\"Count is {count}\")\n",
    "    count += 1 # Unindent, if you dare!\n",
    "\n",
    "print(\"A small indentation error can have dire consequences...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf716d47-b605-439f-a2c4-77838a789bc8",
   "metadata": {},
   "source": [
    "Loops can run in any direction. Just remember to always check carefully you are updating your counter variable in the direction you are taking, and are using the correct operator in the `while` condition.\n",
    "\n",
    "**Predict:** Is this loop going to count down from 5 to 1, or from 5 to 0?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c004",
   "metadata": {},
   "outputs": [],
   "source": [
    "fuel = 5\n",
    "while fuel > 0:\n",
    "    print(f\"Using one unit; {fuel} available\")\n",
    "    fuel -= 1\n",
    "print(\"Fuel empty\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c005",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: count down safely</b>\n",
    "\n",
    "Use a `while` loop to print 5, 4, 3, 2, 1, 0, and then `Lift off!`.\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c006",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "countdown = 5\n",
    "# Complete the loop\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c007",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "countdown = 5\n",
    "while countdown >= 0: # We use >= because we want to print the 0 as well\n",
    "    print(countdown)\n",
    "    countdown -= 1  # Guarantees progress towards the stopping condition\n",
    "\n",
    "print(\"Lift off!\") # We only want to print this once at the end, so add it outside of the loop body\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2bca496-6528-49cb-8bb1-ceeecdfff049",
   "metadata": {},
   "source": [
    "## 2. Definite repetition with `range()` and `for`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c747c7f-e197-4be2-9d12-31ce2db1f4f8",
   "metadata": {},
   "source": [
    "`while` loops are useful when repetition depends on a _condition_; Python will simply run it as often as it needs to before the condition becomes `False`.\n",
    "\n",
    "By contrast, a **`for` loop** visits each item from an **iterable**, an object that supplies items _one at a time_. This is particularly useful when dealing with *lists* of data, in which we need to process each one. While we won't discuss how to create lists until the next lecture, we can still explore how `for` loops work with ranges of numbers. \n",
    "\n",
    "Fortunately, Python has a convenient function built-in for producing ranges of integers: `range()`.\n",
    "\n",
    "- `range(stop)` produces integers from 0 up to, but **not including**, `stop`.\n",
    "- `range(start, stop)` produces integers from `start` up to but not including `stop`.\n",
    "- Finally, `range(start, stop, step)` produces integers from `start` up to but not including `stop` taking `step` steps at a time.\n",
    "\n",
    "**Predict:** What do you think the output from each of these three loops is going to be?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c009",
   "metadata": {},
   "outputs": [],
   "source": [
    "for number in range(4):\n",
    "    print(number)\n",
    "\n",
    "print(\"---\")\n",
    "for number in range(2, 6):\n",
    "    print(number)\n",
    "\n",
    "print(\"---\")\n",
    "for number in range(10, 3, -2):\n",
    "    print(number)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c010",
   "metadata": {},
   "source": [
    "As you can see, `for` loops follow a similar syntax to `if` statements and `while` loops, with again a colon at the end of the head and an indented body.\n",
    "\n",
    "The main difference is that a `for` loop does not use a Boolean condition in its head, but instead an expression using the `in` keyword. In this expression, we create a variable that we can refer to inside of our loop for each value that is produced by `range()`.\n",
    "\n",
    "Because `range()` always supplies a finite sequence of integers, using a `for` loop with `range()` does not carry the danger of an infinite loop that a `while` loop has.\n",
    "\n",
    "Note again that, by default, `range()` starts counting at 0 and does **not** include the stop value. This means that you can quickly see from a `range()` call how often it will repeat: for example, `range(5)` repeats exactly 5 times, by producing the numbers 0 through 4."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c2f64b13-0d39-4eaa-b2f8-e347be52c57f",
   "metadata": {},
   "outputs": [],
   "source": [
    "for number in range(5, 1, -1):\n",
    "    print(number)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d87731c9-0f9d-437e-841b-00c0ea7f1d4c",
   "metadata": {},
   "source": [
    "`range()` is far from the only thing a `for` loop can iterate over. As a matter of fact, strings are iterables too!\n",
    "\n",
    "**Predict:** What is going to be the difference between `print(word)` and the following piece of code?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c011",
   "metadata": {},
   "outputs": [],
   "source": [
    "word = \"code\"\n",
    "for character in word:\n",
    "    print(character)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c012",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: choosing the right range</b>\n",
    "\n",
    "First, print the even numbers 2, 4, 6, 8, and 10 with one `range()`. Then, print 3 down to 0.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c013",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# First loop: 2, 4, 6, 8, 10\n",
    "\n",
    "# Second loop: 3, 2, 1, 0\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c014",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for the solution</b></summary>\n",
    "\n",
    "```python\n",
    "# When counting up, the stop value just has to be beyond the last number we want,\n",
    "# so range(2, 11, 2) and range(2, 12, 2) both stop after 10\n",
    "for number in range(2, 11, 2):\n",
    "    print(number)\n",
    "\n",
    "# When counting down, the stop value has to be below the last number we want,\n",
    "# because the stop value itself is never printed\n",
    "for number in range(3, -1, -1):\n",
    "    print(number)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c015",
   "metadata": {},
   "source": [
    "## 3. Accumulator patterns\n",
    "\n",
    "A common way of using loops is to build some result gradually over time, such as a total, a count, or a piece of text. The variable holding that developing result is called an **accumulator**. Accumulators are initialised before loops and updated from inside.\n",
    "\n",
    "**Predict:** Trace `total` after each iteration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c016",
   "metadata": {},
   "outputs": [],
   "source": [
    "total = 0\n",
    "for number in range(1, 5):\n",
    "    total += number\n",
    "    print(f\"After adding {number}, total is {total}\")\n",
    "print(f\"Final total: {total}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c017",
   "metadata": {},
   "source": [
    "Note the importance of initialising `total` *outside* of the loop. If we had written `total = 0` inside the loop, it would have been reset on each iteration, erasing any previous work.\n",
    "\n",
    "Accumulators start to become particularly powerful when we **nest** statements, that is, putting statements inside one another. The following example uses an `if` statement inside of a `for` loop to count all vowels in a word:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c018",
   "metadata": {},
   "outputs": [],
   "source": [
    "vowel_count = 0\n",
    "word = \"algorithm\"\n",
    "\n",
    "for character in word:\n",
    "    if character == \"a\" or character == \"e\" or character == \"i\" or character == \"o\" or character == \"u\":\n",
    "        vowel_count += 1\n",
    "\n",
    "print(vowel_count)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c019",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-info\">\n",
    "<b>Note: indentation in nested statements</b>\n",
    "\n",
    "Look how the level of indentation updates with each statement in the code above. `vowel_count += 1` is the body of the `if` statement, and the `if` statement *as a whole* forms the body of the `for` loop.\n",
    "Each time we go a level deeper, we have to indent one level further.\n",
    "Remember to ***be consistent*** while doing this: if your standard indentation is 4 spaces, then the next level should be 8 spaces, and so on.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9f93258-e199-481a-b4b9-4bfaab43d8dd",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: sum multiples</b>\n",
    "\n",
    "Print the **sum** of all multiples of 3, from 3 through 30, which are divisible by 2. (The correct output is 90.)\n",
    "\n",
    "You'll need to use an accumulator, a `range()`, and a nested `if` statement. If you're not sure how to fit them all together, trace on paper and write pseudocode for what you would expect each step of the way to be.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c020",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "total = 0\n",
    "# Complete the loop and print total\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c021",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for the solution</b></summary>\n",
    "\n",
    "```python\n",
    "# Initialise the accumulator\n",
    "total = 0\n",
    "\n",
    "# Loop over all values from 3 up to but not including 31, stepping 3 at a time\n",
    "for number in range(3, 31, 3):\n",
    "    # If the modulo 2 equals 0, that means it's divisible by 2\n",
    "    if number % 2 == 0:\n",
    "        # Only add these numbers to the total by using correct indentation\n",
    "        total += number\n",
    "\n",
    "print(total)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c022",
   "metadata": {},
   "source": [
    "## 4. Controlling a loop\n",
    "\n",
    "Sometimes a loop needs to stop early, skip one case, or temporarily contain an unfinished branch. Thankfully, Python has functionality for all three.\n",
    "\n",
    "`break` is a keyword that immediately exits the nearest loop. It is useful when, for example, we're trying to find a particular item in a range, and we can stop the loop prematurely when we've found it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c023",
   "metadata": {},
   "outputs": [],
   "source": [
    "for attempt in range(1, 6):\n",
    "    print(f\"Checking attempt {attempt}\")\n",
    "    if attempt == 3:\n",
    "        print(\"Match found\")\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c024",
   "metadata": {},
   "source": [
    "`continue` is a keyword that skips the remainder of the current iteration and begins the next one."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c025",
   "metadata": {},
   "outputs": [],
   "source": [
    "for number in range(1, 8):\n",
    "    if number % 2 == 0:\n",
    "        continue\n",
    "    print(number)  # Only odd numbers reach this line!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c026",
   "metadata": {},
   "source": [
    "`pass` is a keyword that does nothing.\n",
    "\n",
    "Literally - it allows us to put placeholders in our code without Python throwing errors. This is especially useful when using control flow that requires an indented body to work. If you ever want to write a condition but only add its body later, simply chuck in `pass` (and a comment to remind yourself!) and Python will happily ignore it.\n",
    "\n",
    "Note that unlike what the word might suggest, `pass` does **not** skip an iteration! That is done by `continue`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c027",
   "metadata": {},
   "outputs": [],
   "source": [
    "for number in range(3):\n",
    "    if number == 1:\n",
    "        pass  # TODO: finish later\n",
    "    print(number)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c028",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: stop at a sentinel</b>\n",
    "\n",
    "Usually, `while True` will create an infinite loop. But now we have a way to `break` out of it if we have to!\n",
    "\n",
    "Start at 1 and print successive integers in a `while True` loop. Exit the loop using `break` when the value reaches 6, and use `continue` to skip the number 3.\n",
    "\n",
    "***This is dangerous territory***: one wrong line and you could inadvertently trigger an infinite loop. Don't worry, happens even to the best, just make sure you have read the [infinite loop box in Section 1](#infinite-loop) before getting started with this task!\n",
    " </div>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c029",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "number = 0\n",
    "\n",
    "while True:\n",
    "    pass # TODO: complete the loop per the challenge above"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c030",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for the solution</b></summary>\n",
    "\n",
    "Note that we update the counter before the checks. If the update were after `continue`, reaching 3 could skip it on every following iteration, preventing further progress.\n",
    "\n",
    "```python\n",
    "number = 0\n",
    "\n",
    "while True:\n",
    "    number += 1\n",
    "    if number == 6:\n",
    "        break\n",
    "    if number == 3:\n",
    "        continue\n",
    "    print(number)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c031",
   "metadata": {},
   "source": [
    "## 5. Nested loops\n",
    "\n",
    "You can not only nest `if` statements in loops. You can also nest loops in loops!\n",
    "\n",
    "In a nested loop, the inner loop completes all its iterations for every *one* iteration of the outer loop.\n",
    "\n",
    "**Predict:** How many coordinate pairs are printed?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c032",
   "metadata": {},
   "outputs": [],
   "source": [
    "for row in range(1, 3):\n",
    "    for column in range(1, 4):\n",
    "        print(f\"({row}, {column})\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "966ae7e7-63ea-4ece-b9f0-8cc1ae46c3cd",
   "metadata": {},
   "source": [
    "**Predict:** This example is more abstract. Can you trace what its output is going to be?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c033",
   "metadata": {},
   "outputs": [],
   "source": [
    "for row in range(1, 4):\n",
    "    line = \"\"\n",
    "    for column in range(1, 5):\n",
    "        line += \"#\"\n",
    "    print(line)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c035",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: make a grid</b>\n",
    "\n",
    "Use nested loops to print three lines, each containing `1 2 3 4`. Build each line as a string before printing it.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-c036",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# your solution here!\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-c037",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details>\n",
    "<summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "for row in range(3):\n",
    "    line = \"\"\n",
    "    for number in range(1, 5):\n",
    "        line += str(number) + \" \"\n",
    "    print(line)\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-consolidation-prompt",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "source": [
    "## Consolidation challenge\n",
    "\n",
    "Let's try out the different techniques we've discussed in this notebook in one program. Remember, notepads and pseudocode are your friends when decomposing a problem into its different parts.\n",
    "\n",
    "<div class=\"alert alert-success\">\n",
    "<b>Try it yourself: multiplication tables</b>\n",
    "\n",
    "Print a 1–5 multiplication table, that is, all products from `1 × 1` to `5 × 5`, with the following requirements: \n",
    "- Each line should have the form `4 × 3 = 12`;\n",
    "- Insert a blank line between the tables for each first factor;\n",
    "- Skip over each product where the second factor is `2`.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "l04-consolidation-work",
   "metadata": {
    "tags": [
     "exercise"
    ]
   },
   "outputs": [],
   "source": [
    "# Your solution here!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-consolidation-solution",
   "metadata": {
    "tags": [
     "solution"
    ]
   },
   "source": [
    "<details><summary><b>Click for a possible solution</b></summary>\n",
    "\n",
    "```python\n",
    "for first in range(1, 6):\n",
    "    for second in range(1, 6):\n",
    "        if second == 2:\n",
    "            continue\n",
    "        print(f\"{first} × {second} = {first * second}\")\n",
    "    print(\"\")\n",
    "```\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "l04-consolidation-summary",
   "metadata": {
    "tags": [
     "summary"
    ]
   },
   "source": [
    "## Summary\n",
    "\n",
    "Well done! We're rapidly advancing through programming concepts; hopefully you're not feeling too... loopy after all this repetition.\n",
    "\n",
    "### Key terms\n",
    "\n",
    "<dl>\n",
    "\t<dt><b>Iteration</b></dt>\n",
    "\t<dd>The act of repeating a process.</dd>\n",
    "\t<dt><b>Loop</b></dt>\n",
    "\t<dd>A control flow structure that repeats code according to a particular condition</dd>\n",
    "\t<dt><b>Iterable</b></dt>\n",
    "\t<dd>An object that can be iterated over, such as a list, string, or <code>range()</code>.</dd>\n",
    "\t<dt><b>Accumulator</b></dt>\n",
    "\t<dd>A variable whose value is gradually constructed over the course of multiple iterations of a loop.</dd>\n",
    "\t<dt><b>Nesting</b></dt>\n",
    "\t<dd>Putting multiple control flow structures inside of one another.</dd>\n",
    "\t<dt><b>Infinite loop</b></dt>\n",
    "\t<dd>A loop that never ends, often completely consuming or even crashing the program that runs it.</dd>\n",
    "</dl>\n",
    "\n",
    "### New syntax\n",
    "\n",
    "<dl>\n",
    "\t<dt><code>while</code></dt>\n",
    "\t<dd>A loop that repeats its body while the condition in its header is true (unless its body exits early with <code>break</code>).</dd>\n",
    "\t<dt><code>for</code></dt>\n",
    "\t<dd>A loop that runs its body once for each item supplied by an iterable.</dd>\n",
    "\t<dt><code>in</code></dt>\n",
    "\t<dd>A keyword used in the head of a <code>for</code> loop to iterate through a sequence.</dd>\n",
    "\t<dt><code>range(start, stop, step)</code></dt>\n",
    "\t<dd>A function that generates integers from <code>start</code> up to but not including <code>stop</code>, taking <code>step</code> steps at a time. When not provided, <code>start</code> defaults to 0 and <code>step</code> to 1.</dd>\n",
    "\t<dt><code>break</code></dt>\n",
    "\t<dd>A keyword that immediately exits the nearest enclosing loop.</dd>\n",
    "\t<dt><code>continue</code></dt>\n",
    "\t<dd>A keyword used to skip the rest of the current iteration of a loop and begin the next one.</dd>\n",
    "\t<dt><code>pass</code></dt>\n",
    "\t<dd>A keyword that does nothing and can be used as a placeholder for an unfinished body. It does <i>not</i> skip an iteration; that's <code>continue</code>!</dd>\n",
    "</dl>\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
