{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ad3b8898-00cb-4a94-a4ea-7456dbaf15a6",
   "metadata": {},
   "source": [
    "# Lists"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "faee75a2",
   "metadata": {
    "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, download\n",
    "\n",
    "# Register as top-level modules so direct imports work in subsequent cells\n",
    "sys.modules['thinkpython'] = thinkpython\n",
    "sys.modules['diagram'] = diagram\n",
    "sys.modules['jupyturtle'] = jupyturtle\n",
    "sys.modules['download'] = download\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "036a57a8",
   "metadata": {},
   "source": [
    "This chapter presents one of Python's most useful built-in types, lists.\n",
    "You will also learn more about objects and what can happen when multiple variables refer to the same object."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a99b3f4",
   "metadata": {},
   "source": [
    "## Learning Objectives\n",
    "\n",
    "By the end of this chapter, you should be able to:\n",
    "\n",
    "- Construct and manipulate Python lists using indexing, slicing (including advanced step slices), and core list methods.\n",
    "\n",
    "- Explain and reason about object identity, aliasing, shallow vs. deep copies, and how lists behave when passed to functions.\n",
    "\n",
    "- Use lists idiomatically in Python: list comprehensions, unpacking and starred expressions, `enumerate()`, `zip()`, and basic sorting/looping patterns."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56175812-24e1-425f-9330-3736c42adb56",
   "metadata": {},
   "source": [
    "## Introduction to Lists"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4d32b3e2",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "### What is a List?\n",
    "\n",
    "If you're familiar with other programming languages, Python lists are similar to arrays in languages like Java, C++, or JavaScript. However, Python lists are more flexible: they can grow or shrink dynamically and can contain mixed data types.\n",
    "\n",
    "A Python **list** is a **sequence** data type, like strings and tuples. Sequences are: \n",
    "- ordered collections that support \n",
    "- indexing, \n",
    "- slicing, \n",
    "- `len()`, `in` membership testing, and \n",
    "- iteration. \n",
    "\n",
    "The key distinction is mutability and heterogeneous element data types: \n",
    "- In a string, the values are characters; in a list, they can be **any type**. The value literals in a list are called **elements**.\n",
    "- Lists are **mutable**, meaning they can be modified after creation. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf597fe5-d948-4877-8542-8f23baaaf004",
   "metadata": {},
   "source": [
    "The following figure shows the state diagram for `fruits`, `numbers` and `empty`. **Lists are objects** in Python's memory. When you create a list, Python creates a **list object** that holds your data in a specific order, and the variable holds a **reference** to that object.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "930eb109",
   "metadata": {},
   "outputs": [],
   "source": [
    "numbers = [42, 123]\n",
    "fruits = ['apple', 'banana', 'cherry']\n",
    "empty = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "25582cad",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "numbers = [42, 123]\n",
    "fruits = ['apple', 'banana', 'cherry']\n",
    "empty = []\n",
    "from diagram import make_list, Binding, Value\n",
    "\n",
    "list1 = make_list(fruits, dy=-0.3, offsetx=0.17)\n",
    "binding1 = Binding(Value('fruits'), list1)\n",
    "\n",
    "list2 = make_list(numbers, dy=-0.3, offsetx=0.17)\n",
    "binding2 = Binding(Value('numbers'), list2)\n",
    "\n",
    "list3 = make_list(empty, dy=-0.3, offsetx=0.1)\n",
    "binding3 = Binding(Value('empty'), list3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "925c7d67",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 366x188 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from diagram import diagram, adjust, Bbox\n",
    "\n",
    "width, height, x, y = [3.66, 1.88, 0.45, 1.2]\n",
    "ax = diagram(width, height)\n",
    "bbox1 = binding1.draw(ax, x, y)\n",
    "bbox2 = binding2.draw(ax, x+2.25, y)\n",
    "bbox3 = binding3.draw(ax, x+2.25, y-1.0)\n",
    "\n",
    "bbox = Bbox.union([bbox1, bbox2, bbox3])\n",
    "#adjust(x, y, bbox)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b8220567",
   "metadata": {},
   "source": [
    "## Creating Lists\n",
    "\n",
    "There are several ways to create a new list:\n",
    "\n",
    "- **Square brackets `[]`**: Enclose elements in square brackets (a subscription expression) to create a list literal\n",
    "- **`list()` constructor**: Convert any iterable (strings, ranges, tuples, etc.) into a list\n",
    "- **List comprehension**: Create lists using a concise expression-based syntax\n",
    "- **`split()` method**: Convert a string into a list of words or parts\n",
    "- **Nested lists**: Create lists containing other lists as elements"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc285bfe-a6fa-4653-9aa5-e85d12bc2675",
   "metadata": {},
   "source": [
    "### Basic List Creation\n",
    "\n",
    "The most common way to create a list is by enclosing comma-separated values in square brackets `[]`.\n",
    "\n",
    "**Syntax:**\n",
    "```python\n",
    "list_name = [element1, element2, element3, ...]\n",
    "```\n",
    "\n",
    "The elements can be of any type, and you can mix different types in the same list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "a16a119b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['apple', 'banana', 'cherry']\n",
      "[1, 2, 3, 4, 5]\n",
      "[]\n"
     ]
    }
   ],
   "source": [
    "### Using square brackets - sequences of items\n",
    "\n",
    "numbers = [1, 2, 3, 4, 5]               ### a list of integers\n",
    "fruits = ['apple', 'banana', 'cherry']  ### a list of strings\n",
    "empty = []                              ### an empty list\n",
    "\n",
    "print(fruits)\n",
    "print(numbers)\n",
    "print(empty)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "45d8be4c",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Create Different Types of Lists\n",
    "# Create the following lists:\n",
    "# 1. A list called 'colors' with three color names\n",
    "# 2. An empty list called 'empty_list'\n",
    "# 3. A list called 'mixed' with a string, an integer, and a float\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "a77888de",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Colors: ['red', 'blue', 'green']\n",
      "Empty list: []\n",
      "Mixed list: ['hello', 42, 3.14]\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "colors = ['red', 'blue', 'green']\n",
    "empty_list = []\n",
    "mixed = ['hello', 42, 3.14]\n",
    "\n",
    "print(f\"Colors: {colors}\")\n",
    "print(f\"Empty list: {empty_list}\")\n",
    "print(f\"Mixed list: {mixed}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3de936b6",
   "metadata": {},
   "source": [
    "### `list()` Constructor\n",
    "\n",
    "The `list()` constructor converts any **iterable** (strings, ranges, tuples, etc.) into a list. This is useful when you need to convert data from one sequence type to another."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "1e6127e9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['s', 'p', 'a', 'm']\n",
      "[0, 1, 2, 3, 4]\n",
      "[1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "chars = list('spam')            ### a list of characters\n",
    "nums = list(range(5))           ### a list of numbers from 0 to 4\n",
    "tuple_data = (1, 2, 3)\n",
    "list_data = list(tuple_data)    ### a list created from a tuple\n",
    "\n",
    "print(chars)\n",
    "print(nums)\n",
    "print(list_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "486e5ec1",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Convert Using list() Constructor\n",
    "# 1. Convert the string \"Python\" into a list of characters\n",
    "# 2. Create a list of numbers from 10 to 14 using range() and list()\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "1277e0e2",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Characters: ['P', 'y', 't', 'h', 'o', 'n']\n",
      "Numbers: [10, 11, 12, 13, 14]\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "chars = list('Python')\n",
    "numbers = list(range(10, 15))\n",
    "\n",
    "print(f\"Characters: {chars}\")\n",
    "print(f\"Numbers: {numbers}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "338430b9-2b95-4568-af95-5a91c809dbcf",
   "metadata": {},
   "source": [
    "### List Comprehension\n",
    "\n",
    "**List comprehension** provides a concise way to create lists based on existing sequences or ranges. It's a powerful and Pythonic approach that often replaces traditional loops. They're a more Pythonic alternative to using `for` loops to build lists.\n",
    "\n",
    "**Basic Syntax:**\n",
    "```python\n",
    "[expression for item in iterable]\n",
    "```\n",
    "\n",
    "**With Condition:**\n",
    "```python\n",
    "[expression for item in iterable if condition]\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "7afb9f75",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Traditional: [0, 1, 4, 9, 16]\n",
      "Comprehension: [0, 1, 4, 9, 16]\n",
      "Even squares: [0, 4, 16, 36, 64]\n"
     ]
    }
   ],
   "source": [
    "# Traditional way\n",
    "squares = []\n",
    "for x in range(5):\n",
    "    squares.append(x**2)\n",
    "print(\"Traditional:\", squares)\n",
    "\n",
    "# List comprehension way\n",
    "squares = [x**2 for x in range(5)]\n",
    "print(\"Comprehension:\", squares)\n",
    "\n",
    "# With condition\n",
    "even_squares = [x**2 for x in range(10) if x % 2 == 0]\n",
    "print(\"Even squares:\", even_squares)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "61bb0fab",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Word lengths: [5, 5, 6, 11]\n",
      "Long words: ['PYTHON', 'PROGRAMMING']\n",
      "Flattened: [1, 2, 3, 4, 5, 6, 7, 8, 9]\n"
     ]
    }
   ],
   "source": [
    "# More complex list comprehensions\n",
    "words = ['hello', 'world', 'python', 'programming']\n",
    "\n",
    "# Get lengths of words\n",
    "lengths = [len(word) for word in words]\n",
    "print(\"Word lengths:\", lengths)\n",
    "\n",
    "# Get uppercase words longer than 5 characters\n",
    "long_words = [word.upper() for word in words if len(word) > 5]\n",
    "print(\"Long words:\", long_words)\n",
    "\n",
    "# Nested comprehension - flatten a 2D list\n",
    "matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n",
    "flattened = [item for row in matrix for item in row]\n",
    "print(\"Flattened:\", flattened)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "2f7ff33f",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: convert string to uppercase using list comprehension\n",
    "### Hint: use str.upper() method\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "6ad04003",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['HELLO', 'WORLD', 'PYTHON']\n"
     ]
    }
   ],
   "source": [
    "# Convert strings to uppercase\n",
    "words = ['hello', 'world', 'python']\n",
    "uppercase_words = [word.upper() for word in words]\n",
    "print(uppercase_words)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "0065d342",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Filter list using list comprehension: \n",
    "### Create a new list with only words longer than 5 characters \n",
    "### Hint: use len() function\n",
    "### Your code starts here:\n",
    "words = ['apple', 'banana', 'cherry', 'date', 'elderberry']\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "aa856dfe",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['banana', 'cherry', 'elderberry']\n"
     ]
    }
   ],
   "source": [
    "words = ['apple', 'banana', 'cherry', 'date', 'elderberry']\n",
    "long_words = [word for word in words if len(word) > 5]\n",
    "print(long_words)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "1bc50eec",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: List Comprehension Practice\n",
    "# 1. Create a list of cubes (x^3) for numbers 1 through 5\n",
    "# 2. Create a list of only odd numbers from 1 to 20\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "153a5ae9",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Cubes: [1, 8, 27, 64, 125]\n",
      "Odd numbers: [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "cubes = [x**3 for x in range(1, 6)]\n",
    "odds = [x for x in range(1, 21) if x % 2 != 0]\n",
    "\n",
    "print(f\"Cubes: {cubes}\")\n",
    "print(f\"Odd numbers: {odds}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "37703787",
   "metadata": {},
   "source": [
    "### Lists and Strings\n",
    "\n",
    "This section explores common patterns for working with strings and lists, focusing on converting between them and manipulating them together.\n",
    "\n",
    "A string is a sequence of characters and a list is a sequence of values, but a list of characters is not the same as a string. To convert a string to a list of individual characters, use the `list()` function (covered earlier). To break a string into words or parts, use the `split()` method shown below."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbcd3e40",
   "metadata": {},
   "source": [
    "#### `split()` Method\n",
    "\n",
    "The `split()` method breaks a string into a list of words or parts based on a delimiter. By default, it splits on whitespace."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "98faa717",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['hello', 'world', 'python']\n",
      "['apple', 'banana', 'cherry']\n"
     ]
    }
   ],
   "source": [
    "# Split by whitespace (default)\n",
    "words = 'hello world python'.split()\n",
    "print(words)\n",
    "\n",
    "# Split by custom delimiter\n",
    "data = 'apple,banana,cherry'.split(',')\n",
    "print(data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aaa7963c",
   "metadata": {},
   "source": [
    "You can also use an optional **delimiter** argument to specify which characters to use as word boundaries:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "d25b1cf4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['ex', 'parrot']\n"
     ]
    }
   ],
   "source": [
    "s = 'ex-parrot'\n",
    "t = s.split('-')\n",
    "print(t)  # ['ex', 'parrot']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6230163f",
   "metadata": {},
   "source": [
    "#### `join()` Method\n",
    "\n",
    "If you have a list of strings, you can concatenate them into a single string using `join()`. Note that `join()` is a string method, so you invoke it on the delimiter and pass the list as an argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "95297ae8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'pining for the fjords'"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "delimiter = ' '\n",
    "t = ['pining', 'for', 'the', 'fjords']\n",
    "s = delimiter.join(t)\n",
    "s"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0fbe0382",
   "metadata": {},
   "source": [
    "In this case the delimiter is a space character, so `join` puts a space\n",
    "between words.\n",
    "To join strings without spaces, you can use the empty string, `''`, as a delimiter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "cdf5cd28",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Using split() and join()\n",
    "# 1. Split the sentence \"Python is an amazing language\" into a list of words\n",
    "# 2. Split the string \"2026-02-16\" by the delimiter \"-\"\n",
    "# 3. Take the sentence \"Python programming is fun\", \n",
    "#    split it, \n",
    "#    reverse the list, and \n",
    "#    join back with \" - \"\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "01866617",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1. Words: ['Python', 'is', 'an', 'amazing', 'language']\n",
      "2. Date parts: ['2026', '02', '16']\n",
      "3. Reversed and joined: fun - is - programming - Python\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "# 1. Split sentence into words\n",
    "sentence = \"Python is an amazing language\"\n",
    "words = sentence.split()\n",
    "\n",
    "# 2. Split date string by delimiter\n",
    "date_string = \"2026-02-16\"\n",
    "date_parts = date_string.split(\"-\")\n",
    "\n",
    "# 3. Split, reverse, and join\n",
    "sentence2 = \"Python programming is fun\"\n",
    "words2 = sentence2.split()\n",
    "words2.reverse()\n",
    "result = \" - \".join(words2)\n",
    "\n",
    "print(f\"1. Words: {words}\")\n",
    "print(f\"2. Date parts: {date_parts}\")\n",
    "print(f\"3. Reversed and joined: {result}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "6c64cc7a-ee3e-41e8-857e-2e5b69943945",
   "metadata": {},
   "source": [
    "### Nested Lists\n",
    "\n",
    "- Although a list can contain another list, the nested list still counts as a single element.\n",
    "- The elements of a list don't have to be the same data type. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7f6e6cf-f57a-4cab-8258-4b912c89fc2d",
   "metadata": {},
   "source": [
    "The following lists contain a string, a float, an integer, and another list (**nested**). We see that the length of the list `mixed_list` is 4, although it looks having more than 4 elements, but the 4th element is a list and counted only as 1 element."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "d9153b34-87ab-4044-a4e8-d4c87ad19f15",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['spam', 2.0, 5, [1, 2, 3, 4, 5]]\n",
      "There are 4 elements in the mixed list.\n"
     ]
    }
   ],
   "source": [
    "numbers = [1, 2, 3, 4, 5]\n",
    "mixed_list = ['spam', 2.0, 5, numbers ]\n",
    "\n",
    "print(mixed_list)\n",
    "print(f\"There are {len(mixed_list)} elements in the mixed list.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "615407c5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1, 2], [3, 4], [5, 6]]\n",
      "The nested list has 3 elements and they are lists themselves.\n"
     ]
    }
   ],
   "source": [
    "nested_list = [ \n",
    "               [1, 2], \n",
    "               [3, 4], \n",
    "               [5, 6]\n",
    "               ]\n",
    "print(nested_list)\n",
    "print(f\"The nested list has {len(nested_list)} elements and they are lists themselves.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "bc2a0fd8",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Working with Nested Lists\n",
    "matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n",
    "# 1. Access the second row (index 1)\n",
    "# 2. Access the element in the first row, third column (value should be 3)\n",
    "# 3. Calculate the total number of elements (not rows) using len()\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "66505057",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Second row: [4, 5, 6]\n",
      "Element at [0][2]: 3\n",
      "Total elements: 9\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n",
    "second_row = matrix[1]\n",
    "element_1_3 = matrix[0][2]\n",
    "total_elements = len(matrix) * len(matrix[0])  # rows * cols\n",
    "\n",
    "print(f\"Second row: {second_row}\")\n",
    "print(f\"Element at [0][2]: {element_1_3}\")\n",
    "print(f\"Total elements: {total_elements}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0b8ff01",
   "metadata": {
    "tags": [
     "list",
     "indexing",
     "slicing"
    ]
   },
   "source": [
    "## Accessing List Elements\n",
    "\n",
    "Now that we know how to create lists, let's learn how to access and extract data from them. Python provides several ways to retrieve elements from a list.\n",
    "\n",
    "- Understanding List Indices\n",
    "  Lists in Python use **zero-based indexing**, meaning the first element is at position `0`, the second at position `1`, and so on. You can think of the index as the offset from the beginning of the list.\n",
    "- List Index Properties\n",
    "  List indices work the same way as string indices:\n",
    "    -   Any integer expression can be used as an index.\n",
    "    -   If you try to read or write an element that does not exist, you get an `IndexError`.\n",
    "    -   If an index has a negative value, it counts backward from the end of the list, starting with `-1`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85098893",
   "metadata": {},
   "source": [
    "Below, we look at **indexing** (accessing a single element) and **slicing** (accessing a sublist) separately."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c10a709f",
   "metadata": {},
   "source": [
    "### Indexing\n",
    "\n",
    "Indexing reads or writes a single element of a list using the **bracket operator**.\n",
    "\n",
    "- **Syntax**: `lst[index]`\n",
    "\n",
    "- Indices are 0-based: the first element is at index `0`.\n",
    "- Negative indices count from the end: `-1` is the last element, `-2` is the one before that.\n",
    "\n",
    "For example, if we have `fruits = ['apple', 'banana', 'cherry']`, then `fruits[0]` returns `'apple'` and `fruits[-1]` returns `'cherry'`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "789cafa2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First: apple, Last: elderberry\n"
     ]
    }
   ],
   "source": [
    "fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']\n",
    "first = fruits[0]\n",
    "last = fruits[-1]\n",
    "\n",
    "print(f\"First: {first}, Last: {last}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0fc56a5",
   "metadata": {},
   "source": [
    "### Slicing\n",
    "\n",
    "Slicing works with **subsequences** of a list using the syntax: \n",
    "\n",
    "`lst[start:stop]`, or \n",
    "\n",
    "`lst[start:stop:step]`.\n",
    "\n",
    "- `start` is the index where the slice begins (inclusive).\n",
    "- `stop` is the index where the slice ends (exclusive).\n",
    "- `step` controls how many positions to advance (defaults to `1`). Use `2` to skip every other element, `-1` to reverse.\n",
    "- If `start` is omitted, Python starts from the beginning of the list.\n",
    "- If `stop` is omitted, Python slices all the way to the end of the list.\n",
    "- Slicing does not modify the original list; it returns a **new list** containing the selected elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "3d4beed3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Middle three: ['banana', 'cherry', 'date']\n",
      "From start: ['apple', 'banana', 'cherry']\n",
      "To end: ['cherry', 'date', 'elderberry']\n",
      "Every other: ['apple', 'cherry', 'elderberry']\n",
      "Reversed: ['elderberry', 'date', 'cherry', 'banana', 'apple']\n"
     ]
    }
   ],
   "source": [
    "fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']\n",
    "\n",
    "middle_three = fruits[1:4]      # elements at indices 1, 2, 3\n",
    "from_start = fruits[:3]         # first three elements\n",
    "to_end = fruits[2:]             # from index 2 to end\n",
    "every_other = fruits[::2]       # step=2: every second element\n",
    "reversed_fruits = fruits[::-1]  # step=-1: reverses the list\n",
    "\n",
    "print(f\"Middle three: {middle_three}\")\n",
    "print(f\"From start: {from_start}\")\n",
    "print(f\"To end: {to_end}\")\n",
    "print(f\"Every other: {every_other}\")\n",
    "print(f\"Reversed: {reversed_fruits}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7072b4d2-c642-47f3-846f-4c71a654fe4a",
   "metadata": {},
   "source": [
    "The step parameter in slicing enables powerful patterns like skipping elements, reversing, and extracting at intervals:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "step-example-1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Every other: [0, 2, 4, 6, 8]\n",
      "Every third: [0, 3, 6, 9]\n",
      "Reversed: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]\n",
      "Every other, reversed: [9, 7, 5, 3, 1]\n",
      "Slice from 1 to 8, step 2: [1, 3, 5, 7]\n",
      "Backwards from 7 to 2: [7, 6, 5, 4, 3]\n"
     ]
    }
   ],
   "source": [
    "numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
    "\n",
    "print(f\"Every other: {numbers[::2]}\")\n",
    "print(f\"Every third: {numbers[::3]}\")\n",
    "print(f\"Reversed: {numbers[::-1]}\")\n",
    "print(f\"Every other, reversed: {numbers[::-2]}\")\n",
    "print(f\"Slice from 1 to 8, step 2: {numbers[1:8:2]}\")\n",
    "print(f\"Backwards from 7 to 2: {numbers[7:2:-1]}\")     ### reversing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "c679966e",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Indexing and Slicing Practice\n",
    "fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']\n",
    "# 1. Get the third element (index 2)\n",
    "# 2. Get the last element using negative indexing\n",
    "# 3. Get a slice of elements from index 1 to 4 (not including 4)\n",
    "# 4. Get every other element\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "80a003df",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Third element: cherry\n",
      "Last element: fig\n",
      "Slice [1:4]: ['banana', 'cherry', 'date']\n",
      "Every other: ['apple', 'cherry', 'elderberry']\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']\n",
    "third = fruits[2]\n",
    "last = fruits[-1]\n",
    "slice_1_to_4 = fruits[1:4]\n",
    "every_other = fruits[::2]\n",
    "\n",
    "print(f\"Third element: {third}\")\n",
    "print(f\"Last element: {last}\")\n",
    "print(f\"Slice [1:4]: {slice_1_to_4}\")\n",
    "print(f\"Every other: {every_other}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ba6d2cf",
   "metadata": {},
   "source": [
    "### Query Methods\n",
    "\n",
    "These methods help you find information about elements in a list without modifying it.\n",
    "- `.count()`\n",
    "- `.index()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "f3efa52e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The number 2 appears 3 times\n",
      "First 'b' is at index: 1\n"
     ]
    }
   ],
   "source": [
    "# count() - Returns number of times element appears\n",
    "numbers = [1, 2, 3, 2, 4, 2, 5]\n",
    "count = numbers.count(2)\n",
    "print(f\"The number 2 appears {count} times\")\n",
    "\n",
    "# index() - Returns index of first occurrence\n",
    "letters = ['a', 'b', 'c', 'b', 'd']\n",
    "position = letters.index('b')\n",
    "print(f\"First 'b' is at index: {position}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ac27310-2b2a-4e32-ac6b-be69eb5eeb13",
   "metadata": {},
   "source": [
    "## List Operations\n",
    "\n",
    "This section covers how to work with lists using \n",
    "- operators, \n",
    "- built-in functions, and \n",
    "- methods."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "849630bb-837f-4825-8c34-71299d544c4a",
   "metadata": {},
   "source": [
    "### List Operators\n",
    "\n",
    "Python supports several operators that work directly with lists:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8996fddb-3fcf-4b9e-ab72-dcf3862b6f3d",
   "metadata": {},
   "source": [
    "| Operator | Name | Description | Example | Result |\n",
    "|----------|------|-------------|---------|--------|\n",
    "| `+` | Concatenation | Combines two lists | `[1, 2] + [3, 4]` | `[1, 2, 3, 4]` |\n",
    "| `*` | Repetition | Repeats a list | `[1, 2] * 3` | `[1, 2, 1, 2, 1, 2]` |\n",
    "| **`in`** | **Membership** | Checks if item exists in list | `3 in [1, 2, 3]` | `True` |\n",
    "| `not in` | Non-membership | Checks if item doesn't exist | `5 not in [1, 2, 3]` | `True` |\n",
    "| `[]` | Indexing | Accesses element by position | `[10, 20, 30][0]` | `10` |\n",
    "| **`[:]`** | **Slicing** | Extracts portion of list | `[0, 1, 2, 3][1:3]` | `[1, 2]` |\n",
    "| **`==`** | **Equality** | Checks if lists are equal | `[1, 2] == [1, 2]` | `True` |\n",
    "| `!=` | Inequality | Checks if lists are not equal | `[1, 2] != [1, 3]` | `True` |\n",
    "| `<` | Less than | Lexicographic comparison | `[1, 2] < [1, 3]` | `True` |\n",
    "| `>` | Greater than | Lexicographic comparison | `[1, 3] > [1, 2]` | `True` |\n",
    "| `<=` | Less than or equal | Lexicographic comparison | `[1, 2] <= [1, 2]` | `True` |\n",
    "| `>=` | Greater than or equal | Lexicographic comparison | `[1, 3] >= [1, 2]` | `True` |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b057c0c",
   "metadata": {},
   "source": [
    "The `+` operator concatenates lists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "66804de0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 3, 4, 5, 6]"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "num1 = [1, 2, 3]\n",
    "num2 = [4, 5, 6]\n",
    "\n",
    "num1 + num2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "474a5c40",
   "metadata": {},
   "source": [
    "The `*` operator repeats a list a given number of times."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "4a43e22a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['spam', 'spam', 'spam', 'spam']"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "['spam'] * 4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "484ebce4",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: List Concatenation and Repetition\n",
    "# 1. Create two lists: list1 = [1, 2, 3] and list2 = [4, 5, 6]\n",
    "# 2. Concatenate them to create list3\n",
    "# 3. Create list4 by repeating [0] three times\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "bf418a5d",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "List1 + List2: [1, 2, 3, 4, 5, 6]\n",
      "[0] * 3: [0, 0, 0]\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "list1 = [1, 2, 3]\n",
    "list2 = [4, 5, 6]\n",
    "list3 = list1 + list2\n",
    "list4 = [0] * 3\n",
    "\n",
    "print(f\"List1 + List2: {list3}\")\n",
    "print(f\"[0] * 3: {list4}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4bb45d01-6316-4d9a-86db-07fef9f46a85",
   "metadata": {},
   "source": [
    "#### Membership Testing\n",
    "\n",
    "The `in` operator checks whether a given element appears anywhere in the list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "c1c1ee1f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'apple' in fruits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "da6c7318",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print('tomato' in fruits)\n",
    "print('tomato' not in fruits)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e81f7580-58d6-4629-b908-7b5e8a9d2eec",
   "metadata": {},
   "source": [
    "When checking membership with `in`, only top-level elements are considered. For example, `'spam'` is in nested `mixed_list`, but `10` is not (since it's inside a nested list):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "a69319b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
    "mixed_list = ['spam', 2.0, 5, numbers ]\n",
    "\n",
    "print('spam' in mixed_list)     ### True\n",
    "print(10 in mixed_list)         ### False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "34c6eb52",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Membership Testing\n",
    "inventory = ['apple', 'banana', 'orange', 'grape', 'mango']\n",
    "# 1. Check if 'orange' is in the inventory\n",
    "# 2. Check if 'strawberry' is NOT in the inventory\n",
    "# 3. Create a list of items to check: ['apple', 'kiwi', 'grape']\n",
    "#    and count how many of them are in inventory using a \n",
    "#    LIST COMPREHENSION and sum()\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "0178ed80",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Has orange: True\n",
      "No strawberry: True\n",
      "Items found in inventory: 2\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "inventory = ['apple', 'banana', 'orange', 'grape', 'mango']\n",
    "has_orange = 'orange' in inventory\n",
    "no_strawberry = 'strawberry' not in inventory\n",
    "\n",
    "check_items = ['apple', 'kiwi', 'grape']\n",
    "count_found = sum( [item in inventory for item in check_items] ) \n",
    "### .count(True) is not needed because True is treated as 1 and False as 0 in sum()\n",
    "\n",
    "print(f\"Has orange: {has_orange}\")\n",
    "print(f\"No strawberry: {no_strawberry}\")\n",
    "print(f\"Items found in inventory: {count_found}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96288b3b-09aa-48ef-8bfb-f0b3ae7d84aa",
   "metadata": {},
   "source": [
    "### List Methods/Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e1711a1-6d05-465b-a420-46a8df3d33b5",
   "metadata": {},
   "source": [
    "Python provides many built-in methods and functions that operate on lists. Common list methods and functions include:\n",
    "\n",
    "| Purpose | Function/Method | Description | Example | Result |\n",
    "|---------|-----------------|-------------|---------|--------|\n",
    "| **Creating/Copying** | `list()` | Creates a new list | `list(\"abc\")` | `['a', 'b', 'c']` |\n",
    "|  | `copy()` | Returns shallow copy | `[1, 2, 3].copy()` | `[1, 2, 3]` (new list) |\n",
    "| **Adding Items** | `append()` | Adds single item to end | `[1, 2].append(3)` | `[1, 2, 3]` |\n",
    "|  | `insert()` | Inserts item at position | `[1, 3].insert(1, 2)` | `[1, 2, 3]` |\n",
    "|  | `extend()` | Adds all items from iterable | `[1, 2].extend([3, 4])` | `[1, 2, 3, 4]` |\n",
    "| **Removing Items** | `remove()` | Removes first occurrence of value | `[1, 2, 3].remove(2)` | `[1, 3]` |\n",
    "|  | `pop()` | Removes and returns last item | `[1, 2, 3].pop()` | Returns `3`, list becomes `[1, 2]` |\n",
    "|  | `pop(index)` | Removes and returns item at index | `[1, 2, 3].pop(0)` | Returns `1`, list becomes `[2, 3]` |\n",
    "|  | `clear()` | Removes all items | `[1, 2, 3].clear()` | `[]` |\n",
    "| **Searching/Counting** | `index()` | Returns index of first occurrence | `[1, 2, 3].index(2)` | `1` |\n",
    "|  | `count()` | Counts occurrences of value | `[1, 2, 2, 3].count(2)` | `2` |\n",
    "| **Sorting/Reversing** | `sort()` | Sorts list in place | `[3, 1, 2].sort()` | `[1, 2, 3]` |\n",
    "|  | `sorted()` | Returns new sorted list | `sorted([3, 1, 2])` | `[1, 2, 3]` (original unchanged) |\n",
    "|  | `reverse()` | Reverses list in place | `[1, 2, 3].reverse()` | `[3, 2, 1]` |\n",
    "|  | `reversed()` | Returns reverse iterator | `list(reversed([1, 2, 3]))` | `[3, 2, 1]` |\n",
    "| **Information/Statistics** | `len()` | Returns number of items | `len([1, 2, 3])` | `3` |\n",
    "|  | `max()` | Returns largest item | `max([1, 2, 3])` | `3` |\n",
    "|  | `min()` | Returns smallest item | `min([1, 2, 3])` | `1` |\n",
    "|  | `sum()` | Returns sum of numeric items | `sum([1, 2, 3])` | `6` |\n",
    "\n",
    "Let's explore some of these with examples:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "963f5b2b-f9b5-43b9-bc25-97f689c5ef99",
   "metadata": {},
   "source": [
    "#### List Functions\n",
    "\n",
    "Python provides several built-in functions that work with lists to perform common operations, such as finding the length (`len()`), maximum (`max()`), minimum (`min()`), sum (`sum()`), sorting (`sorted()`), and type conversion (`list()`)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03cb7d4e-d19c-47e5-a96c-afff8b252003",
   "metadata": {},
   "source": [
    "The `len` function returns the **length** of a list as the count of number of the elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "eca29c8a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "There are 5 numbers in [1, 2, 3, 4, 5].\n",
      "There are 5 fruits in ['apple', 'banana', 'cherry', 'date', 'elderberry'].\n"
     ]
    }
   ],
   "source": [
    "numbers = [1, 2, 3, 4, 5]\n",
    "fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']\n",
    "print(f\"There are {len(numbers)} numbers in {numbers}.\")\n",
    "print(f\"There are {len(fruits)} fruits in {fruits}.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cc84a14-09fc-4244-8f80-a57b92473f2a",
   "metadata": {},
   "source": [
    "The length of an empty list is `0`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "38541506",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "empty = []\n",
    "len(empty)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe756ee1-c51a-4086-b10f-2a10cc46a50d",
   "metadata": {},
   "source": [
    "No other mathematical operators work with lists, but the built-in function `sum` adds up the elements.\n",
    "And the `min` and `max` functions find the smallest and largest elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "96620f93",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n",
      "1\n",
      "6\n"
     ]
    }
   ],
   "source": [
    "num1 = [1, 2, 3]\n",
    "num2 = [4, 5, 6]\n",
    "\n",
    "print(sum(num1))\n",
    "print(min(num1))\n",
    "print(max(num2))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a65d26fe-cb4d-4004-a635-2e5cda2d49ea",
   "metadata": {},
   "source": [
    "### List Modifying Methods\n",
    "\n",
    "Lists have built-in methods that allow you to modify them in place, such as adding, removing, or reordering elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "id": "11a2ecd1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original list:\t\t ['a', 'b', 'c', 'd']\n",
      "After append 'e':\t ['a', 'b', 'c', 'd', 'e']\n",
      "After extend ['f', 'g']: ['a', 'b', 'c', 'd', 'e', 'f', 'g']\n",
      "After insert at 0:\t ['z', 'a', 'b', 'c', 'd', 'e', 'f', 'g']\n",
      "After remove 'z':\t ['a', 'b', 'c', 'd', 'e', 'f', 'g']\n",
      "Popped: g, Remaining:\t ['a', 'b', 'c', 'd', 'e', 'f']\n",
      "After clear:\t\t []\n"
     ]
    }
   ],
   "source": [
    "letters = ['a', 'b', 'c', 'd']\n",
    "print(f\"Original list:\\t\\t {letters}\")\n",
    "\n",
    "# append() - Adds element to the end\n",
    "letters.append('e')\n",
    "print(f\"After append 'e':\\t {letters}\")\n",
    "\n",
    "# extend() - Appends all elements from another list\n",
    "letters.extend(['f', 'g'])\n",
    "print(f\"After extend ['f', 'g']: {letters}\")\n",
    "\n",
    "# insert() - Inserts element at specific position\n",
    "letters.insert(0, 'z')\n",
    "print(f\"After insert at 0:\\t {letters}\")\n",
    "\n",
    "# remove() - Removes first occurrence of element\n",
    "letters.remove('z')\n",
    "print(f\"After remove 'z':\\t {letters}\")\n",
    "\n",
    "# pop() - Removes and returns element at index (or last if no index)\n",
    "last_item = letters.pop()\n",
    "print(f\"Popped: {last_item}, Remaining:\\t {letters}\")\n",
    "\n",
    "# clear() - Removes all elements\n",
    "temp = [1, 2, 3]\n",
    "temp.clear()\n",
    "print(f\"After clear:\\t\\t {temp}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23a0f293",
   "metadata": {},
   "source": [
    "**Note:** If you try to `remove()` an element that doesn't exist, Python raises a `ValueError`. If you try to `pop()` from an empty list, Python raises an `IndexError`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc1aac68-8337-4c2b-9771-7b7a82747295",
   "metadata": {},
   "source": [
    "### Iteration Helpers: `enumerate` and `zip`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5501f012-d9c3-48f6-8e8c-9b0ca3c1d3dd",
   "metadata": {},
   "source": [
    "**Using `enumerate()`**\n",
    "\n",
    "When looping through a list, you sometimes need to know both the **element** and its **index**. The `enumerate()` function returns pairs of `(index, element)` for each item in the list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "1f752a18",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0: apple\n",
      "1: banana\n",
      "2: cherry\n"
     ]
    }
   ],
   "source": [
    "fruits = ['apple', 'banana', 'cherry']\n",
    "\n",
    "for index, fruit in enumerate(fruits):\n",
    "    print(f\"{index}: {fruit}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c23beb37-37e3-41a6-8f13-74d6b82bf3ee",
   "metadata": {},
   "source": [
    "**Using `zip()`**\n",
    "\n",
    "The `zip()` function is useful when you need to loop through two or more lists in parallel. It pairs up elements from each list and returns tuples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "id": "7298f340",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Alice is 25 years old and lives in New York\n",
      "Bob is 30 years old and lives in London\n",
      "Charlie is 35 years old and lives in Tokyo\n"
     ]
    }
   ],
   "source": [
    "names = ['Alice', 'Bob', 'Charlie']\n",
    "ages = [25, 30, 35]\n",
    "cities = ['New York', 'London', 'Tokyo']\n",
    "\n",
    "for name, age, city in zip(names, ages, cities):\n",
    "    print(f\"{name} is {age} years old and lives in {city}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "9cf32314",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<zip object at 0x110726cc0>\n",
      "[('Alice', 25, 'New York'), ('Bob', 30, 'London'), ('Charlie', 35, 'Tokyo')]\n",
      "{'Alice': 25, 'Bob': 30, 'Charlie': 35}\n"
     ]
    }
   ],
   "source": [
    "test = zip(names, ages, cities)\n",
    "print(test)         # This will print a zip object, not the contents\n",
    "print(list(test))   # Convert zip object to list to see contents\n",
    "\n",
    "test2  = zip(names, ages)\n",
    "print(dict(test2))  # Convert to dict to see contents (keys from names, values from ages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "366d2980",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Using List Methods\n",
    "numbers = [3, 1, 4, 1, 5, 9, 2, 6]\n",
    "# 1. Count how many times 1 appears in the list \n",
    "# 2. Append the number 7 to the end\n",
    "# 3. Remove the first occurrence of 1\n",
    "# 4. Find the index of the number 5\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "id": "5501d681",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Count of 1s: 2\n",
      "List after appending 7: [3, 1, 4, 1, 5, 9, 2, 6, 7]\n",
      "List after removing first occurrence of 1: [3, 4, 1, 5, 9, 2, 6, 7]\n",
      "Index of 5: 3\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "numbers = [3, 1, 4, 1, 5, 9, 2, 6]\n",
    "count_ones = numbers.count(1)\n",
    "print(f\"Count of 1s: {count_ones}\")\n",
    "numbers.append(7)\n",
    "print(f\"List after appending 7: {numbers}\")\n",
    "numbers.remove(1)\n",
    "print(f\"List after removing first occurrence of 1: {numbers}\")\n",
    "index_of_five = numbers.index(5)\n",
    "print(f\"Index of 5: {index_of_five}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 114,
   "id": "5ee315ec",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Using enumerate() and zip()\n",
    "### with the following lists:\n",
    "fruits = ['apple', 'banana', 'cherry']\n",
    "prices = [10, 20, 30]\n",
    "# 1. Use enumerate() to print the index and fruit name \n",
    "#    as \"0: apple\", \"1: banana\", etc.\n",
    "# 2. Use zip() to print each fruit with its corresponding price \n",
    "#    as \"apple costs 10\", \"banana costs 20\", etc.\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c19497c",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0: apple\n",
      "1: banana\n",
      "2: cherry\n",
      "apple costs 10\n",
      "banana costs 20\n",
      "cherry costs 30\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "\n",
    "fruits = ['apple', 'banana', 'cherry']\n",
    "prices = [10, 20, 30]\n",
    "\n",
    "for index, fruit in enumerate(fruits):\n",
    "    print(f\"{index}: {fruit}\")      \n",
    "\n",
    "for fruit, price in zip(fruits, prices):\n",
    "    print(f\"{fruit} costs {price}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae4bd6e9-f261-4908-a8fe-4e07b0ae31c5",
   "metadata": {},
   "source": [
    "### List Unpacking\n",
    "\n",
    "**Unpacking** is a Python feature that allows you to assign **multiple values** from a list (or any iterable) to **multiple variables** in a single statement. Instead of accessing elements one by one with indexing, you can extract them all at once."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01a6b198",
   "metadata": {},
   "source": [
    "This makes your code more readable and **Pythonic**, especially when working with structured data."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e285933c",
   "metadata": {},
   "source": [
    "#### Basic Unpacking"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "7b199cc7-36e2-4082-9aa5-19d1136168cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Without unpacking (verbose)\n",
    "point = [10, 20, 30]\n",
    "x = point[0]\n",
    "y = point[1]\n",
    "z = point[2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "abc07bbe-0852-4bef-b1d8-8d674f00c837",
   "metadata": {},
   "outputs": [],
   "source": [
    "# With unpacking (concise)\n",
    "point = [10, 20, 30]\n",
    "x, y, z = point         ### assigns 10 to x, 20 to y, 30 to z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "e6e89e67",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "first=h, second=i\n"
     ]
    }
   ],
   "source": [
    "# Unpacking actually works with any iterable\n",
    "\n",
    "first, second = \"hi\"\n",
    "print(f\"first={first}, second={second}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3754c88d",
   "metadata": {},
   "source": [
    "#### With Star Operator\n",
    "\n",
    "- Using `*` (the **unpacking operator**) allows you to capture multiple elements.\n",
    "- You can only have one `*variable` per unpacking statement."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "664bf954",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First: 1\n",
      "Rest: [2, 3, 4, 5]\n"
     ]
    }
   ],
   "source": [
    "# Capture the first element and the rest\n",
    "numbers = [1, 2, 3, 4, 5]\n",
    "first, *rest = numbers\n",
    "\n",
    "print(f\"First: {first}\")\n",
    "print(f\"Rest: {rest}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "458191e0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First: 1, Middle: [2, 3, 4], Last: 5\n"
     ]
    }
   ],
   "source": [
    "# Capture first, last, and middle\n",
    "first, *middle, last = numbers\n",
    "print(f\"First: {first}, Middle: {middle}, Last: {last}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "id": "c918f47c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Most: [1, 2, 3, 4], Last: 5\n"
     ]
    }
   ],
   "source": [
    "# Capture last element\n",
    "*most, last = numbers\n",
    "print(f\"Most: {most}, Last: {last}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "a35aaa3a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First: P\n",
      "Middle: ['y', 't', 'h', 'o']\n",
      "Last: n\n"
     ]
    }
   ],
   "source": [
    "### Unpacking works with all iterables, including \n",
    "### a string into individual characters\n",
    "\n",
    "word = \"Python\"\n",
    "first, *middle, last = word\n",
    "\n",
    "print(f\"First: {first}\")\n",
    "print(f\"Middle: {middle}\")\n",
    "print(f\"Last: {last}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa16de5a",
   "metadata": {},
   "source": [
    "#### Unpacking in Function Calls\n",
    "\n",
    "The `*` operator can also unpack a list into function **arguments**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "23d5fe56",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Alice is 30 years old and lives in New York\n"
     ]
    }
   ],
   "source": [
    "# Unpack a list as function arguments\n",
    "def display_info(name, age, city):\n",
    "    print(f\"{name} is {age} years old and lives in {city}\")\n",
    "\n",
    "person = ['Alice', 30, 'New York']\n",
    "display_info(*person)  ### unpacks to display_info('Alice', 30, 'New York')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "76cd75e3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 3 4 5\n",
      "1-2-3-4-5\n"
     ]
    }
   ],
   "source": [
    "# Useful with functions like print\n",
    "values = [1, 2, 3, 4, 5]\n",
    "print(*values)              # Prints: 1 2 3 4 5 (separated by spaces)\n",
    "print(*values, sep='-')     # Prints: 1-2-3-4-5"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3387bdb",
   "metadata": {},
   "source": [
    "**Common use cases:**\n",
    "- Swapping values: `a, b = b, a`\n",
    "- Parsing CSV data: `name, age, email = row.split(',')`\n",
    "- Function returns: `min_val, max_val = find_min_max(numbers)`\n",
    "- Ignoring values: `first, *_, last = data` (use `_` for values you don't need)\n",
    "\n",
    "Note: \n",
    "- `_` in `*_` is used as a **throwaway variable name**, it's a Python convention meaning \"I don't care about this value.\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e9781b0",
   "metadata": {},
   "source": [
    "## Aliasing and Copying\n",
    "\n",
    "When working with lists, it's crucial to understand the difference between aliasing, shallow copies, and deep copies. These concepts determine whether changes to one list affect another. The table below summarizes the key differences between aliasing, shallow copy, and deep copy:\n",
    "\n",
    "| Concept      | Outer Object | Inner Objects | Syntax                    | Changes affect original? |\n",
    "| ------------ | ------------ | ------------- | ------------------------- | ------------------------ |\n",
    "| Aliasing     | **Same**     | **Same**      | `b = a`                   | Yes                      |\n",
    "| Shallow Copy | **New**      | **Same**      | `[:]`, `copy()`, `list()` | Sometimes (when nested)  |\n",
    "| Deep Copy    | **New**      | **New**       | `copy.deepcopy()`         | No                       |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3383bdc",
   "metadata": {},
   "source": [
    "### Aliasing\n",
    "\n",
    "Aliasing occurs when two or more variables point to the same object in memory. When you do a **variable assignment** using `=` in Python, you're not **copying** the object—you're creating another variable that points to the **same object**. This second variable is an **alias**.\n",
    "\n",
    "Both variables refer to the same list object, so any modification through either variable affects the same underlying list:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "59ba7aa8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original:\t [999, 2, 3, 4, 5]\n",
      "Alias:\t\t [999, 2, 3, 4, 5]\n",
      "ID of original:\t 4570906048\n",
      "ID of alias:\t 4570906048\n",
      "Same object?\t True\n"
     ]
    }
   ],
   "source": [
    "# Aliasing: both variables point to the same list\n",
    "original = [1, 2, 3, 4, 5]\n",
    "alias = original    ### NOT a copy!\n",
    "\n",
    "alias[0] = 999      ### Modify through the alias\n",
    "\n",
    "print(\"Original:\\t\", original)  # check to see if original is modified\n",
    "print(\"Alias:\\t\\t\", alias)        \n",
    "\n",
    "print(\"ID of original:\\t\", id(original))    ### check the memory address of original\n",
    "print(\"ID of alias:\\t\", id(alias))          ### check the memory address of alias\n",
    "\n",
    "print(\"Same object?\\t\", original is alias)  # True"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48707a9d",
   "metadata": {},
   "source": [
    "Contrast with **immutable** strings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "79650416",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Chen 4569344368\n",
      "Chen 4569344368\n",
      "Alice 4569343792\n",
      "Chen 4569344368\n",
      "Same ID? False\n"
     ]
    }
   ],
   "source": [
    "# Contrast with immutable strings\n",
    "from os import name\n",
    "\n",
    "\n",
    "name1 = \"Chen\"\n",
    "print(name1, id(name1))         # \"Chen\"\n",
    "\n",
    "name2 = name1                   # Alias created\n",
    "print(name2, id(name2))         # \"Chen\" - same object\n",
    "\n",
    "name2 = \"Alice\"                 # Creates NEW object, reassigns name2\n",
    "print(name2, id(name2))         # \"Alice\" - NEW object\n",
    "\n",
    "print(name1, id(name1))           # \"Chen\" - UNCHANGED!\n",
    "print(\"Same ID?\", id(name1) == id(name2))  # Check if name1 and name2 point to the same object (should be False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2816d85",
   "metadata": {},
   "source": [
    "Aliasing is useful for efficiency (no copying needed), but can cause unexpected behavior if you modify mutable objects, thinking you have independent copies."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47259663",
   "metadata": {},
   "source": [
    "### Shallow Copy\n",
    "\n",
    "To create a shallow copy, you can use \n",
    "- list slicing `[:]`, \n",
    "- the `.copy()` method, \n",
    "- the `list()` function, or \n",
    "- `copy.copy()`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "id": "0084ced0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import copy\n",
    "\n",
    "a = [1, 2, 3]\n",
    "\n",
    "b = a[:]          # list slicing\n",
    "b = a.copy()      # copy() method\n",
    "b = list(a)       # list() function\n",
    "b = copy.copy(a)  # copy module"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b7bf643",
   "metadata": {},
   "source": [
    "All these methods create a new list object, but if the list contains other mutable objects (like nested lists), those nested objects are not copied—only their references are copied.\n",
    "\n",
    "A shallow copy creates a new object while retaining references to the objects contained in the original. It only copies the top-level structure without duplicating nested elements. \n",
    "\n",
    "For simple **1-D lists** (containing only **immutable** objects like numbers, strings, or booleans), shallow copying works perfectly fine and you shall see the new lists all have different ID's."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "5509ba45",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['a', 'b', 'c', 'd'] 4570940096\n",
      "['a', 'b', 'c', 'd'] 4570952128\n",
      "['a', 'b', 'c', 'd'] 4570941184\n",
      "['a', 'b', 'c', 'd'] 4570940288\n",
      "All copies have the same contents? True\n",
      "letters_copy1 is letters? False\n",
      "letters_copy2 is letters? False\n",
      "letters_copy3 is letters? False\n",
      "letters_copy4 is letters? False\n"
     ]
    }
   ],
   "source": [
    "# four ways to create a shallow copy\n",
    "import copy\n",
    "\n",
    "letters = ['a', 'b', 'c', 'd']\n",
    "\n",
    "letters_copy1 = letters[:]          # list slicing\n",
    "letters_copy2 = letters.copy()      # the copy() method\n",
    "letters_copy3 = list(letters)       # the list function\n",
    "letters_copy4 = copy.copy(letters)  # using the copy module\n",
    "    \n",
    "print(letters_copy1, id(letters_copy1))\n",
    "print(letters_copy2, id(letters_copy2))\n",
    "print(letters_copy3, id(letters_copy3))\n",
    "print(letters_copy4, id(letters_copy4))\n",
    "\n",
    "print(\"All copies have the same contents?\", letters_copy1 == letters_copy2 == letters_copy3 == letters_copy4)  # True, contents are the same\n",
    "print(\"letters_copy1 is letters?\", letters_copy1 is letters)  # False, different objects in memory\n",
    "print(\"letters_copy2 is letters?\", letters_copy2 is letters)  # False, different objects in memory\n",
    "print(\"letters_copy3 is letters?\", letters_copy3 is letters)  # False, different objects in memory\n",
    "print(\"letters_copy4 is letters?\", letters_copy4 is letters)  # False, different objects in memory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "id": "a95c3cea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Same value?\t True\n",
      "Same object?\t False\n",
      "update shallow:\t [1, 2, 3, 4, 999]\n",
      "Original:\t [1, 2, 3, 4, 5]\n",
      "Shallow:\t [1, 2, 3, 4, 999]\n"
     ]
    }
   ],
   "source": [
    "### simpler example: shallow copy works fine for 1-D lists: Update\n",
    "original = [1, 2, 3, 4, 5]\n",
    "shallow = original[:]  # Creates a new list\n",
    "\n",
    "print(\"Same value?\\t\", original == shallow)   # True\n",
    "print(\"Same object?\\t\", original is shallow)  # False\n",
    "\n",
    "# Modify the shallow copy\n",
    "shallow[4] = 999\n",
    "print(\"update shallow:\\t\", shallow)\n",
    "\n",
    "print(\"Original:\\t\", original)  # Original is unchanged\n",
    "print(\"Shallow:\\t\", shallow)    # Only the copy is modified\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fb75f91",
   "metadata": {},
   "source": [
    "However, for nested lists (lists containing other lists), shallow copy shares references to the nested objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "id": "8a1cdeaa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "original-shallow same value?\t True\n",
      "original-shallow same object?\t False\n",
      "update shallow (99):\t\t [[1, 2, 99], [3, 4]]\n",
      "is original updated?\t\t [[1, 2, 99], [3, 4]]\n",
      "same nested object?\t\t True\n"
     ]
    }
   ],
   "source": [
    "# Using copy.copy() with nested lists\n",
    "import copy\n",
    "original = [[1, 2], [3, 4]]\n",
    "shallow = copy.copy(original)\n",
    "\n",
    "print(\"original-shallow same value?\\t\", original == shallow)   # True - contents are the same\n",
    "print(\"original-shallow same object?\\t\", id(original) == id(shallow))  # False - different list objects\n",
    "\n",
    "shallow[0].append(99)\n",
    "print(\"update shallow (99):\\t\\t\", shallow)\n",
    "print(\"is original updated?\\t\\t\", original)  # [[1, 2, 99], [3, 4]] - nested list affected!\n",
    "\n",
    "print(\"same nested object?\\t\\t\", shallow[0] is original[0])  # True - same nested list object"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd8342d4",
   "metadata": {},
   "source": [
    "Now that shallow copy has given us two variable names referencing the same object, which we prefer not to happen in most cases. Same thing happens with using slicing for shallow copy with nested lists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "f4a9d602",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original: [[999, 2, 3], [4, 5, 6]]\n",
      "Shallow: [[999, 2, 3], [4, 5, 6]]\n",
      "Same nested object? True\n"
     ]
    }
   ],
   "source": [
    "# Using slicing with nested lists\n",
    "original = [[1, 2, 3], [4, 5, 6]]\n",
    "shallow = original[:]  # or original.copy()\n",
    "\n",
    "# Modify the nested list\n",
    "shallow[0][0] = 999\n",
    "\n",
    "print(\"Original:\", original)  # Original is also modified!\n",
    "print(\"Shallow:\", shallow)\n",
    "print(\"Same nested object?\", shallow[0] is original[0])  # True - same nested list object"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d09c783a",
   "metadata": {},
   "source": [
    "As you can see, modifying the nested list in `shallow` also affects `original` because they share references to the same inner lists."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5db2e698",
   "metadata": {},
   "source": [
    "### Deep Copy\n",
    "\n",
    "To create a **deep copy** that copies all nested objects recursively, use the `copy` module's `deepcopy()` function. This creates completely independent copies of all nested structures. Deep copy creates a new object and recursively copies all nested objects—everything is independent."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "id": "6167935c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original: [[1, 2, 3], [4, 5, 6]]\n",
      "Deep copy: [[1, 2, 3], [4, 5, 6]]\n",
      "original-deep same value? True\n",
      "original-deep same object? False\n",
      "same nested object? False\n",
      "Original: [[1, 2, 3], [4, 5, 6]]\n",
      "Deep copy: [[999, 2, 3], [4, 5, 6]]\n"
     ]
    }
   ],
   "source": [
    "import copy\n",
    "\n",
    "original = [[1, 2, 3], [4, 5, 6]]\n",
    "deep = copy.deepcopy(original)\n",
    "\n",
    "print(\"Original:\", original)  # Original is unchanged\n",
    "print(\"Deep copy:\", deep)    # Only the deep copy is modified\n",
    "\n",
    "print(\"original-deep same value?\", original == deep)   # True - contents are the same\n",
    "print(\"original-deep same object?\", id(original) == id(deep))  # False - different list objects\n",
    "print(\"same nested object?\", deep[0] is original[0])  # False - different nested list objects\n",
    "\n",
    "# Modify the nested list\n",
    "print\n",
    "deep[0][0] = 999\n",
    "\n",
    "print(\"Original:\", original)  # Original is unchanged\n",
    "print(\"Deep copy:\", deep)    # Only the deep copy is modified"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "id": "e182a1b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4570904832\n",
      "4570943680\n",
      "[[1, 2], [3, 4]]\n",
      "[[1, 2, 99], [3, 4]]\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "### simpler example with deep copy\n",
    "import copy\n",
    "original = [[1, 2], [3, 4]]\n",
    "deep = copy.deepcopy(original)\n",
    "\n",
    "print(id(original))\n",
    "print(id(deep))\n",
    "\n",
    "deep[0].append(99)\n",
    "print(original)  # [[1, 2], [3, 4]] - original unchanged!\n",
    "print(deep)      # [[1, 2, 99], [3, 4]] - only deep copy changed\n",
    "print(deep[0] is original[0])    # False - different nested list objects"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce98b3d5",
   "metadata": {},
   "source": [
    "## Advanced List Concepts\n",
    "\n",
    "Understanding how Python manages list objects in memory is crucial for avoiding subtle bugs. This section explores object identity, aliasing, and how lists behave when passed to functions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d9d27df5",
   "metadata": {},
   "source": [
    "### Objects and Values\n",
    "\n",
    "If we run these assignment statements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "aa547282",
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 'banana'\n",
    "b = 'banana'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "33d020aa",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "We know that `a` and `b` both refer to a string, but we don't know whether they refer to the *same* string. \n",
    "There are two possible states, shown in the following figure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "95a2aded",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [],
   "source": [
    "from diagram import Frame, Stack\n",
    "\n",
    "s = 'banana'\n",
    "bindings = [Binding(Value(name), Value(repr(s))) for name in 'ab']\n",
    "frame1 = Frame(bindings, dy=-0.25)\n",
    "\n",
    "binding1 = Binding(Value('a'), Value(repr(s)), dy=-0.11)\n",
    "binding2 = Binding(Value('b'), draw_value=False, dy=0.11)\n",
    "frame2 = Frame([binding1, binding2], dy=-0.25)\n",
    "\n",
    "stack = Stack([frame1, frame2], dx=1.7, dy=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "3d75a28c",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 285x76 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "width, height, x, y = [2.85, 0.76, 0.17, 0.51]\n",
    "ax = diagram(width, height)\n",
    "bbox = stack.draw(ax, x, y)\n",
    "# adjust(x, y, bbox)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2f0b0431",
   "metadata": {},
   "source": [
    "In the diagram on the left, `a` and `b` refer to two different objects that have the\n",
    "same value. In the diagram on the right, they refer to the same object.\n",
    "To check whether two variables refer to the same object, you can use the `is` operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "a37e37bf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = 'banana'\n",
    "b = 'banana'\n",
    "a is b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1eb0e36",
   "metadata": {},
   "source": [
    "In this example, Python only created one string object, and both `a`\n",
    "and `b` refer to it.\n",
    "But when you create two lists, you get two objects."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "d6af7316",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = [1, 2, 3]\n",
    "b = [1, 2, 3]\n",
    "a is b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8d4c3d4",
   "metadata": {},
   "source": [
    "So the state diagram looks like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "dea08b82",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "t = [1, 2, 3]\n",
    "binding1 = Binding(Value('a'), Value(repr(t)))\n",
    "binding2 = Binding(Value('b'), Value(repr(t)))\n",
    "frame = Frame([binding1, binding2], dy=-0.25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "id": "7e66ee69",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAIgAAABgCAYAAADGrTq9AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAABQdJREFUeJzt3EkobXEcB/CfKTJcHuHVXaCQpJeQedgoNkToEWUhC6VkTAorGVaEWL0oJbKShSELr2fMs7AQXTLkGrIwZZ5e/39ZvFy/e6/cAd9PnQznnM6953zP//yOe34snp6engjgFZavzQBAQEArjCDAQkCAhYAACwEBFgICLAQEWAgIsBAQYCEgwEJAgIWAAAsBARYCAiwEBFgICLAQEGAhIMCypjc6Pz9/66pgYk5OTjovixEEWAgIsBAQYCEgwEJAgIWAAAsBARYCAiwEBFgICLAQEC0UCgVFRkbS2NiY/Pnv37+UmJhInp6elJOTQ7oaGhqimJgYioiIkFN7e7tO683Pz8v1xBQeHk4lJSV0c3Mj583MzMjfi9d4cnJChmDx1v8Pou9nMQUFBbS+vk63t7ekVCqps7NT7mRT2Nrakjs3ODiYAgICyNLy9fNEoVDQzs4Oubi4yJ/VajXt7+/T8vIyTUxMUH9/v07bnJubIx8fH/meT09PKT4+njo6OiguLo5d7/LykmxsbOT0+PhIeXl5FB0dTcXFxa++xg/5WUxzczNNTU3R7OysfIONjY1kKu7u7uTg4ECjo6PU29tLKysrcufrQqlUUlhYGNna2uq1TTEKPZ8Qzs7O5O/vLw+qNvb29jIcgji5rq6uyMLCgsz+01x9DQ4O0sDAAF1fX8vJzc3txTILCwu0t7dnrJckD/bx8bEMyuTkJGVlZdH3798Nvt3V1VX5XltbW3Vafnt7W17ONjc3KSkpiQoLC8lYjDKCiFGju7tbXofFNVWMHs/X0a9GrVZTdna2DIcIqC68vLzkJVGlUsn9Njw8TJ9qBBEFlLjuubq6ymHy169fGpcTRZgxXFxcyPpBHKxv377JekBbLfIeRO2SmppKlZWVlJ6ervf6jo6OlJGRIUfjzMxM+jQjiKj6/fz8KCQkRA6RP378IFM6OjqSxV9ycjLl5+dTYGDgu4RjcXGRUlJSNM47ODiQ80pLSyk3N/e/eeKyGhoaqnG9jY0Nuru7k9+Lk2tkZISCgoLoU40gosjq6en573d1dXVkKt7e3nJ6C5VKJQ+0CJiopcTIU15eLusCUXTa2dlpXK+hoYF2d3epq6tLTkJRUZG8KxEBsbbWfCh+//4tL89WVlZ0f39PCQkJVFVVRcZitNvcj0qhxy1kWVmZLHSjoqL02kZbW5u8wxG1iaFfo763uQiIFr6+vuTh4UH19fXy8mhOROEq6pnDw0NaWlqSQdEFAgIf7w9l8DEhIMBCQICFgAALAQEWAgIsBARYCAiwEBBgISDAQkCAhYAACwEB0wfEkH0bYFgYQbRQoHHK8E+UiZ1cUVFB4+Pj8oHh6upq+vnzJ5kKGqeczK8vRjT7/PnzR/Z2iOcqRSOReJzf1I1Tog1DtELq+lS7UqmU09raml7bFO/3mb6NU88+deOUeHpcEO2Hop90enr6RUDQOPVFG6c0MeZZYE7UaJzSrK+vj2pqauTZIB62bWpqerEMGqe+aOOU8PDwQLGxsZSWlkYtLS0mqz8ENE6ZWQ1ydnYmv9bW1pI5QOOU7tAXo4UCjVPorOP4onEKAflqnNA4Be8Fn8UACwEBFgICLAQEWAgIsBAQYCEgYJg/tcPXgBEEWAgIsBAQYCEgwEJAgIWAAAsBARYCAiwEBFgICLAQEGAhIMBCQICFgAALAQEWAgIsBARYCAiwEBBgISBAnH9XF5b1n3vZ8wAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 116x76 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "width, height, x, y = [1.16, 0.76, 0.21, 0.51]\n",
    "ax = diagram(width, height)\n",
    "bbox = frame.draw(ax, x, y)\n",
    "# adjust(x, y, bbox)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc115a9f",
   "metadata": {},
   "source": [
    "In this case we would say that the two lists are **equivalent**, because they have the same elements, but not **identical**, because they are not the same object. \n",
    "If two objects are identical, they are also equivalent, but if they are equivalent, they are not necessarily identical."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "d0f49fa5",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Objects and Values\n",
    "x = [10, 20, 30]\n",
    "y = x\n",
    "z = [10, 20, 30]\n",
    "# 1. Check if x and y refer to the same object using \"is\"\n",
    "# 2. Check if x and z have the same value using \"==\"\n",
    "# 3. Check if x and z refer to the same object using \"is\"\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "id": "ed132eb8",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x is y: True\n",
      "x == z: True\n",
      "x is z: False\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "x = [10, 20, 30]\n",
    "y = x\n",
    "z = [10, 20, 30]\n",
    "\n",
    "same_object_xy = x is y\n",
    "same_value_xz = x == z\n",
    "same_object_xz = x is z\n",
    "\n",
    "print(f\"x is y: {same_object_xy}\")  # True - same object (alias)\n",
    "print(f\"x == z: {same_value_xz}\")   # True - same value (equivalent)\n",
    "print(f\"x is z: {same_object_xz}\")  # False - different objects"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a58db021",
   "metadata": {},
   "source": [
    "### Aliasing\n",
    "\n",
    "If `a` refers to an object and you assign `b = a`, then both variables refer to the same object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "d6a7eb5b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = [1, 2, 3]\n",
    "b = a\n",
    "b is a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6ab3262",
   "metadata": {},
   "source": [
    "So the state diagram looks like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "dd406791",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "t = [1, 2, 3]\n",
    "binding1 = Binding(Value('a'), Value(repr(t)), dy=-0.11)\n",
    "binding2 = Binding(Value('b'), draw_value=False, dy=0.11)\n",
    "frame = Frame([binding1, binding2], dy=-0.25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "id": "552e1e1e",
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 111x81 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "width, height, x, y = [1.11, 0.81, 0.17, 0.56]\n",
    "ax = diagram(width, height)\n",
    "bbox = frame.draw(ax, x, y)\n",
    "# adjust(x, y, bbox)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c676fde9",
   "metadata": {},
   "source": [
    "The association of a variable with an object is called a **reference**.\n",
    "In this example, there are two references to the same object.\n",
    "\n",
    "An object with more than one reference has more than one name, so we say the object is **aliased**.\n",
    "If the aliased object is mutable, changes made with one name affect the other.\n",
    "In this example, if we change the object `b` refers to, we are also changing the object `a` refers to."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "id": "6e3c1b24",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[5, 2, 3]"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b[0] = 5\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e3ef0537",
   "metadata": {},
   "source": [
    "So we would say that `a` \"sees\" this change.\n",
    "Although this behavior can be useful, it is error-prone.\n",
    "In general, it is safer to avoid aliasing when you are working with mutable objects.\n",
    "\n",
    "For immutable objects like strings, aliasing is not as much of a problem.\n",
    "In this example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "id": "dad8a246",
   "metadata": {},
   "outputs": [],
   "source": [
    "c = 'banana'\n",
    "d = 'banana'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "952bbf60",
   "metadata": {},
   "source": [
    "It almost never makes a difference whether `a` and `b` refer to the same\n",
    "string or not."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "id": "45f181b7",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: Aliasing\n",
    "a = [1, 2, 3]\n",
    "# 1. Create an alias b that points to the same list as a\n",
    "# 2. Modify the list through b by changing the first element to 99\n",
    "# 3. Create a true copy c using slicing\n",
    "# 4. Modify c and observe that a remains unchanged\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "id": "1085deea",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a after aliasing: [99, 2, 3]\n",
      "b: [99, 2, 3]\n",
      "Same object? True\n",
      "\n",
      "a after copying: [99, 2, 3]\n",
      "c: [99, 777, 3]\n",
      "Same object? False\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "a = [1, 2, 3]\n",
    "b = a  # alias\n",
    "b[0] = 99\n",
    "\n",
    "print(f\"a after aliasing: {a}\")\n",
    "print(f\"b: {b}\")\n",
    "print(f\"Same object? {a is b}\")\n",
    "\n",
    "c = a[:]  # true copy\n",
    "c[1] = 777\n",
    "\n",
    "print(f\"\\na after copying: {a}\")\n",
    "print(f\"c: {c}\")\n",
    "print(f\"Same object? {a is c}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35045bef",
   "metadata": {},
   "source": [
    "### List Arguments in Functions\n",
    "\n",
    "When you pass a list to a function, the function gets a reference to the\n",
    "list. If the function modifies the list, the caller sees the change. For\n",
    "example, `pop_first` uses the list method `pop` to remove the first element from a list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "id": "613b1845",
   "metadata": {},
   "outputs": [],
   "source": [
    "def pop_first(lst):\n",
    "    return lst.pop(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4953b0f9",
   "metadata": {},
   "source": [
    "We can use it like this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "id": "3aff3598",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'a'"
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "letters = ['a', 'b', 'c']\n",
    "pop_first(letters)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef5d3c1e",
   "metadata": {},
   "source": [
    "The return value is the first element, which has been removed from the list -- as we can see by displaying the modified list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "id": "c10e4dcc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['b', 'c']"
      ]
     },
     "execution_count": 100,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "letters"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5288e08",
   "metadata": {},
   "source": [
    "In this example, the parameter `lst` and the variable `letters` are aliases for the same object, so the state diagram looks like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "id": "a13e72c7",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [],
   "source": [
    "from diagram import make_list, Binding, Value\n",
    "\n",
    "lst = make_list('abc', dy=-0.3, offsetx=0.1)\n",
    "binding1 = Binding(Value('letters'), draw_value=False)\n",
    "frame1 = Frame([binding1], name='__main__', loc='left')\n",
    "\n",
    "binding2 = Binding(Value('lst'), draw_value=False, dx=0.61, dy=0.35)\n",
    "frame2 = Frame([binding2], name='pop_first', loc='left', offsetx=0.08)\n",
    "\n",
    "stack = Stack([frame1, frame2], dx=-0.3, dy=-0.5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "id": "ac62b394",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[np.float64(2.05), np.float64(1.22), np.float64(1.06), np.float64(0.85)]"
      ]
     },
     "execution_count": 102,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAOAAAACQCAYAAAARD2cgAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAADtVJREFUeJztnQuwVeMbxt9ujlCuuauIEqVIQ+4kIpcoROSayWVIZaZxq2lMY5gxhHGbiImEIUYuSUIlYVxSklsRuauOS4X6z++d/zpzOu1zzj5nr3W+tfZ+fjN79j7n7LP32muv5/ve7/ve9/karV+/fr0JIYLQOMzbCiEkQCECox5QiIBIgEIERAIUIiASoBABkQCFCIgEKERAJEAhAiIBChEQCVCIgEiAQgREAhQiIBKgEAGRAIUIiAQoREAkQCECIgEKERAJUIiASIBCBEQCFCIgEqBIBY0aNbIVK1b44xNPPNE+++yzGp8/evRoW716tWWdRrIlFGkR4O+//25bbbVVIs9PK+oBRepo27atffjhh/745ptvto4dO1rXrl39tnTpUhsyZIj/7fDDD/ff/fTTT5ZV1AOKVNCoUo+GAKdMmWJt2rSx3Xff3ZYvX27Nmze3v/76yxo3bmybbrqpekAhkqZly5a211572bnnnmv333+//fbbby6+YkIhqEgtTZo0sblz59rQoUM9zDz44IPtrbfesmKiaegDEKI6ysvL/cZYj9uCBQvsgw8+8MctWrSwlStXZn4SRgIUqWXlypXWv39/+/PPP33MRzh6/vnn+9+GDx9uvXr1ss0228ymTZtm22+/vWURTcIIERCNAYUIiAQoREAkQCECokmYGmAGrphhJjENlBfpec7n/KoHFCIgEqAQAZEAhQiIBChEQCRAIQIiAQoREAlQiIBIgEIERAIUIiCpFyCeH2nPlKByO3L0qomxY8du4OT1wgsv2Lx58xI+OlEbnTp1iuU5RSlAzHnSkjJVKLfccstGAnz33Xfr9Vr//feflSpvvvmmTZ482V566aXMn4fEBHjUUUd50eQRRxxhrVu3thtvvNFefPFFO+yww9x05/bbb6947ogRI6x79+7e2/H8yp6Qlf0i+b+bbrrJevTo4WY9OGaljS+++MKLSI888kg/TrxMAFsF6N27tx166KH2+OOP+/kYN26c//zII4/43ydNmmRHH320V33z3Pnz5/vvH3vsMevTp4/7o2DN8N5779mtt95qBx54oP8/t2+++SbgJzd74okn7Pnnn3dzpaT4+eef7Y8//rCzzjrLttlmG/v8888Lfs1tt93W76+//nr/3jiXnPvKrx09JzMFuQhwu+2285Zq1apVLp7zzjvP7rrrLvv++++tQ4cOtmzZMrcU4KS2atWq4kucMGGCvfzyyzndsk455RS/aH/55Rdr166dLVy40HbZZZckPkLeoS8hKBc/PfUxxxxjDz74oLVv395dvHr27Gl33323devWreJ5kY0C9nqdO3e2K664wn/G/+S2225zcZaVldmcOXNcuISpCHDYsGE2a9YsrwznnOy33362ePHijRzD8iXuyIJoZfr06fb3339bly5dvBHZeuuta/2/8joMMT766CNr1qyZ7bPPPvbjjz/aJ5984uc4DrimuGbh6aef9nP+7LPP1vv18jm/iVZD0BNgrMOXsMcee9hJJ53kgkIwCG7JkiXe67366qsuTL6IdevWuftVdZxzzjl+z4niNb/++uvEBFhXaDE//fRTu/DCCyt+R2u9aNEiF2BtTJ061S8oRByB0Lig4aCDDnLxAWLm8w8ePNiff/zxx+c8D/w/Rkac16o0bRr/17/TTju5lcTHH3/s/i0cb/SdxcHq1att880398ebbLJJrO7YM2bM8IiF74zzlWRP3iACrNwaI8SqP//777/eI1x55ZU+FqJH44sjDM33NXmNtEAwQWMze/bsev8/F+uoUaNy/j268KLPzgXzzjvvuMDoBR566CE75JBDrJgpKyuztWvX+mPu47Ip/Pbbb30oNHPmTG/YaAhPOOEEK/p6QFpLQgpaTi5AwrWsQmtP2DFx4kQfq8GXX37pomS8wt8Ix6MQNPo5gj0RLrnkErvoootst91281aYsO6AAw7Y6L2IFmipERw3el7Cs6oC5L0J2xsiBKXHe+211+ocgtaFnXfe2d5//30PQYmg4op++B64DnfccUe/Dh944AFrCIILkDHQgAEDbN999/WBbt++fS2rENI9+eSTNnLkSLvnnnt8ho7PNH78eP87Pf2pp57qTl44P/O5L7vsMg89CSVx/BozZowNHDjQe3ZaeELLXALkgmFMzdiPsJ7oIc5Qrz4wecbYNwnhReB+xvljboEGhEmoOOD669evn4f5NJZMeDUEckWrgbSv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",
      "text/plain": [
       "<Figure size 204x124 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "width, height, x, y = [2.04, 1.24, 1.06, 0.85]\n",
    "ax = diagram(width, height)\n",
    "bbox1 = stack.draw(ax, x, y)\n",
    "bbox2 = lst.draw(ax, x+0.5, y)\n",
    "bbox = Bbox.union([bbox1, bbox2])\n",
    "adjust(x, y, bbox)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b3b07c9",
   "metadata": {},
   "source": [
    "Passing a reference to an object as an argument to a function creates a form of aliasing.\n",
    "If the function modifies the object, those changes persist after the function is done."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0c5b37ad",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: List Arguments in Functions\n",
    "def add_item(lst, item):\n",
    "    \"\"\"Add item to list and return the list\"\"\"\n",
    "    lst.append(item)\n",
    "    return lst\n",
    "\n",
    "# 1. Create a list with [1, 2, 3], call it original\n",
    "# 2. Call add_item with your list and the value 4, \n",
    "#    save the result in a variable called updated\n",
    "# 3. Print the original list to see if it changed after calling the function\n",
    "# 4. check if original and updated refer to the same object using \"is\"\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "id": "f2c291b0",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original list: [1, 2, 3]\n",
      "Returned list: [1, 2, 3, 4]\n",
      "Original list after function call: [1, 2, 3, 4]\n",
      "Same object? True\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "def add_item(lst, item):\n",
    "    \"\"\"Add item to list and return the list\"\"\"\n",
    "    lst.append(item)\n",
    "    return lst\n",
    "\n",
    "original = [1, 2, 3]\n",
    "print(f\"Original list: {original}\")\n",
    "\n",
    "updated = add_item(original, 4)\n",
    "print(f\"Returned list: {updated}\")\n",
    "print(f\"Original list after function call: {original}\")\n",
    "print(f\"Same object? {updated is original}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c607d32",
   "metadata": {},
   "source": [
    "### The `all()` and `any()` Functions\n",
    "\n",
    "Python provides two built-in functions for checking Boolean conditions across list elements:\n",
    "\n",
    "- **`all(iterable)`**: Returns `True` if all elements are **truthy** (or if the list is empty)\n",
    "- **`any(iterable)`**: Returns `True` if at least one element is **truthy**\n",
    "\n",
    "These are particularly useful when combined with list comprehensions or generator expressions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "id": "de205e19",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "All numbers even? True\n",
      "All numbers positive? True\n",
      "All mixed numbers positive? False\n"
     ]
    }
   ],
   "source": [
    "# all() - check if all elements satisfy a condition\n",
    "numbers = [2, 4, 6, 8, 10]\n",
    "\n",
    "# Check if all numbers are even\n",
    "all_even = all(num % 2 == 0 for num in numbers)\n",
    "print(f\"All numbers even? {all_even}\")\n",
    "\n",
    "# Check if all numbers are positive\n",
    "all_positive = all(num > 0 for num in numbers)\n",
    "print(f\"All numbers positive? {all_positive}\")\n",
    "\n",
    "# With a list containing a negative number\n",
    "mixed = [2, 4, -6, 8]\n",
    "all_positive_mixed = all(num > 0 for num in mixed)\n",
    "print(f\"All mixed numbers positive? {all_positive_mixed}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "id": "c6c6c58c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Has any even number? False\n",
      "Has number > 5? True\n",
      "Has word longer than 10 chars? True\n"
     ]
    }
   ],
   "source": [
    "# any() - check if at least one element satisfies a condition\n",
    "numbers = [1, 3, 5, 7, 9]\n",
    "\n",
    "# Check if any number is even\n",
    "has_even = any(num % 2 == 0 for num in numbers)\n",
    "print(f\"Has any even number? {has_even}\")\n",
    "\n",
    "# Check if any number is greater than 5\n",
    "has_large = any(num > 5 for num in numbers)\n",
    "print(f\"Has number > 5? {has_large}\")\n",
    "\n",
    "# Practical example: check if any word is longer than 10 characters\n",
    "words = ['hello', 'world', 'programming', 'python']\n",
    "has_long_word = any(len(word) > 10 for word in words)\n",
    "print(f\"Has word longer than 10 chars? {has_long_word}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d0a05d2",
   "metadata": {},
   "source": [
    "**Common use cases:**\n",
    "- Validation: `all(score >= 60 for score in scores)` - check if all students passed\n",
    "- Search: `any(word.startswith('py') for word in words)` - check if any word starts with 'py'\n",
    "- Data quality: `all(value is not None for value in data)` - check for missing data\n",
    "\n",
    "**Note:** Both `all()` and `any()` use **short-circuit evaluation**—they stop as soon as the result is determined, making them efficient for large lists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "71d57ca9",
   "metadata": {
    "tags": [
     "thebe-interactive"
    ]
   },
   "outputs": [],
   "source": [
    "### EXERCISE: all() and any() Functions\n",
    "numbers = [2, 4, 6, 8, 9]\n",
    "words = ['python', 'java', 'javascript', 'go']\n",
    "# 1. Check if all numbers are even using list comprehension (print) \n",
    "# 2. Check if any word starts with 'j' using the \n",
    "#    method \".startswith()\" and list comprehension (print)\n",
    "# 3. Check if any number is greater than 10 (print)\n",
    "### Your code starts here:\n",
    "\n",
    "\n",
    "\n",
    "### Your code ends here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 108,
   "id": "e9f6b2c0",
   "metadata": {
    "tags": [
     "hide-input"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "All numbers even: False\n",
      "Any word starts with 'j': True\n",
      "Any number > 10: False\n"
     ]
    }
   ],
   "source": [
    "# Solution\n",
    "numbers = [2, 4, 6, 8, 9]\n",
    "words = ['python', 'java', 'javascript', 'go']\n",
    "\n",
    "all_even = all(num % 2 == 0 for num in numbers)\n",
    "any_starts_j = any(word.startswith('j') for word in words)\n",
    "any_gt_10 = any(num > 10 for num in numbers)\n",
    "\n",
    "print(f\"All numbers even: {all_even}\")\n",
    "print(f\"Any word starts with 'j': {any_starts_j}\")\n",
    "print(f\"Any number > 10: {any_gt_10}\")"
   ]
  }
 ],
 "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
}
