{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d5130508-dc03-4364-a64c-18147e075924",
   "metadata": {},
   "source": [
    "# Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9daed68-806a-49a7-ace0-3b783895cf2d",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "current = Path.cwd()\n",
    "for parent in [current, *current.parents]:\n",
    "    if (parent / '_config.yml').exists():\n",
    "        project_root = parent  # ← Add project root, not chapters\n",
    "        break\n",
    "else:\n",
    "    project_root = Path.cwd().parent.parent\n",
    "\n",
    "sys.path.insert(0, str(project_root))\n",
    "\n",
    "from shared import thinkpython, diagram, jupyturtle\n",
    "\n",
    "# Register as top-level module so 'import jupyturtle' and\n",
    "# 'from jupyturtle import ...' work in subsequent cells\n",
    "sys.modules['jupyturtle'] = jupyturtle\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4220e939-273e-481c-a50e-8f7e57e7380f",
   "metadata": {},
   "source": [
    "**Why custom functions?**\n",
    "\n",
    "The real power of programming comes from creating your own **functions**. A function is a **reusable** block of code that performs a specific task. Functions help you organize code, avoid repetition, and make programs easier to understand and **maintain**. For example, you write a function and (re)use it everywhere in your project, and when you need to modify the function, you only need to modify it in one place. \n",
    "\n",
    "It may not be clear yet why it is worth the trouble to divide a program into functions. There are several reasons:\n",
    "\n",
    "-   Creating a new function gives you an opportunity to name a **group of statements**, which makes your program easier to read and debug.\n",
    "-   Functions can make a program smaller by **eliminating repetitive** code.\n",
    "-   If you make a **change** to the code, with a function, you only have to make it in **one place**.\n",
    "-   Dividing a long program into functions allows you to debug the parts\n",
    "    one at a time as a **module** and then assemble them into a working whole.\n",
    "-   Well-designed functions are often useful for many programs. Once you write and debug one, you can **reuse** it."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1ab3d56-8c96-4427-ac3c-dbcf27e52c07",
   "metadata": {},
   "source": [
    "While there are plenty of Python built-in functions and modules, we still need to learn to design our own custom functions to perform problem-specific tasks, whereas the functions shipped with Python are general-purpose tools. The comparison below of built-ins and custom functions should give you a clear rationale for learning to build custom functions. \n",
    "\n",
    "| Feature                  | Built-in Functions | Custom Functions |\n",
    "| ------------------------ | ------------------ | ---------------- |\n",
    "| Who defines them         | Python             | You              |\n",
    "| Need `def`               | No                 | Yes              |\n",
    "| Always available         | Yes                | No               |\n",
    "| Reusable                 | Yes                | Yes              |\n",
    "| Problem-specific         | **No**             | **Yes**              |\n",
    "| Can call other functions | No                 | **Yes**          |\n",
    "| Can accept defaults      | Limited            | Yes              |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1465723-c652-4e6a-9916-0753894a3981",
   "metadata": {},
   "source": [
    "**Before we officially learn about functions, let us play with the Python `turtle` module.**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "679e387f-7076-4211-b186-89994ed0fbbe",
   "metadata": {},
   "source": [
    "## `jupyturtle`\n",
    "\n",
    "Python [Turtle graphics](https://docs.python.org/3/library/turtle.html) is an old module (created in 1967!) that has been used for decades for learning Python. [Luciano Ramalho](https://github.com/ramalho/jupyturtle) ported it to Jupyter Notebook while reviewing Allen Downey's [Python book](https://allendowney.github.io/ThinkPython). Turtle is fun to use and great for learning Python programming. Here we are using it to learn functions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f29ca8c-0125-49a6-ae20-aee5de630607",
   "metadata": {},
   "source": [
    "To use `jupyturtle`, you need to install and import the module.\n",
    "\n",
    "```python\n",
    "%pip install jupyturtle    ### comment it out after installation\n",
    "import jupyturtle\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 136,
   "id": "3bd6bed9-0267-4b72-9c52-7ed63c740995",
   "metadata": {},
   "outputs": [],
   "source": [
    "# %pip install jupyturtle\n",
    "import jupyturtle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 137,
   "id": "670dccc4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "\n",
       "<g transform=\"rotate(-90.0,150,75) translate(150, 75)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<jupyturtle.Turtle at 0x111e0d860>"
      ]
     },
     "execution_count": 137,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "jupyturtle.make_turtle()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33c5f1a8-d80c-4fbd-bda1-dca2978f39f3",
   "metadata": {},
   "source": [
    "`make_turtle` creates a **canvas**, which is a space on the screen where we can draw, and a turtle, which is represented by a circular shell and a triangular head.\n",
    "The circle shows the turtle's location, and the triangle indicates the direction it is facing."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19daba99-a1e7-42fd-9c2e-b9c95236ad4e",
   "metadata": {},
   "source": [
    "Now we can use the functions defined in the module, like `make_turtle` and `forward`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 138,
   "id": "d6307016-173a-49e2-a079-4b9071265c52",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"250.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,250.0,75.0) translate(250.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "jupyturtle.make_turtle()       ### create a new canvas\n",
    "jupyturtle.forward(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "021163df-0ba5-4512-b15c-62d51d54a0a6",
   "metadata": {},
   "source": [
    "`forward` moves the turtle a given distance in the direction it's facing, drawing a line segment along the way.\n",
    "The distance is in arbitrary units -- the actual size depends on your computer's screen."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6e2948e-3ff2-4678-90b6-db8d9d2bdf85",
   "metadata": {},
   "source": [
    "We will use functions defined in the `jupyturtle` module many times, so it would be nice if we didn't have to write the module name every time. That’s possible if we import the module like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 139,
   "id": "53ee2996-aab0-48bd-94ef-dc6490019a8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from jupyturtle import make_turtle, forward"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12bb71a9-7973-453a-a280-4e4a727c07d0",
   "metadata": {},
   "source": [
    "Note:\n",
    "\n",
    "To check out the **functions** available in `jupyturtle`, use the **dot** operator (press the `Tab` keys after `t` to see."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d6fbae6-cd0d-487b-a733-87b28874975b",
   "metadata": {},
   "source": [
    "This version of the import statement imports `make_turtle` and `forward` from the `jupyturtle` module, so we can call them like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 140,
   "id": "4a4ac280-eefb-4234-91f1-70a798c3d5cb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"250.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,250.0,75.0) translate(250.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "forward(100)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d228170-336f-4cc0-bfc1-be48d6b3dffa",
   "metadata": {},
   "source": [
    "`jupyturtle` provides two other functions we’ll use, called `left` and `right`. We’ll import them like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 141,
   "id": "2b8b46e0-4c0a-49bb-86e8-f8cdf90a1578",
   "metadata": {},
   "outputs": [],
   "source": [
    "from jupyturtle import left, right"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53868db7-be91-496e-a835-087edb9e449b",
   "metadata": {},
   "source": [
    "`left` causes the turtle to turn left. It takes one argument, which is the angle of the turn in degrees. For example, we can make a 90-degree left turn like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 142,
   "id": "dc79435a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(180.0,200.0,25.0) translate(200.0, 25.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "forward(50)\n",
    "left(90)\n",
    "forward(50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "396b11ef-e1b1-4527-a022-bb5c217f9cb9",
   "metadata": {},
   "source": [
    "jupyturtle provides two other functions we’ll use, called left and right. We’ll import them like this."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3cdade54-91c4-429a-be3b-0e7642fe9a87",
   "metadata": {},
   "source": [
    "This program moves the turtle east and then north, leaving two line segments behind.\n",
    "Before you go on, see if you can modify the previous program to make a square."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0e0fb1a-bdd0-400f-b039-1ff40e3a3c25",
   "metadata": {},
   "source": [
    "### Making a square\n",
    "\n",
    "Here's one way to make a square."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "id": "39deadac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"25.0\" x2=\"150.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"25.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "\n",
    "forward(50)\n",
    "left(90)\n",
    "forward(50)\n",
    "left(90)\n",
    "forward(50)\n",
    "left(90)\n",
    "forward(50)\n",
    "left(90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "577b8d98-4a77-4896-a541-978c74834d88",
   "metadata": {},
   "source": [
    "Because this program repeats the same pair of lines four times, we can do the same thing more concisely with a `for` loop."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 144,
   "id": "d9831709",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"25.0\" x2=\"150.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"25.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()\n",
    "for i in range(4):\n",
    "    forward(50)\n",
    "    left(90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e83d168-3ec2-4776-825a-67d624990c0b",
   "metadata": {},
   "source": [
    "### Encapsulation\n",
    "\n",
    "Let’s take the square-drawing code from the previous section and put it in a function called `square`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 145,
   "id": "cf58933e-402c-4941-bf37-903170c2cedf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def square():\n",
    "    for i in range(4):\n",
    "        forward(50)\n",
    "        left(90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad9c7d33-557e-46ee-a7c0-14055f58aa93",
   "metadata": {},
   "source": [
    "Now we can call the function like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "id": "5ff18e3e-b3d5-45ae-922f-a5c9aafee3d0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"25.0\" x2=\"150.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"25.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "make_turtle()     ### create a canvas object\n",
    "square()          ### call a custom function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 147,
   "id": "6dd35207-12a3-467f-93fe-cf1035298538",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### Exercise 1: Polygon\n",
    "### Try producing the same output as the cell below.\n",
    "### For now, use forward(50).\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "id": "f6da50af-4871-433f-9588-3be411d44099",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"175.0\" y2=\"31.7\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"175.0\" y1=\"31.7\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"200.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"25.0\" x2=\"150.0\" y2=\"25.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"150.0\" y1=\"25.0\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<svg width=\"300\" height=\"150\">\n",
       "    <rect width=\"100%\" height=\"100%\" fill=\"#F3F3F7\" />\n",
       "\n",
       "<line x1=\"150\" y1=\"75\" x2=\"200.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"200.0\" y1=\"75.0\" x2=\"215.5\" y2=\"27.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"215.5\" y1=\"27.4\" x2=\"175.0\" y2=\"-1.9\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"175.0\" y1=\"-1.9\" x2=\"134.5\" y2=\"27.4\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "<line x1=\"134.5\" y1=\"27.4\" x2=\"150.0\" y2=\"75.0\"\n",
       "    stroke=\"#663399\" stroke-width=\"2\" />\n",
       "\n",
       "<g transform=\"rotate(-90.0,150.0,75.0) translate(150.0, 75.0)\">\n",
       "    <circle stroke=\"#63A375\" stroke-width=\"2\" fill=\"transparent\" r=\"5.5\" cx=\"0\" cy=\"0\"/>\n",
       "    <polygon points=\"0,12 2,9 -2,9\" style=\"fill:#63A375;stroke:#63A375;stroke-width:2\"/>\n",
       "</g>\n",
       "\n",
       "</svg>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def polygon(n):\n",
    "    jupyturtle.make_turtle()\n",
    "    \n",
    "    angle = 360/n\n",
    "    for i in range(n):\n",
    "        jupyturtle.forward(50)\n",
    "        jupyturtle.left(angle)\n",
    "\n",
    "polygon(3)\n",
    "polygon(4)\n",
    "polygon(5)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0edea812-080b-4d86-a8aa-531047b7014c",
   "metadata": {},
   "source": [
    "## Defining Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "052682f9-eecc-4d31-bd2c-ac77d27f33af",
   "metadata": {},
   "source": [
    "A **function definition** specifies the name of a new function and the sequence of statements that run when the function is called.\n",
    "\n",
    "The syntax of a Python function is:\n",
    "\n",
    "```python\n",
    "def function_name([parameters]):\n",
    "    \"\"\"\n",
    "    Docstring describing what the function does.\n",
    "    \"\"\"\n",
    "    [function body]            \n",
    "    [return value]     ### optional\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ebe725d2-334a-4035-8fc0-6fe923ef04ed",
   "metadata": {},
   "source": [
    "We use the `def` keyword to define a function. The `def` line is called a header, and the rest of the lines are the `body`. The elements here are:\n",
    "\n",
    "- Header:\n",
    "    1. **`def`**: Keyword that starts a function definition\n",
    "    2. **Function name**: Follows variable naming rules (snake_case)\n",
    "    3. **Parameters**: Input values (optional)\n",
    "    4. **Colon**\n",
    "- Body\n",
    "    1. Indentation (4 as recommended by PEP8)\n",
    "    2. **Docstring**: Documentation string (recommended)\n",
    "    3. **Function body**: **Indented** (instead of `{}`) code that runs when the function is called\n",
    "    4. **`return`**: Sends a value back to the **caller** (optional, but almost always have)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 149,
   "id": "9e8aef50",
   "metadata": {},
   "outputs": [],
   "source": [
    "def greet(name):\n",
    "    \"\"\"\n",
    "    Print a greeting message to the user with their name.\n",
    "    \n",
    "    Parameters:\n",
    "    name (str): The name of the user to greet.\n",
    "    \n",
    "    Returns:\n",
    "    None\n",
    "    \"\"\"\n",
    "    print(f\"Hello, {name}!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc67a1a0-7a23-4d98-84c7-7c2fc5d95bde",
   "metadata": {},
   "source": [
    "### Parameters and Arguments\n",
    "\n",
    "With the elements in mind, we can already play with different patterns of functions. Let's start with **parameters**. Python functions can have several types of parameters and arguments:\n",
    "\n",
    "In the function definition:\n",
    "  1. **Positional** Parameters: These are matched to arguments based on their **position** in the function definition.\n",
    "  2. **Default** Parameters: These have a **default value** that's used if no argument is provided.\n",
    "     | Type           | Matched By | Order Matters? | Can Use Keyword?               |\n",
    "| -------------- | ---------- | -------------- | ------------------------------ |\n",
    "| Positional     | Order      | Yes       | Yes  |\n",
    "| Keyword        | Name       | No (must be after positional)       | Yes       |\n",
    "\n",
    "  3. **\\*args** (Variable Positional Arguments): Allows a function to accept any number of positional arguments as a **tuple**. Note that *args itself is a name by convention.\n",
    "  4. **\\*\\*kwargs** (Variable Keyword Arguments): Allows a function to accept any number of keyword arguments as a **dictionary**. Note that **kwargs itself is a name by convention.\n",
    "     \n",
    "In a function call:\n",
    "  1. Keyword-Only Parameters: Parameters that can only be passed by **name**, not by position. They come after *args or a single **\\***.\n",
    "  2. **Keyword** Arguments: You can pass arguments by specifying the parameter name, which allows any order.\n",
    "\n",
    "Note that **positional** parameter/arguments must come before **keyword** parameter/arguments. The full precedence order is: **positional => \\*args => keyword-only => \\*\\*kwargs**. Once you start naming arguments (e.g., age=35), everything after that must also be named.\n",
    "\n",
    "The following example should make the ideas clear:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5acfda37-f3eb-4be1-878d-b51f1c2573bf",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "Now, some simpler patterns and examples:\n",
    "\n",
    "- no parameter\n",
    "- one parameter\n",
    "- multiple parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 150,
   "id": "4727bbdf-189d-4c05-9c40-e1b200dbd202",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello!\n"
     ]
    }
   ],
   "source": [
    "### 1. No Parameter\n",
    "def greet():\n",
    "    \"\"\"Print a greeting message.\"\"\"\n",
    "    print(\"Hello!\")\n",
    "\n",
    "greet()     ### function call output: Hello, World!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 151,
   "id": "c664ca1a-c1dc-4bc0-b111-dae6b005ee7c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Alice!\n",
      "Hello, Bob!\n"
     ]
    }
   ],
   "source": [
    "### 2. Single Parameters**\n",
    "### Parameters allow functions to work with different inputs:\n",
    "def greet_person(name):\n",
    "    \"\"\"Greet a person by name.\"\"\"\n",
    "    print(f\"Hello, {name}!\")\n",
    "\n",
    "greet_person(\"Alice\")  # Output: Hello, Alice!\n",
    "greet_person(\"Bob\")    # Output: Hello, Bob!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 152,
   "id": "e5766f59-9719-4f40-abc2-540084349dcb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8\n"
     ]
    }
   ],
   "source": [
    "### 3. Multiple Parameters**\n",
    "def add_numbers(a, b):\n",
    "    \"\"\"Add two numbers and return the result.\"\"\"\n",
    "    result = a + b\n",
    "    return result\n",
    "\n",
    "total = add_numbers(5, 3)      # Returns 8\n",
    "print(total)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08993348-6b68-43d1-a6b8-78322f9f08a1",
   "metadata": {},
   "source": [
    "### Default Parameters\n",
    "\n",
    "A default parameter has a default value. Parameters can have default values, and they can be **overwritten** by arguments.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "id": "6f864791-174b-4347-be51-451e1e6582a2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Alice!\n",
      "Hi, Bob!\n",
      "Hey, Charlie!\n"
     ]
    }
   ],
   "source": [
    "def greet(name, greeting=\"Hello\"):\n",
    "    \"\"\"Greet with a customizable greeting.\"\"\"\n",
    "    return f\"{greeting}, {name}!\"\n",
    "\n",
    "print(greet(\"Alice\"))              # Hello, Alice!   ### default is \"Hello\"\n",
    "print(greet(\"Bob\", \"Hi\"))          # Hi, Bob!        ### now it's Hi\n",
    "print(greet(\"Charlie\", \"Hey\"))     # Hey, Charlie!   ### now it's Hey"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "id": "ex2-q-b6d649e6",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### === EXERCISE 2: Functions with Default Parameters ===\n",
    "### Write a function called greet_with_title() that takes two parameters:\n",
    "### - name: a person's name\n",
    "### - title: a title with a default value of \"Doctor\"\n",
    "### The function should print: \"Hello, Doctor Alice!\"\n",
    "### Call it twice: once with and once without the title parameter.\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code stops here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "id": "ex2-a-39d6d3a3",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Doctor Alice!\n",
      "Hello, Professor Bob!\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def greet_with_title(name, title=\"Doctor\"):\n",
    "    print(f\"Hello, {title} {name}!\")\n",
    "\n",
    "### Call with and without the title parameter\n",
    "greet_with_title(\"Alice\")\n",
    "greet_with_title(\"Bob\", \"Professor\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "id": "060c82e7-7317-4da2-add9-5d56d9d3cd38",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "5 squared: 25\n",
      "5 cubed: 125\n",
      "2 to the 8th: 256\n"
     ]
    }
   ],
   "source": [
    "def power(base, exponent=2):\n",
    "    \"\"\"Calculate base raised to exponent (default is 2).\"\"\"\n",
    "    return base ** exponent\n",
    "\n",
    "print(f\"\\n5 squared: {power(5)}\")        # Uses default exponent=2\n",
    "print(f\"5 cubed: {power(5, 3)}\")         # Overrides default\n",
    "print(f\"2 to the 8th: {power(2, 8)}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 157,
   "id": "033dd2cf",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi, I'm Alice and I am 25 years old\n",
      "Hi, I'm Bob and I am 30 years old\n",
      "Hi, I'm Charlie and I am 22 years old\n"
     ]
    }
   ],
   "source": [
    "%%expect NameError\n",
    "### === EXERCISE 3: Functions with Parameters ===\n",
    "### Write a function called introduce() that takes two parameters: name and age\n",
    "### The function should print a message like: \"Hi, I'm Alice and I am 25 years old\"\n",
    "### Then call introduce() at least twice with different values.\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "introduce(\"Alice\", 25)\n",
    "introduce(\"Bob\", 30)\n",
    "introduce(\"Charlie\", 22)\n",
    "### Your code stops here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "id": "2d0ace0b",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi, I'm Alice and I am 25 years old\n",
      "Hi, I'm Bob and I am 30 years old\n",
      "Hi, I'm Charlie and I am 22 years old\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def introduce(name, age):\n",
    "    print(f\"Hi, I'm {name} and I am {age} years old\")\n",
    "\n",
    "### Test the function with different values\n",
    "introduce(\"Alice\", 25)\n",
    "introduce(\"Bob\", 30)\n",
    "introduce(\"Charlie\", 22)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9c32727-2d83-4379-b317-63d239a63b53",
   "metadata": {},
   "source": [
    "### *`args` and **`kwargs`\n",
    "\n",
    "In Python, \\*args and \\*\\*kwargs are special syntax used to pass a variable number of arguments to a function. The names args and kwargs are conventional but not mandatory; the single (*) and double (**) asterisks are the key elements that enable this functionality. `*` means “many values”, while `**` means “many **key–value pairs**”."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "c5b90d59-443d-4011-82e4-d5e362633b8d",
   "metadata": {},
   "source": [
    "The **\\*args**: The *args syntax allows a function to accept any number of non-keyword (positional) arguments. Inside the function, these arguments are collected into a `tuple`. \n",
    "\n",
    "The **\\*\\*kwargs** (Arbitrary Keyword Arguments) syntax allows a function to accept any number of keyword arguments (arguments in the format key=value). Inside the function, these arguments are collected into a `dictionary`. \n",
    "\n",
    "Together, `*args` and `**kwargs` extend the basic parameter patterns:\n",
    "- regular positional parameters (like `name`)\n",
    "- parameters with default values (like `greeting=\"Hello\"`)\n",
    "- multiple parameters (like `a, b`) that can now be followed by `*args` and `**kwargs` for extra values."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e188617c-e66e-4392-add3-813487ee92b8",
   "metadata": {},
   "source": [
    "*args collects extra positional arguments into a tuple, while **kwargs collects extra keyword arguments into a dictionary. As a comparison:\n",
    "\n",
    "| Feature       | `*args`              | `**kwargs`        |\n",
    "| ------------- | -------------------- | ----------------- |\n",
    "| Accepts       | Positional arguments | Keyword arguments |\n",
    "| Stored as     | Tuple                | Dictionary        |\n",
    "| Order matters | Yes                  | No                |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "id": "08ba7325",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "required = 10\n",
      "default = 20\n",
      "args    = (30, 40)\n",
      "kwargs  = {'mode': 'fast', 'debug': True}\n"
     ]
    }
   ],
   "source": [
    "def describe_order(required, default=0, *args, **kwargs):\n",
    "    \"\"\"Mixes normal, default, *args, and **kwargs to show ordering.\"\"\"\n",
    "    print(f'required = {required}')\n",
    "    print(f'default = {default}')\n",
    "    print(f'args    = {args}')\n",
    "    print(f'kwargs  = {kwargs}')\n",
    "\n",
    "describe_order(10, 20, 30, 40, mode='fast', debug=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "id": "044196ee-b3bc-4886-87df-cc450e8bb1bb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'>\n",
      "6\n"
     ]
    }
   ],
   "source": [
    "def sum_all(*args):\n",
    "    \"\"\"Calculates the sum of an arbitrary number of arguments.\"\"\"\n",
    "    print(type(args))           ### testing; tuple\n",
    "    return sum(args)            ### variable name: args\n",
    "\n",
    "print(sum_all(1, 2, 3))         # 6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "id": "33e2a1e9-89c8-4953-8ba9-2942a4298793",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: John\n",
      "age: 30\n",
      "city: New York\n"
     ]
    }
   ],
   "source": [
    "def print_details(**kwargs):\n",
    "    \"\"\"Prints the key-value pairs of the provided details.\"\"\"\n",
    "    for key, value in kwargs.items():\n",
    "        # print(type(kwargs))    ### checking type\n",
    "        print(f\"{key}: {value}\")\n",
    "\n",
    "print_details(name=\"John\", age=30, city=\"New York\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 162,
   "id": "kwargs-ex-q-bfb7af9c",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### === EXERCISE 4: Write the add_numbers() function\n",
    "### Write a function called add_numbers(*args) that:\n",
    "### Takes any number of numeric arguments using *args\n",
    "### Prints how many numbers were passed in\n",
    "### Returns the sum of all the numbers\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code stops here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "id": "kwargs-ex-a-42a054b4",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Number of values passed: 4\n",
      "Result: 100\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def add_numbers(*args):\n",
    "    \"\"\"Takes any number of arguments and returns their sum\"\"\"\n",
    "    print(f\"Number of values passed: {len(args)}\")\n",
    "    total = sum(args)\n",
    "    return total\n",
    "\n",
    "\n",
    "# Test it\n",
    "result = add_numbers(10, 20, 30, 40)\n",
    "print(f\"Result: {result}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "c0f7783c-dca2-4ca4-85eb-5d9f4b9e3e4e",
   "metadata": {},
   "source": [
    "### Return Value\n",
    "\n",
    "Functions can return values using the `return` statement. As you have probably noticed, we usually store return values in a variable for later use. Note that:\n",
    "\n",
    "- A function without `return` returns `None`\n",
    "- You can **return multiple values**: `return x, y, z`, but still one thing: a **`tuple`**\n",
    "- `return` immediately exits the function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "id": "b2abcb7f-a9bc-4297-ba96-901bf888c749",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Area: 15\n",
      "Area: 15\n"
     ]
    }
   ],
   "source": [
    "def calculate_area(length, width):\n",
    "    \"\"\"Calculate the area of a rectangle.\"\"\"\n",
    "    area = length * width\n",
    "    return area\n",
    "\n",
    "print(f\"Area: {calculate_area(5, 3)}\")    ### Area: 15\n",
    "\n",
    "### or you may do\n",
    "\n",
    "rect_area = calculate_area(5, 3)\n",
    "print(f\"Area: {rect_area}\")               ### Area: 15"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "id": "ex4-q-ebb5f51f",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### === EXERCISE 5: Functions with Return Values ===\n",
    "### Write a function called add_tax() that takes two parameters:\n",
    "### - price: the original price\n",
    "### - tax_rate: the tax rate as a decimal (e.g., 0.08 for 8%)\n",
    "### The function should calculate and return the total price including tax\n",
    "### Then call the function and print results for different prices.\n",
    "\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code stops here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "id": "ex4-a-b38cf9e3",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Price: $100, Tax Rate: 8%, Total: $108.0\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def add_tax(price, tax_rate):\n",
    "    tax_amount = price * tax_rate\n",
    "    return price + tax_amount\n",
    "\n",
    "### Test with different prices\n",
    "total1 = add_tax(100, 0.08)\n",
    "print(f\"Price: $100, Tax Rate: 8%, Total: ${total1}\")\n",
    "\n",
    "total2 = add_tax(50, 0.1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 167,
   "id": "ac0aa587-3cd9-45bb-9003-60f9727ed20a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Total: 15\n"
     ]
    }
   ],
   "source": [
    "def add_numbers(x, y):\n",
    "    \"\"\"Add two numbers and return the sum.\"\"\"\n",
    "    total = x + y\n",
    "    return total\n",
    "\n",
    "result = add_numbers(10, 5)\n",
    "print(f\"\\nTotal: {result}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Function Type Annotations\n",
    "A function can include **type annotations** for its parameters and return value. The parameter annotation goes after the parameter name, and the return annotation goes after `->` in the function header.\n",
    "\n",
    "```python\n",
    "def function_name(parameter: type) -> return_type:\n",
    "    ...\n",
    "```\n",
    "\n",
    "These annotations are a lightweight contract. They tell readers and tools what kinds of values the function expects and returns, but Python does not enforce them automatically at runtime.\n",
    "\n"
   ],
   "id": "type-hints-01"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def greet(name: str) -> str:\n",
    "    return f\"Hello, {name}\"\n",
    "\n",
    "message = greet(\"Ada\")\n",
    "print(message)\n"
   ],
   "id": "type-hints-02"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def area_rectangle(width: float, height: float) -> float:\n",
    "    return width * height\n",
    "\n",
    "print(area_rectangle(3.0, 4.5))\n"
   ],
   "id": "type-hints-03"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Use `-> None` when a function performs an action but does not return a useful value. This is common for functions whose main job is printing, drawing, saving a file, or modifying an object.\n",
    "\n"
   ],
   "id": "type-hints-04"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_receipt(total: float) -> None:\n",
    "    print(f\"Total: ${total:.2f}\")\n",
    "\n",
    "result = print_receipt(12.5)\n",
    "print(result)\n"
   ],
   "id": "type-hints-05"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A function with no `return` statement still returns `None`; the annotation simply makes that intention explicit.\n",
    "\n"
   ],
   "id": "type-hints-06"
  },
  {
   "cell_type": "markdown",
   "id": "6e2e5fea-497c-4e08-9169-5f7fad84329d",
   "metadata": {},
   "source": [
    "### Unpacking\n",
    "\n",
    "Taking elements out of a collection (tuple, list, dict) and passing them into individual parameters.\n",
    "\n",
    "Observe the multiple return values of the following example. It has two values enclosed in parentheses, which form a `tuple`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 168,
   "id": "5ff2db53-393e-4316-a1df-aed00021a749",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 2)"
      ]
     },
     "execution_count": 168,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def func(a, b):\n",
    "    return a, b\n",
    "\n",
    "func(1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79be316a",
   "metadata": {},
   "source": [
    "Here we see that a tuple is returned, and we unpack the tuple into the variables when returned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "da292e70",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Return type: <class 'tuple'>\n",
      "Stats for [10, 20, 30, 40, 50]:\n",
      "Min: 10, Max: 50, Average: 30.0\n"
     ]
    }
   ],
   "source": [
    "def get_stats(numbers):\n",
    "    \"\"\"Return min, max, and average of a list.\"\"\"\n",
    "    minimum = min(numbers)\n",
    "    maximum = max(numbers)\n",
    "    average = sum(numbers) / len(numbers)\n",
    "    return minimum, maximum, average\n",
    "\n",
    "data = [10, 20, 30, 40, 50]                     ### arguments\n",
    "min_val, max_val, avg_val = get_stats(data)     ### returned and \"unpacking\"\n",
    "\n",
    "print(f\"Return type: {type(get_stats(data))}\")\n",
    "print(f\"Stats for {data}:\")\n",
    "print(f\"Min: {min_val}, Max: {max_val}, Average: {avg_val}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 170,
   "id": "ex5-q-3667b782",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### === EXERCISE 6: Functions Returning Multiple Values ===\n",
    "### Write a function called rectangle_stats() that takes two parameters:\n",
    "### - length: the length of a rectangle\n",
    "### - width: the width of a rectangle\n",
    "### The function should calculate and return both the area and perimeter\n",
    "### Then call the function and unpack the results.\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code stops here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 171,
   "id": "ex5-a-102182eb",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Rectangle (5x3): Area = 15, Perimeter = 16\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def rectangle_stats(length, width):\n",
    "    area = length * width\n",
    "    perimeter = 2 * (length + width)\n",
    "    return area, perimeter\n",
    "\n",
    "### Call and unpack the results\n",
    "area, perim = rectangle_stats(5, 3)\n",
    "print(f\"Rectangle (5x3): Area = {area}, Perimeter = {perim}\")\n",
    "\n",
    "area2, perim2 = rectangle_stats(10, 4)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aee17814-92f9-4744-8c8d-dd738767a67b",
   "metadata": {},
   "source": [
    "### Scopes\n",
    "\n",
    "Variables created inside functions are **local** to that function:\n",
    "\n",
    "```python\n",
    "def my_function():\n",
    "    local_var = 10  # Only exists inside the function\n",
    "    print(local_var)\n",
    "\n",
    "my_function()     # Prints: 10\n",
    "# print(local_var)  # Error: local_var doesn't exist here\n",
    "```\n",
    "\n",
    "Variables outside functions are **global**:\n",
    "\n",
    "```python\n",
    "global_var = 100  # Accessible everywhere\n",
    "\n",
    "def show_global():\n",
    "    print(global_var)  # Can read global variable\n",
    "\n",
    "show_global()  # Prints: 100\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9923f7df-8dcd-4a44-a6fb-dd70b36259bb",
   "metadata": {},
   "source": [
    "#### Variables and parameters are local\n",
    "\n",
    "When you create a variable inside a function, it is **local**, which means it exists only inside the function. For example, the following function takes two arguments, con**cat**enates them, and prints the result twice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 172,
   "id": "c397c812-43db-4bb6-b695-797c75159329",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n",
      "Hello, Homer!\n"
     ]
    }
   ],
   "source": [
    "%%expect NameError\n",
    "\n",
    "def greet():\n",
    "    message = \"Hello\"\n",
    "    print(message)\n",
    "\n",
    "greet()\n",
    "\n",
    "print(message)   ### can't do! \"message\" variable is local to the function definition.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 173,
   "id": "977798e0-3256-44f9-a903-08bd7ef1c8a8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n",
      "Hello\n"
     ]
    }
   ],
   "source": [
    "%%expect NameError\n",
    "\n",
    "def greet():\n",
    "    global message        ### must declare first\n",
    "    message = \"Hello\"\n",
    "    print(message)\n",
    "\n",
    "greet()\n",
    "\n",
    "print(message)   ### can do! \"message\" variable is now global in the function definition.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b97534fe-43bb-41e1-80ab-c80fd15ba083",
   "metadata": {},
   "source": [
    "### Function Composition\n",
    "\n",
    "Function composition is the process of using the output of one function as the input to another."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 174,
   "id": "7a2932f9-a9a2-4733-a480-934fbab6f54e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "36\n"
     ]
    }
   ],
   "source": [
    "def double(x):\n",
    "    return x * 2\n",
    "\n",
    "def square(x):\n",
    "    return x ** 2\n",
    "\n",
    "result = square(double(3))\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 175,
   "id": "ex7-q-59a52e59",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### === EXERCISE 7: Function Composition ===\n",
    "### Write three functions:\n",
    "### - calculate_area(length, width): returns the area\n",
    "### - format_result(value): returns 'Area is X square units'\n",
    "### Then write report_rectangle() that uses both to calculate and format.\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code stops here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 176,
   "id": "ex7-a-4daaf03f",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Area is 15 square units\n",
      "Area is 20 square units\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def calculate_area(length, width):\n",
    "    return length * width\n",
    "\n",
    "def format_result(value):\n",
    "    return f'Area is {value} square units'\n",
    "\n",
    "### Function composition: one function calls another\n",
    "def report_rectangle(length, width):\n",
    "    area = calculate_area(length, width)\n",
    "    return format_result(area)\n",
    "\n",
    "### Test\n",
    "print(report_rectangle(5, 3))\n",
    "print(report_rectangle(10, 2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 177,
   "id": "ex6-q-231171d8",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### === EXERCISE 9: Variable Scopes ===\n",
    "### Write a function called modify_variables() that:\n",
    "### - Takes a parameter x\n",
    "### - Creates a local variable local_var = x * 2\n",
    "### - Prints both x and local_var inside the function\n",
    "### Then call it and try to print local_var outside (it should fail).\n",
    "\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 178,
   "id": "ex6-a-6431a37c",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Inside: x=5, local_var=10\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def modify_variables(x):\n",
    "    local_var = x * 2\n",
    "    print(f\"Inside: x={x}, local_var={local_var}\")\n",
    "\n",
    "### Call the function\n",
    "modify_variables(5)\n",
    "\n",
    "### This will cause NameError because local_var doesn't exist outside\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "495cb571-e8a9-4865-ba61-9622564f54e6",
   "metadata": {},
   "source": [
    "When we run `print_verse`, it calls `first_two_lines`, which calls `repeat`, which calls `print`.\n",
    "That's a lot of functions.\n",
    "\n",
    "Of course, we could have done the same thing with fewer functions, but the point of this example is to show how functions can work together."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "105e0774-6927-4c40-866d-58264f19f216",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "### Docstrings\n",
    "\n",
    "**Docstrings** (**documentation** strings) are used to document **functions**/methods, _classes_, and _modules_. They use _triple quotes_ and should be the first statement after defining a function or class. \n",
    "\n",
    "Always document your functions with docstrings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "id": "95498a60",
   "metadata": {},
   "outputs": [],
   "source": [
    "### Function with docstring\n",
    "def greet(name):\n",
    "    \"\"\"\n",
    "    This function does xxx and yyy.         ### 1. what this function is about\n",
    "    \n",
    "    Args:                                   ### 2. input parameters\n",
    "        name: The person's name (string)    \n",
    "    \n",
    "    Returns:                                ### 3. what the function returns\n",
    "        A greeting message (string)\n",
    "    \"\"\"\n",
    "    return f\"Hello, {name}!\"\n",
    "\n",
    "message = greet(\"Homer\")       ### call the function"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5dbe1c8-83ec-4ef7-bb72-f936e4806a93",
   "metadata": {},
   "source": [
    "As an example:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f1bb7d6-611f-4af5-979d-9f91fb0e2bea",
   "metadata": {},
   "source": [
    "```python\n",
    "def calculate_bmi(weight, height):\n",
    "    \"\"\"\n",
    "    Calculate Body Mass Index (BMI).\n",
    "    \n",
    "    Args:\n",
    "        weight: Weight in kilograms (float)\n",
    "        height: Height in meters (float)\n",
    "    \n",
    "    Returns:\n",
    "        BMI value (float)\n",
    "    \n",
    "    Example:\n",
    "        >>> calculate_bmi(70, 1.75)\n",
    "        22.86\n",
    "    \"\"\"\n",
    "    return weight / (height ** 2)\n",
    "```\n",
    "\n",
    "Good docstrings include:\n",
    "1. What the function does\n",
    "2. Parameters and their types\n",
    "3. What the function returns\n",
    "4. Usage examples (optional)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 180,
   "id": "4bef9858-6692-4dc9-b2bf-534c14390028",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Testing calculate_discount:\n",
      "  $100 with 20% off = $80.0\n",
      "\n",
      "Testing is_valid_email:\n",
      "  'user@example.com' is valid: True\n",
      "  'invalid-email' is valid: False\n",
      "\n",
      "Testing celsius_to_fahrenheit:\n",
      "  0°C = 32.0°F\n",
      "  100°C = 212.0°F\n",
      "  37°C = 98.6°F\n"
     ]
    }
   ],
   "source": [
    "### Practical functions with docstrings\n",
    "\n",
    "def calculate_discount(price, discount_percent):\n",
    "    \"\"\"\n",
    "    Calculate the final price after applying a discount.\n",
    "    \n",
    "    Args:\n",
    "        price: Original price (float or int)\n",
    "        discount_percent: Discount percentage (0-100)\n",
    "    \n",
    "    Returns:\n",
    "        Final price after discount (float)\n",
    "    \"\"\"\n",
    "    discount_amount = price * (discount_percent / 100)\n",
    "    final_price = price - discount_amount\n",
    "    return final_price\n",
    "\n",
    "def is_valid_email(email):\n",
    "    \"\"\"\n",
    "    Check if email has basic valid format.\n",
    "    \n",
    "    Args:\n",
    "        email: Email address to validate (str)\n",
    "    \n",
    "    Returns:\n",
    "        True if email contains @ and ., False otherwise\n",
    "    \"\"\"\n",
    "    return '@' in email and '.' in email\n",
    "\n",
    "def celsius_to_fahrenheit(celsius):\n",
    "    \"\"\"\n",
    "    Convert Celsius to Fahrenheit.\n",
    "    \n",
    "    Args:\n",
    "        celsius: Temperature in Celsius\n",
    "    \n",
    "    Returns:\n",
    "        Temperature in Fahrenheit\n",
    "    \"\"\"\n",
    "    return (celsius * 9/5) + 32\n",
    "\n",
    "### Test the functions\n",
    "print(\"Testing calculate_discount:\")\n",
    "original = 100\n",
    "discount = 20\n",
    "final = calculate_discount(original, discount)\n",
    "print(f\"  ${original} with {discount}% off = ${final}\")\n",
    "\n",
    "print(\"\\nTesting is_valid_email:\")\n",
    "print(f\"  'user@example.com' is valid: {is_valid_email('user@example.com')}\")\n",
    "print(f\"  'invalid-email' is valid: {is_valid_email('invalid-email')}\")\n",
    "\n",
    "print(\"\\nTesting celsius_to_fahrenheit:\")\n",
    "print(f\"  0°C = {celsius_to_fahrenheit(0)}°F\")\n",
    "print(f\"  100°C = {celsius_to_fahrenheit(100)}°F\")\n",
    "print(f\"  37°C = {celsius_to_fahrenheit(37):.1f}°F\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 181,
   "id": "ex8-q-8a5c47d0",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### === EXERCISE 10: Writing Good Docstrings ===\n",
    "### Write a function called validate_password() that:\n",
    "### - Takes a password string as a parameter\n",
    "### - Checks if it's at least 8 characters long\n",
    "### - Returns True if valid, False otherwise\n",
    "### Include a comprehensive docstring with description, Args, and Returns sections.\n",
    "\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 182,
   "id": "ex8-a-e14ff35b",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "### Solution:\n",
    "def validate_password(password):\n",
    "    \"\"\"\n",
    "    Validate if a password meets minimum length requirements.\n",
    "    \n",
    "    Args:\n",
    "        password (str): The password to validate\n",
    "    \n",
    "    Returns:\n",
    "        bool: True if password is at least 8 characters, False otherwise\n",
    "    \"\"\"\n",
    "    return len(password) >= 8\n",
    "\n",
    "### Test the function\n",
    "print(validate_password(\"short\"))        ### False\n",
    "print(validate_password(\"verylongpassword\"))  ### True\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c46d682b",
   "metadata": {},
   "source": [
    "## Lambda Functions\n",
    "\n",
    "**Syntax:** `lambda arguments: expression`\n",
    "\n",
    "Lambda functions are small, anonymous functions that can have any number of arguments but can only have one expression. They are useful for short, simple functions that you don't want to define formally."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9ef797aa",
   "metadata": {},
   "source": [
    "### Lambda Limitations\n",
    "\n",
    "- Can only contain expressions, not statements\n",
    "- Cannot contain assignments, print statements, or other statements\n",
    "- Best for simple, one-line functions\n",
    "- For complex logic, use regular functions (`def`)\n",
    "\n",
    "If you find yourself writing a multi-line `lambda`, switch to a normal `def` function instead."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 183,
   "id": "31ac93fd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "### Basic lambda function\n",
    "square = lambda x: x ** 2\n",
    "print(square(5))  # Output: 25\n",
    "\n",
    "### Lambda with multiple arguments\n",
    "add = lambda x, y: x + y\n",
    "print(add(3, 4))  # Output: 7"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9c6e2c1",
   "metadata": {},
   "source": [
    "### Using Lambda with Built-in Functions\n",
    "\n",
    "Lambda functions are commonly used with `map()`, `filter()`, and `sorted()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 184,
   "id": "be56e0b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "squares from map: [1, 4, 9, 16]\n",
      "evens from filter: [2, 4]\n",
      "sorted by length: ['spam', 'eggs', 'SPAM', 'wonderful']\n"
     ]
    }
   ],
   "source": [
    "### Using lambda with map: apply a function to each item\n",
    "nums = [1, 2, 3, 4]\n",
    "squares = list(map(lambda x: x ** 2, nums))\n",
    "print('squares from map:', squares)\n",
    "\n",
    "### Using lambda with filter: keep only even numbers\n",
    "evens = list(filter(lambda x: x % 2 == 0, nums))\n",
    "print('evens from filter:', evens)\n",
    "\n",
    "### Using lambda with sorted: sort words by length\n",
    "words = ['spam', 'wonderful', 'eggs', 'SPAM']\n",
    "by_length = sorted(words, key=lambda w: len(w))\n",
    "print('sorted by length:', by_length)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46884308",
   "metadata": {},
   "source": [
    "## Applications"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32a0cba0-9eff-4a1f-99f1-1d7c98026be8",
   "metadata": {},
   "source": [
    "### Return values and conditionals\n",
    "\n",
    "If Python did not provide `abs`, we could write it like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 193,
   "id": "2cc3f78a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def absolute_value(x):\n",
    "    if x < 0:\n",
    "        return -x\n",
    "    else:\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02c98cad-281b-4484-8f93-25f88fc6f78f",
   "metadata": {},
   "source": [
    "If `x` is negative, the first `return` statement returns `-x` and the function ends immediately.\n",
    "Otherwise, the second `return` statement returns `x` and the function ends.\n",
    "So this function is correct.\n",
    "\n",
    "However, if you put `return` statements in a conditional, you must ensure that every possible path through the program hits a `return` statement (exhaustive).\n",
    "For example, here's an incorrect version of `absolute_value`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 194,
   "id": "4298cd37",
   "metadata": {},
   "outputs": [],
   "source": [
    "def absolute_value_wrong(x):\n",
    "    if x < 0:\n",
    "        return -x\n",
    "    if x > 0:\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c93481fe-ef59-4230-b6af-5b3be8ba1f97",
   "metadata": {},
   "source": [
    "Here's what happens if we call this function with `0` as an argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 195,
   "id": "6b535dda",
   "metadata": {},
   "outputs": [],
   "source": [
    "absolute_value_wrong(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30d9744c-5eb3-4a5d-b67d-79e8d54f3b77",
   "metadata": {},
   "source": [
    "We get nothing! Here's the problem: when `x` is `0`, neither condition is true, and the function ends without hitting a `return` statement, which means that the return value is `None`, so Jupyter displays nothing.\n",
    "\n",
    "As another example, here's a version of `absolute_value` with an extra `return` statement at the end."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "id": "2737e2fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "def absolute_value_extra_return(x):\n",
    "    if x < 0:\n",
    "        return -x\n",
    "    else:\n",
    "        return x\n",
    "    \n",
    "    return 'This is dead code'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e852136d-262b-400f-a81c-5f02cbf6a4e0",
   "metadata": {},
   "source": [
    "If `x` is negative, the first `return` statement runs and the function ends.\n",
    "Otherwise the second `return` statement runs and the function ends.\n",
    "Either way, we never get to the third `return` statement -- so it can never run.\n",
    "\n",
    "Code that can never run is called **dead code**.\n",
    "In general, dead code doesn't do any harm, but it often indicates a misunderstanding, and it might be confusing to someone trying to understand the program."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20f62ad7-9722-4616-a11d-16809abcdfb0",
   "metadata": {},
   "source": [
    "### Boolean functions\n",
    "\n",
    "Functions can return the boolean values `True` and `False`, which is often convenient for encapsulating a complex test in a function.\n",
    "For example, `is_divisible` checks whether `x` is divisible by `y` with no remainder."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 198,
   "id": "22376689",
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_divisible(x, y):\n",
    "    if x % y == 0:\n",
    "        return True\n",
    "    else:\n",
    "        return False"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13610789-88ff-4e87-b7f7-e9d84fb1a102",
   "metadata": {},
   "source": [
    "Here's how we use it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 199,
   "id": "a06461bd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 199,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "is_divisible(6, 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 200,
   "id": "9b31b4b4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 200,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "is_divisible(6, 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9245fdb3-a024-4b59-bc46-ff663d2390fa",
   "metadata": {},
   "source": [
    "Inside the function, the result of the `==` operator is a boolean, so we can write the\n",
    "function more concisely by returning it directly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 201,
   "id": "352a8977",
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_divisible(x, y):\n",
    "    return x % y == 0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4e62aab8-670b-4b41-94cb-2573b1b1e895",
   "metadata": {},
   "source": [
    "Boolean functions are often used in conditional statements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 202,
   "id": "9d1cd83a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "divisible\n"
     ]
    }
   ],
   "source": [
    "if is_divisible(6, 2):\n",
    "    print('divisible')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46d99214-7bae-4109-8802-184491ec5c61",
   "metadata": {},
   "source": [
    "It might be tempting to write something like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 203,
   "id": "a48d82d7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "divisible\n"
     ]
    }
   ],
   "source": [
    "if is_divisible(6, 2) == True:\n",
    "    print('divisible')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "233d00f2-2da6-414e-9bc5-744af32a5e34",
   "metadata": {},
   "source": [
    "But the comparison is unnecessary."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": ".venv (3.13.7)",
   "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.13.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
