{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "30b43901",
   "metadata": {},
   "source": [
    "# Looping and Sorting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9c07ad67",
   "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": "86b09411",
   "metadata": {},
   "source": [
    "## Looping Through String Lists\n",
    "\n",
    "You can use a `for` statement to loop through the elements of a list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "id": "a0145b1d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "apple\n",
      "banana\n",
      "cherry\n"
     ]
    }
   ],
   "source": [
    "fruits = ['apple', 'banana', 'cherry']\n",
    "\n",
    "for fruit in fruits:\n",
    "    print(fruit)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32a3b5d4",
   "metadata": {},
   "source": [
    "`.split()` returns a list of words, we can use `for` to loop through them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "id": "b0d0d37b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "We\n",
      "are\n",
      "programmed\n",
      "to\n",
      "receive\n"
     ]
    }
   ],
   "source": [
    "s = 'We are programmed to receive'  ### lyric from the Eagles' 1976 hit song \"Hotel California\".\n",
    "\n",
    "for word in s.split():\n",
    "    print(word)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07423363",
   "metadata": {},
   "source": [
    "Not that it's useful, but a `for` loop over an empty list never runs the indented statements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "id": "ff8dd4bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "for x in []:\n",
    "    print('This never happens.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ad54a44",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Looping Through String Lists\n",
    "# Difficulty: Basic\n",
    "words = ['apple', 'Banana', 'cherry', 'Date', 'elderberry']\n",
    "# 1. Loop through the words and print each word in lowercase\n",
    "# 2. Create a new list containing only words that start with a vowel (a, e, i, o, u)\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "1e0a8da5",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Numbers multiplied by 2:\n",
      "20\n",
      "40\n",
      "60\n",
      "80\n",
      "100\n",
      "\n",
      "Numbers > 25: [30, 40, 50]\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "words = ['apple', 'Banana', 'cherry', 'Date', 'elderberry']\n",
    "\n",
    "print(\"Words in lowercase:\")\n",
    "for word in words:\n",
    "    print(word.lower())\n",
    "\n",
    "vowels = ['a', 'e', 'i', 'o', 'u']\n",
    "starts_with_vowel = []\n",
    "for word in words:\n",
    "    if word[0].lower() in vowels:\n",
    "        starts_with_vowel.append(word)\n",
    "\n",
    "print(f\"\\nWords starting with a vowel: {starts_with_vowel}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24c73570",
   "metadata": {},
   "source": [
    "## Sorting String Lists\n",
    "\n",
    "Python provides a built-in function called `sorted` that sorts the elements of a list and the `.sort()` method that does similarly.\n",
    "\n",
    "- `sorted()`\n",
    "- `.join()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "39d8106e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['a', 'b', 'c']"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scramble = ['c', 'a', 'b']\n",
    "sorted(scramble)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c11a1493",
   "metadata": {},
   "source": [
    "The original list is unchanged."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "c5d3412c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['c', 'a', 'b']"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scramble"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "909b3603",
   "metadata": {},
   "source": [
    "`sorted` works with any kind of sequence, not just strings or lists. So we can sort the letters in a string like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "ac2bce0d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['e', 'e', 'l', 'r', 's', 't', 't']"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sorted('letters')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40f50da5",
   "metadata": {},
   "source": [
    "The result is a **list**. To convert the list to a string, we can use `join`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "61192a2c",
   "metadata": {},
   "outputs": [],
   "source": [
    "letters = ''.join(sorted('letters'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ee43856",
   "metadata": {},
   "source": [
    "With an empty string as the delimiter, the elements of the list are joined with nothing between them."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91b9571a",
   "metadata": {},
   "source": [
    "In lists, you have a `.sort()` method, which is not available in strings; it is list only."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be42f9cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%expect AttributeError\n",
    "letters.sort()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "6b6cea8d",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Sorting Lists\n",
    "# Difficulty: Intermediate\n",
    "scores = [85, 92, 78, 90, 88]\n",
    "names = ['Charlie', 'Alice', 'Bob']\n",
    "# 1. Sort the scores in descending order (highest first)\n",
    "# 2. Sort the names alphabetically and join them with commas\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "93c692ed",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Scores (descending): [92, 90, 88, 85, 78]\n",
      "Names (alphabetically): Alice, Bob, Charlie\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "scores = [85, 92, 78, 90, 88]\n",
    "names = ['Charlie', 'Alice', 'Bob']\n",
    "\n",
    "sorted_scores = sorted(scores, reverse=True)\n",
    "sorted_names = sorted(names)\n",
    "names_joined = \", \".join(sorted_names)\n",
    "\n",
    "print(f\"Scores (descending): {sorted_scores}\")\n",
    "print(f\"Names (alphabetically): {names_joined}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e796b6b",
   "metadata": {},
   "source": [
    "(section_docstring)=\n",
    "## Docstrings\n",
    "\n",
    "A **docstring** is a string at the beginning of a function that explains the interface (\"doc\" is short for \"documentation\").\n",
    "Here is an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "24c7ccd7",
   "metadata": {},
   "outputs": [],
   "source": [
    "def polyline(n, length, angle):\n",
    "    \"\"\"Draws line segments with the given length and angle between them.\n",
    "    \n",
    "    n: integer number of line segments\n",
    "    length: length of the line segments\n",
    "    angle: angle between segments (in degrees)\n",
    "    \"\"\"    \n",
    "    for i in range(n):\n",
    "        forward(length)\n",
    "        left(angle)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9608df4d",
   "metadata": {},
   "source": [
    "By convention, docstrings are triple-quoted strings, also known as **multiline strings** because the triple quotes allow the string to span more than one line.\n",
    "\n",
    "A docstring should:\n",
    "\n",
    "* Explain concisely what the function does, without getting into the details of how it works,\n",
    "\n",
    "* Explain what effect each parameter has on the behavior of the function, and\n",
    "\n",
    "* Indicate what type each parameter should be, if it is not obvious.\n",
    "\n",
    "Writing this kind of documentation is an important part of interface design.\n",
    "A well-designed interface should be simple to explain; if you have a hard time explaining one of your functions, maybe the interface could be improved."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "e437d5df",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Writing a Docstring\n",
    "# Difficulty: Challenge\n",
    "# Write a function called area_rectangle(width, height).\n",
    "# 1. Add type hints for parameters and return value.\n",
    "# 2. Include a docstring describing parameters, return value, and one raised error.\n",
    "# 3. Raise ValueError if width or height is negative.\n",
    "# 4. Call the function with (3, 4) and print the result.\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "id": "5f4cfedb",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "def area_rectangle(width: float, height: float) -> float:\n",
    "    \"\"\"Return the area of a rectangle.\n",
    "\n",
    "    width: non-negative numeric width of the rectangle\n",
    "    height: non-negative numeric height of the rectangle\n",
    "    returns: numeric area\n",
    "    raises: ValueError if width or height is negative\n",
    "    \"\"\"\n",
    "    if width < 0 or height < 0:\n",
    "        raise ValueError('width and height must be non-negative')\n",
    "    return width * height\n",
    "\n",
    "print(area_rectangle(3, 4))"
   ]
  }
 ],
 "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
}
