{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "67fd7e0c",
   "metadata": {},
   "source": [
    "# Tuples and Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "194efbc7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "# Find project root by looking for _config.yml\n",
    "current = Path.cwd()\n",
    "for parent in [current, *current.parents]:\n",
    "    if (parent / '_config.yml').exists():\n",
    "        project_root = parent\n",
    "        break\n",
    "else:\n",
    "    project_root = Path.cwd().parent.parent\n",
    "\n",
    "# Add project root to path\n",
    "sys.path.insert(0, str(project_root))\n",
    "\n",
    "# Import shared teaching helpers and cell magics\n",
    "from shared import thinkpython, diagram, jupyturtle, structshape\n",
    "from shared.download import download\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e647515",
   "metadata": {},
   "source": [
    "## Tuples as Return Values\n",
    "\n",
    "**A function returns one object**, but that object can be a tuple.\n",
    "Returning a tuple is a common way to provide **multiple** results.\n",
    "\n",
    "For example, a scheduling system may need to convert a total number of minutes into hours and minutes.\n",
    "The built-in function `divmod` (`//` and `%`) takes two arguments and returns a tuple with the quotient and remainder."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 194,
   "id": "64b83062",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 15)"
      ]
     },
     "execution_count": 194,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "divmod(135, 60)    # 135 minutes → (2 hours, 15 minutes)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b5ce6cc",
   "metadata": {},
   "source": [
    "We can use tuple assignment to store the hours and minutes in separate variables."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 195,
   "id": "208be1eb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 195,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hours, minutes = divmod(135, 60)\n",
    "hours"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "056b3902",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "minutes"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b183272e",
   "metadata": {},
   "source": [
    "Here is a simple function that returns both the minimum and maximum from a list — useful for summarizing a range of values, such as daily sales figures."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "a93d62d4",
   "metadata": {},
   "outputs": [],
   "source": [
    "def min_max(t):\n",
    "    return min(t), max(t)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "016fd796",
   "metadata": {},
   "source": [
    "`min` and `max` are built-in functions that find the smallest and largest elements of a sequence.\n",
    "`min_max` computes both and returns them as a tuple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 201,
   "id": "9c73a695",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(7200, 12400)"
      ]
     },
     "execution_count": 201,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "min_max([8500, 12400, 7200, 9900])    # daily sales figures"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9cd8b4e",
   "metadata": {},
   "source": [
    "We can unpack the result like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c00f8d05",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 4)"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "low, high = min_max([8500, 12400, 7200, 9900])\n",
    "low, high"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "31aa277d",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### Exercise: Tuples as Return Values\n",
    "#   Write a function called `stats` that takes a list of numbers and returns\n",
    "#   a tuple containing (min, max, sum, mean) of the list.\n",
    "#   Test it with [10, 20, 30, 40, 50] and unpack the result into four variables.\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "7f31585a",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 50 150 30.0\n"
     ]
    }
   ],
   "source": [
    "def stats(numbers):\n",
    "    return min(numbers), max(numbers), sum(numbers), sum(numbers) / len(numbers)\n",
    "\n",
    "lo, hi, total, avg = stats([10, 20, 30, 40, 50])\n",
    "print(lo, hi, total, avg)    # 10 50 150 30.0"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "63cb9f0d",
   "metadata": {},
   "source": [
    "## Argument Packing\n",
    "\n",
    "Functions can take a variable number of arguments.\n",
    "A parameter name that begins with `*` **packs** extra arguments into a tuple.\n",
    "The following function (pretending we don't have a built-in for it) takes any number of arguments and computes their arithmetic mean."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "id": "7a2beb89",
   "metadata": {},
   "outputs": [],
   "source": [
    "def mean(*args):                # variable-length argument list\n",
    "    return sum(args) / len(args)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "514388b0",
   "metadata": {},
   "source": [
    "The parameter name can be anything, but `args` is conventional.\n",
    "Here we call it with three employee performance scores:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 214,
   "id": "047c3f54",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9500.0"
      ]
     },
     "execution_count": 214,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mean(8500, 12400, 7200, 9900)    # average of four daily sales figures"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc83e72d",
   "metadata": {},
   "source": [
    "If you have a sequence of values and want to pass them as separate arguments, use `*` to **unpack** the sequence.\n",
    "For example, `divmod` takes exactly two arguments - if you pass a tuple, it counts as a single argument and raises an error."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4f91f0f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%expect TypeError\n",
    "t = (135, 60)\n",
    "divmod(t)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17ad85eb",
   "metadata": {},
   "source": [
    "Even though the tuple contains two elements, it is still one argument.\n",
    "If you unpack it, the two elements are passed separately."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 216,
   "id": "9390dce8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 15)"
      ]
     },
     "execution_count": 216,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "divmod(*t)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18a060b9",
   "metadata": {},
   "source": [
    "Packing and unpacking are handy when you want to adapt an existing function.\n",
    "For example, this function takes any number of arguments, removes the lowest and highest, and computes the mean of the rest."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 217,
   "id": "ae6c283a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def trimmed_mean(*args):\n",
    "    low, high = min_max(args)\n",
    "    trimmed = list(args)        # convert to a list for mutability\n",
    "    trimmed.remove(low)\n",
    "    trimmed.remove(high)\n",
    "    return mean(*trimmed)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb767fee",
   "metadata": {},
   "source": [
    "First it uses `min_max` to find the lowest and highest values.\n",
    "Then it converts `args` to a list so it can use `remove`.\n",
    "Finally it **unpacks** the trimmed list so the elements are passed to `mean` as separate arguments.\n",
    "\n",
    "Here is an example using judge scores, where one score is unusually low:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 218,
   "id": "55f07796",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7.75"
      ]
     },
     "execution_count": 218,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mean(8.5, 9.2, 8.8, 4.5)    # raw mean including the outlier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 219,
   "id": "a655f1ea",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8.65"
      ]
     },
     "execution_count": 219,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "trimmed_mean(8.5, 9.2, 8.8, 4.5)    # mean after dropping lowest (4.5) and highest (9.2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8d915ad",
   "metadata": {},
   "source": [
    "You can also unpack sequences to build new tuples (or lists) without using `+`.\n",
    "For example, merging two quarterly sales tuples into a single half-year tuple:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 220,
   "id": "4a12c0ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(112, 98, 135, 144, 101, 129)"
      ]
     },
     "execution_count": 220,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "q1 = (112, 98, 135)    # monthly sales for Q1\n",
    "q2 = (144, 101, 129)   # monthly sales for Q2\n",
    "h1 = (*q1, *q2)        # combined first-half data\n",
    "h1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32edf06a",
   "metadata": {},
   "source": [
    "This kind of trimmed mean is used in sports with subjective judging (like diving and gymnastics) to reduce the influence of outlier scores."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 221,
   "id": "86500fb9",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### Exercise: When to Use Tuples\n",
    "#   For each scenario below, decide whether a tuple or a list is more appropriate\n",
    "#   and implement it:\n",
    "#   1. Store the (latitude, longitude) coordinates of a fixed location: 38.9, -77.0\n",
    "#   2. Build a collection of city names that will have cities added to it over time.\n",
    "#      Start with ['Paris', 'Tokyo'] and append 'Nairobi'.\n",
    "#   3. Use a (month, day) tuple as a key in a dictionary to store holiday names.\n",
    "#      Add entries for (1,1)->'New Year' and (7,4)->'Independence Day'.\n",
    "#      Print the holiday for July 4th.\n",
    "### Your code starts here.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 223,
   "id": "a8f708f3",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(38.9, -77.0)\n",
      "['Paris', 'Tokyo', 'Nairobi']\n",
      "Independence Day\n"
     ]
    }
   ],
   "source": [
    "# 1. Tuple — fixed record, won't change\n",
    "location = (38.9, -77.0)\n",
    "print(location)\n",
    "\n",
    "# 2. List — will grow over time\n",
    "cities = ['Paris', 'Tokyo']\n",
    "cities.append('Nairobi')\n",
    "print(cities)\n",
    "\n",
    "# 3. Tuple as dict key — tuples are hashable\n",
    "holidays = {(1, 1): 'New Year', (7, 4): 'Independence Day'}\n",
    "print(holidays[(7, 4)])    # Independence Day"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fa001a8c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": ".venv",
   "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
}
