diff --git a/sum23/lecture_materials/13_Copying/lec-13-worksheet-solutions.pdf b/sum23/lecture_materials/13_Copying/lec-13-worksheet-solutions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2cd79bf69b38e9871d7e64fa6a069d979f78e283 Binary files /dev/null and b/sum23/lecture_materials/13_Copying/lec-13-worksheet-solutions.pdf differ diff --git a/sum23/lecture_materials/13_Recursion/lec_13_recursion_notes.ipynb b/sum23/lecture_materials/13_Recursion/lec_13_recursion_notes.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..4367f61289192290d7bf12b748bc5f6ff004cc87 --- /dev/null +++ b/sum23/lecture_materials/13_Recursion/lec_13_recursion_notes.ipynb @@ -0,0 +1,865 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Recursion" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Review 1\n", + "\n", + "- Why does Python have the complexity of separate references and objects?\n", + "- Why not follow the original organization we saw for everything (i.e., boxes of data with labels)?\n", + " - Reason 1: Performance\n", + " - Reason 2: Centralized Updates" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before\n", + "After\n", + "End\n" + ] + } + ], + "source": [ + "# Reason 1: Performance\n", + "# Try this example in PythonTutor\n", + "\n", + "print(\"Before\")\n", + "x = \"this string is millions of characters\" + \"!\" * (10 ** 6)\n", + "print(\"After\")\n", + "y = x # this is fast! Why? \n", + "print(\"End\")\n", + " # Answer: Recall that assignment just creates a reference copy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Reason 2: Centralized Updates\n", + "\n", + "# Try this example in PythonTutor\n", + "alice = {\"name\":\"Alice\", \"score\":10, \"age\":30}\n", + "bob = {\"name\":\"Bob\", \"score\":8, \"age\":25}\n", + "winner = alice\n", + "\n", + "alice[\"age\"] += 1\n", + "print(\"Winner age:\", winner[\"age\"]) \n", + "# what line 9 will output?\n", + "# Answer: 31" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Review 2\n", + "\n", + "Assignment creates reference copy immaterial of whether it is into a variable or into another data structure. Recall that references can be stored either in a variable or inside object instances for other data structures." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Try this example in PythonTutor\n", + "\n", + "alice = {\"name\":\"Alice\", \"score\":10, \"age\":30}\n", + "bob = {\"name\":\"Bob\", \"score\":8, \"age\":25}\n", + "team = [alice, bob] \n", + "# TODO: discuss does this create new inner dictionaries?\n", + "# Answer: this line only create a new object for outer \n", + "# list, which directly stores references to existing\n", + "# dictionary object instances\n", + "\n", + "players = {\"A\": alice, \"B\": bob} \n", + "# TODO: discuss does this create new inner dictionaries?\n", + "# Answer: this line only create a new object for outer \n", + "# dictionary, which directly stores references to \n", + "# existing dictionary object instances" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Review 3" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], + "source": [ + "# Use 'in' to determine if the given thing is in my_list\n", + "my_list = [\"meet\", \"me\", \"after\", \"2:00pm\"]\n", + "print(\"me\" in my_list) # TODO: predict the output\n", + "print(\"Meena\" in my_list) # TODO: predict the output" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n", + "False\n", + "True\n" + ] + } + ], + "source": [ + "# Let's try a nested list\n", + "my_list = [11, \"meet\", [\"me\", \"them\", \"us\"], [84, 19, 22], \"school\", 2.54]\n", + "print(\"me\" in my_list) # TODO: predict the output\n", + "print(84 in my_list) # TODO: predict the output\n", + "print(11 in my_list) # TODO: predict the output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Warmup 1" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[11, 'meet', ['me', 'them', 'us'], [84, 19, 22], 'school', 2.54]\n", + "True\n", + "True\n", + "False\n" + ] + } + ], + "source": [ + "def search_list_depth2(target, some_list):\n", + " ''' returns True if thing in some_list, False otherwise'''\n", + " for thing in some_list:\n", + " #print(thing, type(thing))\n", + " if type(thing) != list:\n", + " if target == thing:\n", + " return True\n", + " else:\n", + " continue # do we need this? (answer: no)\n", + " else: # its a list\n", + " if target in thing:\n", + " return True\n", + " else: \n", + " continue # do we need this? (answer: no)\n", + " \n", + " return False # after all possible searching....not found\n", + "\n", + "print(my_list)\n", + "print(search_list_depth2(\"school\", my_list)) # in list\n", + "print(search_list_depth2(22, my_list)) # in nested list\n", + "print(search_list_depth2(\"house\", my_list)) # not anywhere" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Warmup 2" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n", + "True\n", + "False\n" + ] + } + ], + "source": [ + "list_3_deep = [22, [33, 44, [55, 66], 77], 88]\n", + "\n", + "# let's try it our previous function\n", + "print(search_list_depth2(22, list_3_deep)) # in list\n", + "print(search_list_depth2(99, list_3_deep)) # not in list\n", + "\n", + "# write other tests to be sure that it works\n", + "print(search_list_depth2(33, list_3_deep)) # in nested list\n", + "print(search_list_depth2(55, list_3_deep)) # in nested nested list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def search_list_depth3(target, some_list):\n", + " ''' returns True if thing in some_list, False otherwise\n", + " NOTE: Anna hasn't checked if this code works (it probably isn't perfect yet)\n", + " The point is just to illustate that as the list gets deeper, we have to add more\n", + " layers of nested if statements\n", + " '''\n", + " for thing in some_list:\n", + " #print(thing, type(thing))\n", + " if type(thing) != list:\n", + " if target == thing:\n", + " return True\n", + " else:\n", + " continue # do we need this? (answer: no)\n", + " else: # its a list\n", + " if target in thing:\n", + " return True\n", + " for thing2 in thing:\n", + " if type(thing) != list:\n", + " if target == thing:\n", + " return True\n", + " else:\n", + " continue # do we need this? (answer: no)\n", + " else: # its a list\n", + " if target in thing:\n", + " return True\n", + " \n", + " return False # after all possible searching....not found\n", + "\n", + "print(my_list)\n", + "print(search_list_depth2(\"school\", my_list)) # in list\n", + "print(search_list_depth2(22, my_list)) # in nested list\n", + "print(search_list_depth2(\"house\", my_list)) # not anywhere" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# what about ANY depth list? \n", + "# that is the goal of today's lecture" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Learning Objectives\n", + "\n", + "After today's Lecture you will be able to: \n", + "\n", + "Define recursion and be able to identify\n", + "- base case\n", + "- recursive case\n", + "- infinite recursion\n", + "\n", + "Explain why the following can be recursively defined\n", + "- lists\n", + "- dictionaries\n", + "\n", + "Trace a recursive function\n", + "- involving numeric computation\n", + "- involving nested data structures" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<div>\n", + "<img src=\"attachment:Droste_effect.png\" width=\"450\"/>\n", + "</div>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Announcements\n", + "Quiz 6 is due today\n", + "\n", + "Project deadlines are unusual for P8, P9, P10 (use the extra time next week to study for the exam)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Recursion is defined as the process of defining something in terms of itself.\n", + "\n", + "**Good example:**\n", + "\n", + "*Hofstadter's Law*: “It always takes longer than you expect, even when you take into account *Hofstadter's Law*.” (From Gödel, Escher, Bach)\n", + "\n", + "**Unhelpful self-reference example:**\n", + "\n", + "*mountain*: “a landmass that projects conspicuously above its surroundings and is higher than a *hill*”\n", + "\n", + "*hill*: “a usually rounded natural elevation of land lower than a *mountain*”\n", + "(From Merriam-Webster dictionary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Mathematical example:**\n", + "\n", + "A number x is a *positive even number* if:\n", + "- x is 2 or\n", + "- x equals another *positive even number* plus two\n", + "\n", + "<div>\n", + "<img src=\"attachment:Recursive%20structure.png\" width=\"450\"/>\n", + "</div>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Term**: branch\n", + "\n", + "**Definition**: wooden stick, with an end splitting into other *branches*, OR terminating with a leaf\n", + "\n", + "\n", + "<div>\n", + "<img src=\"attachment:Trees.png\" width=\"400\"/>\n", + "</div>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<div>\n", + "<img src=\"attachment:Base_case_recursive_case.png\" width=\"250\"/>\n", + "</div>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Term**: directory\n", + "\n", + "**Definition**: a collection of files and *directories*\n", + " \n", + "<div>\n", + "<img src=\"attachment:Directory_structure-2.png\" width=\"450\"/>\n", + "</div> " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Define recursion and be able to identify \n", + "- base case\n", + "- recursive case\n", + "- infinite recursion" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Factorial of a number" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Goal: work from examples to get to recursive code\n", + "\n", + "#### Step 1: Come up with examples\n", + "\n", + "```python\n", + "1! = 1\n", + "2! = 1*2 = 2\n", + "3! = 1*2*3 = 6\n", + "4! = 1*2*3*4 = 24\n", + "5! = 1*2*3*4*5 = 120\n", + "```\n", + "\n", + "#### Step 2: Identify self-reference\n", + "```python\n", + "1! = # don't need a pattern at the start\n", + "2! = \n", + "3! = \n", + "4! = \n", + "5! = \n", + "```\n", + "\n", + "#### Step 3: Recursive definition\n", + "```python\n", + "1! is ???\n", + "N! is ??? for N > 1\n", + "```\n", + "\n", + "#### Step 4: Convert to Python code\n", + "- **Rule 1**: Base case should always be defined and be terminal\n", + "- **Rule 2**: Recursive case should make progress towards base case" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def fact(n):\n", + " if n == 1:\n", + " return 1\n", + " p = fact(n-1) \n", + " return n * p" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How does Python keep all the variables separate?\n", + "- Frames\n", + "\n", + "<div>\n", + "<img src=\"attachment:Frames.png\" width=\"450\"/>\n", + "</div> " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Try this in PythonTutor\n", + "fact(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<div>\n", + "<img src=\"attachment:Factorial.png\" width=\"500\"/>\n", + "</div> " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### RecursionError\n", + "\n", + "#### If there is no base case what happens in the above example? \n", + "- recursion never ends......infinite recursion\n", + "\n", + "#### infinite recursion can also happen if the recursive case does not move towards the base" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "ename": "RecursionError", + "evalue": "maximum recursion depth exceeded", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[2], line 6\u001b[0m\n\u001b[1;32m 4\u001b[0m p \u001b[38;5;241m=\u001b[39m bad_fact(n\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m) \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n\u001b[0;32m----> 6\u001b[0m \u001b[43mbad_fact\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[2], line 4\u001b[0m, in \u001b[0;36mbad_fact\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbad_fact\u001b[39m(n):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;66;03m#if n == 1:\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;66;03m# return 1\u001b[39;00m\n\u001b[0;32m----> 4\u001b[0m p \u001b[38;5;241m=\u001b[39m \u001b[43mbad_fact\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n", + "Cell \u001b[0;32mIn[2], line 4\u001b[0m, in \u001b[0;36mbad_fact\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbad_fact\u001b[39m(n):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;66;03m#if n == 1:\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;66;03m# return 1\u001b[39;00m\n\u001b[0;32m----> 4\u001b[0m p \u001b[38;5;241m=\u001b[39m \u001b[43mbad_fact\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n", + " \u001b[0;31m[... skipping similar frames: bad_fact at line 4 (2970 times)]\u001b[0m\n", + "Cell \u001b[0;32mIn[2], line 4\u001b[0m, in \u001b[0;36mbad_fact\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbad_fact\u001b[39m(n):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;66;03m#if n == 1:\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;66;03m# return 1\u001b[39;00m\n\u001b[0;32m----> 4\u001b[0m p \u001b[38;5;241m=\u001b[39m \u001b[43mbad_fact\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" + ] + } + ], + "source": [ + "def bad_fact(n):\n", + " #if n == 1:\n", + " # return 1\n", + " p = bad_fact(n-1) \n", + " return n * p\n", + "bad_fact(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "ename": "RecursionError", + "evalue": "maximum recursion depth exceeded in comparison", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[3], line 6\u001b[0m\n\u001b[1;32m 4\u001b[0m p \u001b[38;5;241m=\u001b[39m bad_fact2(n) \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n\u001b[0;32m----> 6\u001b[0m \u001b[43mbad_fact2\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[3], line 4\u001b[0m, in \u001b[0;36mbad_fact2\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m----> 4\u001b[0m p \u001b[38;5;241m=\u001b[39m \u001b[43mbad_fact2\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m)\u001b[49m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n", + "Cell \u001b[0;32mIn[3], line 4\u001b[0m, in \u001b[0;36mbad_fact2\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m----> 4\u001b[0m p \u001b[38;5;241m=\u001b[39m \u001b[43mbad_fact2\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m)\u001b[49m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n", + " \u001b[0;31m[... skipping similar frames: bad_fact2 at line 4 (2969 times)]\u001b[0m\n", + "Cell \u001b[0;32mIn[3], line 4\u001b[0m, in \u001b[0;36mbad_fact2\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m----> 4\u001b[0m p \u001b[38;5;241m=\u001b[39m \u001b[43mbad_fact2\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m)\u001b[49m \n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m p\n", + "Cell \u001b[0;32mIn[3], line 2\u001b[0m, in \u001b[0;36mbad_fact2\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mbad_fact2\u001b[39m(n):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m==\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m:\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m 4\u001b[0m p \u001b[38;5;241m=\u001b[39m bad_fact2(n) \n", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded in comparison" + ] + } + ], + "source": [ + "def bad_fact2(n):\n", + " if n == 1:\n", + " return 1\n", + " p = bad_fact2(n) \n", + " return n * p\n", + "bad_fact2(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Self-check: Tracing example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Tracing a recursive function in the Python Tutor\n", + "# do this on your own\n", + "# Example 1\n", + "\n", + "def foo(n):\n", + " # I always start recursive functions by printing the parameters\n", + " print(\"Starting foo with n= \" , n)\n", + " if n < 0:\n", + " print(\"base case, returning 100\")\n", + " return 100\n", + " else:\n", + " temp = foo(n-2)\n", + " print(\"Ending foo returning \" , (n + temp))\n", + " return n + temp\n", + "\n", + "foo(13)\n", + "\n", + "# What happens if we replace < with == ? \n", + "# Recursion error if the original n is odd" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "in collatz with n = 13\n", + "in the odd case\n", + "in collatz with n = 40\n", + "In the even case\n", + "in collatz with n = 20\n", + "In the even case\n", + "in collatz with n = 10\n", + "In the even case\n", + "in collatz with n = 5\n", + "in the odd case\n", + "in collatz with n = 16\n", + "In the even case\n", + "in collatz with n = 8\n", + "In the even case\n", + "in collatz with n = 4\n", + "In the even case\n", + "in collatz with n = 2\n", + "In the even case\n", + "in collatz with n = 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Example 2\n", + "# The Collatz Conjecture problem \n", + "# https://en.wikipedia.org/wiki/Collatz_conjecture\n", + "# run this in Python Tutor on your ownn\n", + "\n", + "def collatz(n):\n", + " # I always start recursive functions by printing the parameters\n", + " print(\"in collatz with n = \" , n)\n", + " if n == 1:\n", + " return 1 # base case\n", + " elif n % 2 == 0:\n", + " print(\"In the even case\")\n", + " return collatz(n//2)\n", + " else:\n", + " print(\"in the odd case\")\n", + " return collatz (3*n+1)\n", + "\n", + "collatz(13) # try other numbers\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Trace a recursive function involving nested data structures" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# Write a recursive function to search *ANY* list of lists/tuples \n", + "# for a given word\n", + "def search_list_recursive(target, some_list):\n", + " ''' returns True if target in some_list, False otherwise'''\n", + " \n", + " # base case 1: success!\n", + " if target in some_list:\n", + " return True\n", + " \n", + " # Strategy: iterate over all elements in some_list\n", + " success = False\n", + " for thing in some_list:\n", + " # Because of base case 1, we know that thing is not target\n", + " # TODO also want to include tuples\n", + " if type(thing) == type([]):\n", + " success = search_list_recursive(target, thing)\n", + " if success:\n", + " break\n", + "\n", + " return success" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "True\n", + "True\n", + "True\n", + "False\n" + ] + } + ], + "source": [ + "fav_stuff = [\"apples\", \"peaches\", \"oranges\", \n", + " [\"A\", \"B\", \"C\", \"D\"],\n", + " [[\"sedan\", \"SUV car\", \"minivan\"], \n", + " [\"bicycle\", \"road bike\", \"scooter\"]]\n", + " ]\n", + "\n", + "print(search_list_recursive(\"apples\", fav_stuff)) # outer list\n", + "print(search_list_recursive(\"D\", fav_stuff)) # list @ depth 1\n", + "print(search_list_recursive(\"road bike\", fav_stuff))# list @ depth 2\n", + "print(search_list_recursive(\"bicycle\", fav_stuff)) # list @ depth 2\n", + "print(search_list_recursive(\"pizza\", fav_stuff)) # doesn't exist" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\n", + "A\n", + "\t[\n", + "\t1\n", + "\t2\n", + "\t3\n", + "\t]\n", + "B\n", + "\t[\n", + "\t4\n", + "\t\t[\n", + "\t\ti\n", + "\t\tii\n", + "\t\t]\n", + "\t]\n", + "]\n" + ] + } + ], + "source": [ + "def pretty_print(items, indent = 0):\n", + " \"\"\"\n", + " prints the nested data structure with proper indentation\n", + " \"\"\"\n", + " # base case: not a list\n", + " # edit: moving base case into a for loop\n", + "# if type(items) != type([]):\n", + "# print(indent * \"\\t\" + items)\n", + "# return\n", + " print(indent * \"\\t\" + \"[\")\n", + " for i in items:\n", + " \n", + " # base case:\n", + " if type(i) != type([]):\n", + " print(indent * \"\\t\" + i)\n", + " else:\n", + " # recursive case\n", + " pretty_print(i, indent + 1)\n", + " print(indent * \"\\t\" + \"]\") \n", + "\n", + "#data = [\"A\", \"B\", \"C\"]\n", + "data = [\"A\", [\"1\", \"2\", \"3\",], \"B\", [\"4\", [\"i\", \"ii\"]]]\n", + "pretty_print(data, 0)\n", + " \n", + "# [ \"A\", \n", + "# [ \"1\", \n", + "# \"2\", \n", + "# \"3\"], \n", + "# \"B\", \n", + "# ], \n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Explain why the following can be recursively defined\n", + "\n", + "- lists\n", + "- dictionaries\n", + "- JSON objects" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### dictionaries can have a recursive structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "person_info = { \"name\": \"Meena\", \n", + " \"age\": 250, \n", + " \"family\" : {\"spouse\": \"Rogers\", \n", + " \"child1\": {\"name\": \"Viyan\", \n", + " \"age\": 2}, \n", + " }\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# let's try to search through a deep dictionary. \n", + "def search_dict_recursive(target_key, some_dict):\n", + " ''' returns the Value associated with target_key if tarket_key \n", + " in any level of some_dict, None otherwise'''\n", + " if target_key in some_dict: # base case\n", + " return some_dict[target_key]\n", + " else:\n", + " for key in some_dict:\n", + " if type(some_dict[key]) == dict: # recursive case\n", + " return search_dict_recursive(target_key, \\\n", + " some_dict[key]) \n", + " return None\n", + "\n", + "print(search_dict_recursive(\"child1\", person_info))\n", + "print(search_dict_recursive(\"father\", person_info))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/sum23/lecture_materials/14_Comprehensions/lec_14_comprehensions_notes.ipynb b/sum23/lecture_materials/14_Comprehensions/lec_14_comprehensions_notes.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..1c61d62f8fbcf18e6377ec24ffc6d22dfc5a64ef --- /dev/null +++ b/sum23/lecture_materials/14_Comprehensions/lec_14_comprehensions_notes.ipynb @@ -0,0 +1,1342 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Comprehensions" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# import statements\n", + "import math\n", + "import csv" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using `lambda`\n", + "- `lambda` functions are a way to abstract a function reference\n", + "- lambdas are simple functions with:\n", + " - multiple possible parameters\n", + " - single expression line as the function body\n", + "- lambdas are useful abstractions for:\n", + " - mathematical functions\n", + " - lookup operations\n", + "- lambdas are often associated with a collection of values within a list\n", + "- Syntax: \n", + "```python \n", + "lambda parameters: expression\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's sort the menu in different ways\n", + "- whenever you need to custom sort a dictionary, you must convert dict to list of tuples\n", + "- recall that you can use items method (applicable only to a dictionary)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'broccoli': 4.99,\n", + " 'orange': 1.19,\n", + " 'pie': 3.95,\n", + " 'donut': 1.25,\n", + " 'muffin': 2.25,\n", + " 'cookie': 0.79,\n", + " 'milk': 1.65,\n", + " 'bread': 5.99}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "menu = { \n", + " 'broccoli': 4.99,\n", + " 'orange': 1.19,\n", + " 'pie': 3.95, \n", + " 'donut': 1.25, \n", + " 'muffin': 2.25,\n", + " 'cookie': 0.79, \n", + " 'milk':1.65, \n", + " 'bread': 5.99} \n", + "menu" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('broccoli', 4.99), ('orange', 1.19), ('pie', 3.95), ('donut', 1.25), ('muffin', 2.25), ('cookie', 0.79), ('milk', 1.65), ('bread', 5.99)])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "menu.items()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort menu using item names (keys)\n", + "- let's first solve this using extract function\n", + "- recall that extract function deals with one of the inner items in the outer data structure\n", + " - outer data structure is list\n", + " - inner data structure is tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def extract(menu_tuple):\n", + " return menu_tuple[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('cookie', 0.79),\n", + " ('orange', 1.19),\n", + " ('donut', 1.25),\n", + " ('milk', 1.65),\n", + " ('muffin', 2.25),\n", + " ('pie', 3.95),\n", + " ('broccoli', 4.99),\n", + " ('bread', 5.99)]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(menu.items(), key = extract)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'cookie': 0.79,\n", + " 'orange': 1.19,\n", + " 'donut': 1.25,\n", + " 'milk': 1.65,\n", + " 'muffin': 2.25,\n", + " 'pie': 3.95,\n", + " 'broccoli': 4.99,\n", + " 'bread': 5.99}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(sorted(menu.items(), key = extract))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now let's solve the same problem using lambdas\n", + "- if you are having trouble thinking through the lambda solution directly:\n", + " - write an extract function\n", + " - then abstract it to a lambda" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'cookie': 0.79,\n", + " 'orange': 1.19,\n", + " 'donut': 1.25,\n", + " 'milk': 1.65,\n", + " 'muffin': 2.25,\n", + " 'pie': 3.95,\n", + " 'broccoli': 4.99,\n", + " 'bread': 5.99}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(sorted(menu.items(), key = lambda menu_tuple : menu_tuple[1]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort menu using prices (values)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# same as above\n", + "dict(sorted(menu.items(), key = lambda ??? : ???))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort menu using length of item names (keys)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'pie': 3.95,\n", + " 'milk': 1.65,\n", + " 'donut': 1.25,\n", + " 'bread': 5.99,\n", + " 'orange': 1.19,\n", + " 'muffin': 2.25,\n", + " 'cookie': 0.79,\n", + " 'broccoli': 4.99}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(sorted(menu.items(), key = lambda menu_tuple :len( menu_tuple[0]) ))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort menu using decreasing order of prices - v1" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'bread': 5.99,\n", + " 'broccoli': 4.99,\n", + " 'pie': 3.95,\n", + " 'muffin': 2.25,\n", + " 'milk': 1.65,\n", + " 'donut': 1.25,\n", + " 'orange': 1.19,\n", + " 'cookie': 0.79}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(sorted(menu.items(), key = lambda menu_tuple: menu_tuple[1], reverse = True))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort menu using decreasing order of prices - v2" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'bread': 5.99,\n", + " 'broccoli': 4.99,\n", + " 'pie': 3.95,\n", + " 'muffin': 2.25,\n", + " 'milk': 1.65,\n", + " 'donut': 1.25,\n", + " 'orange': 1.19,\n", + " 'cookie': 0.79}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(sorted(menu.items(), key = lambda menu_tuple: -menu_tuple[1]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterable\n", + "\n", + "- What is an iterable? Anything that you can write a for loop to iterate over is called as an iterable.\n", + "- Examples of iteratables:\n", + " - `list`, `str`, `tuple`, `range()` (any sequence)\n", + " - `dict`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## List comprehensions\n", + "\n", + "- concise way of generating a new list based on existing list item manipulation \n", + "- short syntax - easier to read, very difficult to debug\n", + "\n", + "<pre>\n", + "new_list = [expression for val in iterable if conditional_expression]\n", + "</pre>\n", + "- iteratble: reference to any iterable object instance\n", + "- conditional_expression: filters the values in the original list based on a specific requirement\n", + "- expression: can simply be val or some other transformation of val\n", + "- enclosing [ ] represents new list\n", + "\n", + "Best approach:\n", + "- write for clause first\n", + "- if condition expression next\n", + "- expression in front of for clause last" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Which animals are in all caps?" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original: ['lion', 'badger', 'RHINO', 'GIRAFFE']\n", + "New list: ['RHINO', 'GIRAFFE']\n" + ] + } + ], + "source": [ + "# Recap: retain animals in all caps\n", + "animals = [\"lion\", \"badger\", \"RHINO\", \"GIRAFFE\"]\n", + "caps_animals = []\n", + "print(\"Original:\", animals)\n", + "\n", + "for val in animals:\n", + " if val.upper() == val:\n", + " caps_animals.append(val) \n", + " \n", + "print(\"New list:\", caps_animals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now let's solve the same problem using list comprehension\n", + "<pre>\n", + "new_list = [expression for val in iterable if conditional_expression]\n", + "</pre>\n", + "For the below example:\n", + "- iterable: animals variable (storing reference to a list object instance)\n", + "- conditional_expression: val.upper() == val\n", + "- expression: val itself" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original: ['lion', 'badger', 'RHINO', 'GIRAFFE']\n", + "New list: ['RHINO', 'GIRAFFE']\n" + ] + } + ], + "source": [ + "# List comprehension version\n", + "print(\"Original:\", animals)\n", + "\n", + "caps_animals = [val for val in animals if val.upper() == val ]\n", + "print(\"New list:\", caps_animals)\n", + "\n", + "# final version to uncomment if you want:\n", + "# caps_animals = [val for val in animals if val.upper() == val]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Why is to tougher to debug?\n", + "- you cannot use a print function call in a comprehension\n", + "- you need to decompose each part and test it separately\n", + "- recommended to write the comprehension with a simpler example" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Other than a badger, what animals can you see at Henry Vilas Zoo?" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original: ['lion', 'badger', 'RHINO', 'GIRAFFE']\n", + "New list: ['lion', 'RHINO', 'GIRAFFE']\n" + ] + } + ], + "source": [ + "print(\"Original:\", animals)\n", + "\n", + "# non_badger_zoo_animals = ???\n", + "\n", + "# step 1\n", + "# non_badger_zoo_animals = [??? for val in animals ???]\n", + " \n", + "# # step 2\n", + "# non_badger_zoo_animals = [??? for val in animals if val != \"badger\"] \n", + "\n", + "# # step 3\n", + "non_badger_zoo_animals = [val for val in animals if val != \"badger\"]\n", + "\n", + "print(\"New list:\", non_badger_zoo_animals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Can we convert all of the animals to all caps?\n", + "- if clause is optional" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original: ['lion', 'badger', 'RHINO', 'GIRAFFE']\n", + "New list: ['LION', 'BADGER', 'RHINO', 'GIRAFFE']\n" + ] + } + ], + "source": [ + "print(\"Original:\", animals)\n", + "\n", + "all_caps_animals = [ val.upper() for val in animals ]\n", + "print(\"New list:\", all_caps_animals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Can we generate a list to store length of each animal name?" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original: ['lion', 'badger', 'RHINO', 'GIRAFFE']\n", + "New list: [4, 6, 5, 7]\n" + ] + } + ], + "source": [ + "print(\"Original:\", animals)\n", + "\n", + "animals_name_length = [ len(val) for val in animals ]\n", + "print(\"New list:\", animals_name_length)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using if ... else ... in a list comprehension\n", + "- syntax changes slightly for if ... else ...\n", + "\n", + "<pre>\n", + "new_list = [expression if conditional_expression else alternate_expression for val in iterable ]\n", + "</pre>\n", + "\n", + "- when an item satifies the if clause, you don't execute the else clause\n", + " - expression is the item in new list when if condition is satified\n", + "- when an item does not satisfy the if clause, you execute the else clause\n", + " - alternate_expression is the item in new list when if condition is not satisfied\n", + " \n", + "- if ... else ... clauses need to come before for (not the same as just using if clause)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What if we only care about the badger? Replace non-badger animals with \"some animal\"." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original: ['lion', 'badger', 'RHINO', 'GIRAFFE']\n", + "New list: ['some animal', 'badger', 'some animal', 'some animal']\n" + ] + } + ], + "source": [ + "animals = [\"lion\", \"badger\", \"RHINO\", \"GIRAFFE\"]\n", + "print(\"Original:\", animals)\n", + "\n", + "#non_badger_zoo_animals = ???\n", + "\n", + "# # step 1:\n", + "# non_badger_zoo_animals = [ ??? for val in animals ???]\n", + "\n", + "# # step 2:\n", + "# non_badger_zoo_animals = [ ??? if val == \"badger\" else ??? for val in animals]\n", + "\n", + "# # step 3: fill in \"val\"\n", + " #non_badger_zoo_animals = [val if val == \"badger\" else ?? for val in animals]\n", + "\n", + "# # step 4: fill in else case\n", + "non_badger_zoo_animals = [val if val == \"badger\" else \"some animal\" for val in animals]\n", + "\n", + "print(\"New list:\", non_badger_zoo_animals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dict comprehensions\n", + "- Version 1:\n", + "<pre>\n", + "{expression for val in iterable if condition}\n", + "</pre>\n", + "- expression has the form <pre>key: val</pre>\n", + "<br/>\n", + "- Version 2 --- the dict function call by passing list comprehension as argument:\n", + "<pre>dict([expression for val in iterable if condition])</pre>\n", + "- expression has the form <pre>(key, val)</pre>" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create a dict to map number to its square (for numbers 1 to 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}\n" + ] + } + ], + "source": [ + "squares_dict = dict()\n", + "for val in range(1, 6):\n", + " squares_dict[val] = val * val\n", + "print(squares_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dict comprehension --- version 1" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}\n" + ] + } + ], + "source": [ + "square_dict = { i : i * i for i in range(1, 6) }\n", + "print(square_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dict comprehension --- version 2" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}\n" + ] + } + ], + "source": [ + "square_dict = dict( [ (i, i * i ) for i in range(1,6) ] )\n", + "print(square_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tuple unpacking\n", + "- you can directly specific variables to unpack the items inside a tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('Bob', '32')\n", + "('Cindy', '45')\n", + "('Alice', '39')\n", + "('Unknown', 'None')\n", + "--------------------\n", + "Bob 32\n", + "Cindy 45\n", + "Alice 39\n", + "Unknown None\n" + ] + } + ], + "source": [ + "scores_dict = {\"Bob\": \"32\", \"Cindy\" : \"45\", \"Alice\": \"39\", \"Unknown\": \"None\"}\n", + "\n", + "for tuple_item in scores_dict.items():\n", + " print(tuple_item)\n", + " \n", + "print(\"--------------------\")\n", + "\n", + "for key, val in scores_dict.items():\n", + " print(key, val)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### From square_dict, let's generate cube_dict" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "square_dict" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{1: 1, 2: 8, 3: 27, 4: 64, 5: 125}\n" + ] + } + ], + "source": [ + "cube_dict = {key: int(math.sqrt(val)) ** 3 for key, val in square_dict.items()}\n", + "print(cube_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Convert Madison *F temperature to *C\n", + "- <pre>C = 5 / 9 * (F - 32)</pre>" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "madison_fahrenheit = {'Nov': 28,'Dec': 20, 'Jan': 10,'Feb': 14}\n", + "print(\"Original:\", madison_fahrenheit)\n", + "\n", + "madison_celsius = {key: ??? \\\n", + " for key, val in madison_fahrenheit.items()}\n", + "print(\"New dict:\", madison_celsius)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Convert type of values in a dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scores_dict = {\"Bob\": \"32\", \"Cindy\" : \"45\", \"Alice\": \"39\", \"Unknown\": \"None\"}\n", + "print(\"Original:\", scores_dict)\n", + "\n", + "updated_scores_dict = {???}\n", + " \n", + "# # step 1: add for statement\n", + "# updated_scores_dict = { ??? for key, val in scores_dict.items() ???}\n", + "\n", + "# # step 2: add if statement - but use if/else to handle None values\n", + "# updated_scores_dict = { ??? if val.isdigit() else ??? for key, val in scores_dict.items()}\n", + "\n", + "# # step 3: fill in \"if\" and \"else\" values\n", + "# updated_scores_dict = {key: int(val) if val.isdigit() else None \\\n", + "# for key, val in scores_dict.items()}\n", + "\n", + "print(\"New dict:\", updated_scores_dict)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create a dictionary to map each player to their max score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scores_dict = {\"Bob\": [18, 72, 61, 5, 83], \n", + " \"Cindy\" : [27, 11, 55, 73, 87], \n", + " \"Alice\": [16, 33, 42, 89, 90], \n", + " \"Meena\": [39, 93, 9, 3, 55]}\n", + "\n", + "{player: max(scores) for player, scores in scores_dict.items()}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Practice problems - sorted + lambda" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Use sorted and lambda function to sort this list of dictionaries based on the score, from low to high" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scores = [ {\"name\": \"Bob\", \"score\": 32} ,\n", + " {\"name\": \"Cindy\", \"score\" : 45}, \n", + " {\"name\": \"Alice\", \"score\": 39}\n", + " ]\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now, modify the lambda function part alone to sort the list of dictionaries based on the score, from high to low" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now, go back to the previous lambda function definition and use sorted parameters to sort the list of dictionaries based on the score, from high to low" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Student Information Survey dataset analysis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def median(items):\n", + " items.sort()\n", + " n = len(items)\n", + " if n % 2 != 0:\n", + " middle = items[n // 2]\n", + " else:\n", + " first_middle = items[n // 2]\n", + " second_middle = items[(n // 2) - 1]\n", + " middle = (first_middle + second_middle) / 2\n", + " return middle" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# inspired by https://automatetheboringstuff.com/2e/chapter16/\n", + "def process_csv(filename):\n", + " exampleFile = open(filename, encoding=\"utf-8\") \n", + " exampleReader = csv.reader(exampleFile) \n", + " exampleData = list(exampleReader) \n", + " exampleFile.close() \n", + " return exampleData\n", + "\n", + "survey_data = process_csv('cs220_survey_data.csv')\n", + "cs220_header = survey_data[0]\n", + "cs220_data = survey_data[1:]\n", + "cs220_header" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def cell(row_idx, col_name):\n", + " \"\"\"\n", + " Returns the data value (cell) corresponding to the row index and \n", + " the column name of a CSV file.\n", + " \"\"\"\n", + " col_idx = cs220_header.index(col_name) \n", + " val = cs220_data[row_idx][col_idx] \n", + " \n", + " # handle missing values\n", + " if val == '':\n", + " return None\n", + " \n", + " # handle type conversions\n", + " if col_name == \"Age\":\n", + " val = int(val)\n", + " if 0 < val <= 118:\n", + " return val\n", + " else:\n", + " # Data cleaning\n", + " return None\n", + " elif col_name in ['Zip Code',]:\n", + " return int(val)\n", + " elif col_name in ['Latitude', 'Longitude']:\n", + " return float(val)\n", + " \n", + " return val" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def transform(header, data):\n", + " \"\"\"\n", + " Transform data into a list of dictionaries, while taking care of type conversions\n", + " \"\"\"\n", + " #should be defined outside the for loop, because it stores the entire data\n", + " dict_list = [] \n", + " for row_idx in range(len(data)):\n", + " row = data[row_idx]\n", + " #should be defined inside the for loop, because it represents one row as a \n", + " #dictionary\n", + " new_row = {} \n", + " for i in range(len(header)):\n", + " val = cell(row_idx, header[i])\n", + " new_row[header[i]] = val\n", + " dict_list.append(new_row)\n", + " return dict_list\n", + " \n", + "transformed_data = transform(cs220_header, cs220_data)\n", + "transformed_data[:2] # top 2 rows" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def bucketize(data, bucket_column):\n", + " \"\"\"\n", + " data: expects list of dictionaries\n", + " bucket_column: column for bucketization\n", + " generates and returns bucketized data based on bucket_column\n", + " \"\"\"\n", + " # Key: unique bucketize column value; Value: list of dictionaries \n", + " # (rows having that unique column value)\n", + " buckets = dict()\n", + " for row_dict in data:\n", + " col_value = row_dict[bucket_column]\n", + " if col_value not in buckets:\n", + " buckets[col_value] = []\n", + " buckets[col_value].append(row_dict)\n", + " \n", + " return buckets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What is the average age of \"LEC001\" students?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "lecture_buckets = bucketize(transformed_data, \"Lecture\")\n", + "lec001_bucket = lecture_buckets[\"LEC001\"]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What is the average age of \"LEC001\" students who like \"pineapple\" pizza topping?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What are the sleep habits of the youngest students?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "min_age = None\n", + "\n", + "# pass 1: find minimum age\n", + "\n", + "\n", + "# pass 2: find sleep habit of students with minimum age\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How many students are there is each lecture?\n", + "- Create a `dict` mapping each lecture to the count of students." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# v1\n", + "{}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# v2\n", + "{}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Find whether 15 oldest students in the class are runners?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "students_with_age = []" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compute median age per lecture in one step using `dict` and `list` comprehension." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "age_by_lecture = {} # Key: lecture; Value: list of ages\n", + "\n", + "for lecture in lecture_buckets:\n", + " lecture_students = lecture_buckets[lecture]\n", + " ages = []\n", + " for student in lecture_students:\n", + " age = student[\"Age\"]\n", + " if age == None:\n", + " continue\n", + " ages.append(age)\n", + " age_by_lecture[lecture] = ages\n", + "\n", + "median_age_by_lecture = {} # Key: lecture; Value: median age of that lecture\n", + "for lecture in age_by_lecture:\n", + " median_age = median(age_by_lecture[lecture])\n", + " median_age_by_lecture[lecture] = median_age\n", + " \n", + "print(median_age_by_lecture)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compute max age per lecture in one step using `dict` and `list` comprehension." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Practice problems - comprehensions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Generate a new list where each number is a square of the original nummber in numbers list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [44, 33, 56, 21, 19]\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Generate a new list of floats from vac_rates, that is rounded to 3 decimal points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vac_rates = [23.329868, 51.28772, 76.12232, 17.2, 10.5]\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Generate a new list of ints from words, that contains length of each word" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "words = ['My', 'very', 'educated', 'mother', 'just', 'served', 'us', 'noodles']\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create 2 dictionaries to map each player to their min and avg score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "scores_dict = {\"Bob\": [18, 72, 61, 5, 83], \n", + " \"Cindy\" : [27, 11, 55, 73, 87], \n", + " \"Alice\": [16, 33, 42, 89, 90], \n", + " \"Meena\": [39, 93, 9, 3, 55]}\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Student Information Survey dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create dict mapping unique age to count of students with that age.\n", + "- Order the dictionary based on increasing order of ages\n", + "- Make sure to drop student dictionaries which don't have Age column information (we already did this in a previous example)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Find whether 15 youngest students in the class are pet owners?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/sum23/lecture_materials/14_Func_Refs/lec_14_function_references_notes.ipynb b/sum23/lecture_materials/14_Func_Refs/lec_14_function_references_notes.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..63f982d5d0b5fb2216f4b6a205dce2226e3522c9 --- /dev/null +++ b/sum23/lecture_materials/14_Func_Refs/lec_14_function_references_notes.ipynb @@ -0,0 +1,1420 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Function references" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Recursion review" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Nested data structures are defined recursively.\n", + "\n", + "# A Python list can contain lists\n", + "# A Python dictionary can contain dictionaries\n", + "# A JSON dictionary can contain a JSON dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Trace Recursion by hand\n", + "# Run this on your own in Python Tutor\n", + "\n", + "def mystery(a, b): \n", + " # precondition: assume a > 0 and b > 0\n", + " if b == 1: \n", + " return a\n", + " return a * mystery(a, b - 1)\n", + "\n", + "# make a function call here\n", + "mystery(3, 2)\n", + "\n", + "# TODO: what does the mystery function compute?\n", + "\n", + "# Question: What would be the result of the below function call?\n", + "# mystery(-3, -1) \n", + "# Answer: " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Learning Objectives:\n", + "\n", + "- Define a function reference and trace code that uses function references.\n", + "- Explain the default use of `sorted()` on lists of tuples, and dictionaries.\n", + "- Sort a list of tuples, a list of dictionaries, or a dictionary using a function as a key.\n", + "- Use a lambda expression when sorting." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Functions are objects\n", + "\n", + "- Every data in Python is an object instance, including a function definition\n", + "- Implications:\n", + " - variables can reference functions\n", + " - lists/dicts can reference functions\n", + " - we can pass function references to other functions\n", + " - we can pass lists of function references to other functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 1: function object references\n", + "#### Use PyTutor to step through this example" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n" + ] + } + ], + "source": [ + "l1 = [1, 2, 3] # Explanation: l1 should reference a new list object\n", + "l2 = l1 # Explanation: l2 should reference whatever l1 references\n", + "\n", + "def f(l): # Explanation: f should reference a new function object\n", + " return l[-1]\n", + "\n", + "g = f # Explanation: g should reference whatever f references\n", + "\n", + "num = f(l2) # Explanation: l should reference whatever l2 references\n", + " # Explanation: num should reference whatever f returns\n", + "\n", + "print(num)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Function references\n", + "\n", + "- Since function definitions are objects in Python, function reference is a variable that refers to a function object.\n", + "- In essence, it gives a function another name" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "3\n" + ] + } + ], + "source": [ + "# Both these calls would have run the same code, returning the same result\n", + "num = f(l1)\n", + "print(num)\n", + "num = g(l2) \n", + "print(num)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 2: function references can be passed as arguments to another function, wow!\n", + "#### Use PyTutor to step through this example" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello there!\n", + "Hello there!\n", + "Wash your hands and stay well, bye!\n", + "Wash your hands and stay well, bye!\n", + "Wash your hands and stay well, bye!\n" + ] + } + ], + "source": [ + "def say_hi():\n", + " print(\"Hello there!\")\n", + "\n", + "def say_bye():\n", + " print(\"Wash your hands and stay well, bye!\")\n", + " \n", + "f = say_hi\n", + "f()\n", + "f()\n", + "f = say_bye\n", + "f()\n", + "f()\n", + "f()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello there!\n", + "Hello there!\n", + "Wash your hands and stay well, bye!\n", + "Wash your hands and stay well, bye!\n", + "Wash your hands and stay well, bye!\n" + ] + } + ], + "source": [ + "for i in range(2):\n", + " say_hi()\n", + "\n", + "for i in range(3):\n", + " say_bye()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello there!\n", + "Hello there!\n", + "Wash your hands and stay well, bye!\n", + "Wash your hands and stay well, bye!\n", + "Wash your hands and stay well, bye!\n" + ] + } + ], + "source": [ + "def call_n_times(f, n):\n", + " for i in range(n):\n", + " f()\n", + "\n", + "call_n_times(say_hi, 2)\n", + "call_n_times(say_bye, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Wash your hands and stay well, bye!\n" + ] + }, + { + "ename": "TypeError", + "evalue": "'NoneType' object is not callable", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mcall_n_times\u001b[49m\u001b[43m(\u001b[49m\u001b[43msay_bye\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# uncomment to see TypeError\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[38;5;66;03m# Question: Why does this give TypeError?\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;66;03m# Answer: when you specify say_bye(), you are invoking the function, which returns None\u001b[39;00m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;66;03m# (default return value when return statement is not defined)\u001b[39;00m\n", + "Cell \u001b[0;32mIn[6], line 3\u001b[0m, in \u001b[0;36mcall_n_times\u001b[0;34m(f, n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcall_n_times\u001b[39m(f, n):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(n):\n\u001b[0;32m----> 3\u001b[0m \u001b[43mf\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: 'NoneType' object is not callable" + ] + } + ], + "source": [ + "call_n_times(say_bye(), 3) # uncomment to see TypeError\n", + "\n", + "# Question: Why does this give TypeError?\n", + "# Answer: when you specify say_bye(), you are invoking the function, which returns None\n", + "# (default return value when return statement is not defined)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 3: Apply various transformations to all items on a list" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "L = [\"1\", \"23\", \"456\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write apply_to_each function" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# a. Input: list object reference, function object\n", + "# b. Output: new list reference to transformed object\n", + "# c. Pseudocode:\n", + "# 1. Initiliaze new empty list for output - we don't want to modify \n", + "# the input list! \n", + "# 2. Process each item in input list\n", + "# 3. Apply the function passed as arugment to 2nd parameter\n", + "# 4. And the transformed item into output list\n", + "# 5. return output list\n", + "\n", + "def apply_to_each(original_L, f):\n", + " \"\"\"\n", + " returns a new list with transformed items, by applying f function\n", + " to each item in the original list\n", + " \"\"\"\n", + "\n", + " # step 1: create a new list\n", + " new_vals = []\n", + "\n", + " # step 2: iterate through items in original_L\n", + " for val in original_L:\n", + " # step 3: apply f to each item\n", + " new_vals.append( f(val) )\n", + " \n", + " # step 4: return new list\n", + " return new_vals \n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Apply `int` function to list L using apply_to_each function" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "34" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# what does int do?\n", + "int(\"34\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 23, 456]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "apply_to_each(L, int)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write strip_dollar function" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# a. Input: string value\n", + "# b. Output: transformed string value\n", + "# c. Pseudocode: \n", + "# 1. Check whether input string begins with $ - \n", + "# what string method do you need here?\n", + "# 2. If so remove it\n", + "\n", + "def strip_dollar(s):\n", + " \"\"\"\n", + " Removes the beginning $ sign from string s\n", + " \"\"\"\n", + "\n", + " # Step 1: check whether input string begins with $\n", + " if s.startswith(\"$\"):\n", + " # Step 2: if so, remove it\n", + " s = s[1:]\n", + " \n", + " # Step 3: return the new string\n", + " return s" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Apply strip_dollar function and then apply int function" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['1', '23', '456']\n", + "[1, 23, 456]\n" + ] + } + ], + "source": [ + "L = [\"$1\", \"23\", \"$456\"]\n", + "vals = apply_to_each(L, strip_dollar)\n", + "print(vals)\n", + "vals = apply_to_each(vals, int)\n", + "print(vals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Apply upper method call to the below list L by using apply_to_each function" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['AAA', 'BBB', 'CCC']\n" + ] + } + ], + "source": [ + "L = [\"aaa\", \"bbb\", \"ccc\"]\n", + "vals = apply_to_each(L, str.upper)\n", + "print(vals)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'AAA'" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# remember, these are equivalent:\n", + "\"aaa\".upper()\n", + "str.upper(\"aaa\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Custom sorting nested data structures\n", + "\n", + "Examples:\n", + "- list of tuples\n", + "- list of dictionaries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 4: Custom sort a list of tuples" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('JJ', 'Watt', 31),\n", + " ('Jonathan', 'Taylor', 22),\n", + " ('Melvin', 'Gordon', 27),\n", + " ('Russel', 'Wilson', 32),\n", + " ('Troy', 'Fumagalli', 88)]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "badgers_in_nfl = [ # tuple storing (first name, last name, age)\n", + " (\"Jonathan\", \"Taylor\", 22 ), \n", + " (\"Russel\", \"Wilson\", 32), \n", + " (\"Troy\", \"Fumagalli\", 88),\n", + " (\"Melvin\", \"Gordon\", 27), \n", + " (\"JJ\", \"Watt\", 31),\n", + " ]\n", + "\n", + "sorted(badgers_in_nfl) # or sort() method by default uses first element to sort" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### What what if we want to sort by the last name or by the length of the name?\n", + "\n", + "- `sorted` function and `sort` method takes a function reference as keyword argument for the parameter `key`\n", + "- We can define functions that take one of the inner data structure as argument and return the field based on which we want to perform the sorting.\n", + " - We then pass a reference to such a function as argument to the parameter `key`.\n", + " \n", + "#### Define functions that will enable extraction of item at each tuple index position. These functions only deal with a single tuple processing" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "def extract_fname(player_tuple): # function must have exactly one parameter\n", + " return player_tuple[0]\n", + "\n", + "def extract_lname(player_tuple):\n", + " return player_tuple[1]\n", + "\n", + "def extract_age(player_tuple):\n", + " return player_tuple[2]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'JJ'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Test extract_fname function on the tuple ('JJ', 'Watt', 31)\n", + "extract_fname(('JJ', 'Watt', 31))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players by their last name" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('Troy', 'Fumagalli', 88),\n", + " ('Melvin', 'Gordon', 27),\n", + " ('Jonathan', 'Taylor', 22),\n", + " ('JJ', 'Watt', 31),\n", + " ('Russel', 'Wilson', 32)]" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(badgers_in_nfl, key = extract_lname) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players by their age" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('Jonathan', 'Taylor', 22),\n", + " ('Melvin', 'Gordon', 27),\n", + " ('JJ', 'Watt', 31),\n", + " ('Russel', 'Wilson', 32),\n", + " ('Troy', 'Fumagalli', 88)]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(badgers_in_nfl, key = extract_age) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players by descending order of age" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('Troy', 'Fumagalli', 88),\n", + " ('Russel', 'Wilson', 32),\n", + " ('JJ', 'Watt', 31),\n", + " ('Melvin', 'Gordon', 27),\n", + " ('Jonathan', 'Taylor', 22)]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(badgers_in_nfl, key = extract_age, reverse = True) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players by length of first name + length of last name" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('JJ', 'Watt', 31),\n", + " ('Russel', 'Wilson', 32),\n", + " ('Melvin', 'Gordon', 27),\n", + " ('Troy', 'Fumagalli', 88),\n", + " ('Jonathan', 'Taylor', 22)]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def compute_name_length(player_tuple):\n", + " first_name = extract_fname(player_tuple)\n", + " last_name = extract_lname(player_tuple)\n", + " return len(first_name) + len(last_name)\n", + "\n", + "sorted(badgers_in_nfl, key = compute_name_length) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 5: Custom sort a list of dictionaries" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "hurricanes = [\n", + " {\"name\": \"A\", \"year\": 2000, \"speed\": 150},\n", + " {\"name\": \"B\", \"year\": 1980, \"speed\": 100},\n", + " {\"name\": \"C\", \"year\": 1990, \"speed\": 250},\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Extract hurricane at index 0" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'A', 'year': 2000, 'speed': 150}" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hurricanes[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Extract hurricane at index 1" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'B', 'year': 1980, 'speed': 100}" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hurricanes[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Can you compare hurricane at index 0 and hurricane at index 1 using \"<\" operator?" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'<' not supported between instances of 'dict' and 'dict'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[29], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mhurricanes\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m<\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mhurricanes\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;66;03m#uncomment to see TypeError\u001b[39;00m\n", + "\u001b[0;31mTypeError\u001b[0m: '<' not supported between instances of 'dict' and 'dict'" + ] + } + ], + "source": [ + "hurricanes[0] < hurricanes[1] #uncomment to see TypeError" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### What about calling sorted method by passing hurricanes as argument?" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'<' not supported between instances of 'dict' and 'dict'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[30], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43msorted\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mhurricanes\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# Doesn't work because there isn't a defined \"first\" key in a dict.\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;66;03m# Unlike tuple, where the first item can be considered \"first\" by ordering.\u001b[39;00m\n", + "\u001b[0;31mTypeError\u001b[0m: '<' not supported between instances of 'dict' and 'dict'" + ] + } + ], + "source": [ + "sorted(hurricanes) # Doesn't work because there isn't a defined \"first\" key in a dict.\n", + "# Unlike tuple, where the first item can be considered \"first\" by ordering." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort hurricanes based on the year" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'B', 'year': 1980, 'speed': 100},\n", + " {'name': 'C', 'year': 1990, 'speed': 250},\n", + " {'name': 'A', 'year': 2000, 'speed': 150}]" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# a. Input: single hurricane's dict\n", + "# b. Output: return \"year\" value from the dict\n", + "\n", + "def get_year(hurricane_dict):\n", + " return hurricane_dict[\"year\"]\n", + "\n", + "sorted(hurricanes, key = get_year)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort hurricanes in descending order of their year" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'A', 'year': 2000, 'speed': 150},\n", + " {'name': 'C', 'year': 1990, 'speed': 250},\n", + " {'name': 'B', 'year': 1980, 'speed': 100}]" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(hurricanes, key = get_year, reverse = True) \n", + "# alternatively get_year function could return negative of year \n", + "# --- that produces the same result as passing True as argument to reverse parameter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort hurricanes in ascending order of their speed" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'C', 'year': 1990},\n", + " {'name': 'B', 'year': 1980, 'speed': 100},\n", + " {'name': 'A', 'year': 2000, 'speed': 150}]" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hurricanes = [\n", + " {\"name\": \"A\", \"year\": 2000, \"speed\": 150},\n", + " {\"name\": \"B\", \"year\": 1980, \"speed\": 100},\n", + " {\"name\": \"C\", \"year\": 1990}, # notice the missing speed key\n", + "]\n", + "\n", + "def get_speed(hurricane):\n", + " if not \"speed\" in hurricane:\n", + " return 0\n", + " return hurricane[\"speed\"]\n", + "\n", + "sorted(hurricanes, key = get_speed)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 6: How can you pass string method to sorted function?" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['A', 'C', 'b', 'd', 'e']" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted([\"A\", \"b\", \"e\", \"C\", \"d\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['A', 'b', 'C', 'd', 'e']" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted([\"A\", \"b\", \"e\", \"C\", \"d\"], key = str.upper) \n", + "# hint: to capitalize \"hello\", we call \"hello\".upper() or str.upper(\"hello\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sorting dictionary by keys / values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 7: sorting dictionaries" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'bob': 20, 'alice': 8, 'alex': 9, 'cindy': 15}" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "players = {\n", + " \"bob\": 20, \n", + " \"alice\": 8, \n", + " \"alex\": 9, \n", + " \"cindy\": 15} # Key: player_name; Value: score\n", + "players" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### This only returns a list of sorted keys. What if we want to create a new sorted dictionary object directly using sorted function?" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['alex', 'alice', 'bob', 'cindy']" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(players) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Let's learn about items method on a dictionary\n", + "- returns a list of tuples\n", + "- each tuple item contains two items: key and value" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('bob', 20), ('alice', 8), ('alex', 9), ('cindy', 15)])" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "players.items()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Write an extract function to extract dict value (that is player score), using items method return value" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "def extract_score(player_tuple):\n", + " print(player_tuple)\n", + " return player_tuple[1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# FYI, lambda version\n", + "dict( sorted(players.items(), key = lambda item: item[1] ))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sort players dict by key" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('bob', 20)\n", + "('alice', 8)\n", + "('alex', 9)\n", + "('cindy', 15)\n" + ] + }, + { + "data": { + "text/plain": [ + "[('alice', 8), ('alex', 9), ('cindy', 15), ('bob', 20)]" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# we'll walk through this step-by-step\n", + "# step1: \n", + "# sorted(???, key = ???)\n", + "\n", + "# step 2: fill in blanks\n", + "# sorted(players, key = extract_score) # --> uncomment!\n", + "\n", + "# step 3: evaluate the input, is it right?\n", + "# no -- what's happening? see next cell\n", + "# We want the input to extract_score to be a tuple, not a key\n", + "\n", + "# fix the code\n", + "sorted(players.items(), key = extract_score) # --> uncomment" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "o\n", + "l\n", + "l\n", + "i\n" + ] + } + ], + "source": [ + "# when we call sorted(players, key = extract_score), this is what gets compared under the hood:\n", + "for item in players:\n", + " print(item[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "How can you convert sorted list of tuples back into a `dict`?" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('bob', 20)\n", + "('alice', 8)\n", + "('alex', 9)\n", + "('cindy', 15)\n" + ] + }, + { + "data": { + "text/plain": [ + "{'alice': 8, 'alex': 9, 'cindy': 15, 'bob': 20}" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict (sorted(players.items(), key = extract_score)) # --> uncomment" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using `lambda`\n", + "- `lambda` functions are a way to abstract a function reference\n", + "- lambdas are simple functions with:\n", + " - multiple possible parameters\n", + " - single expression line as the function body\n", + "- lambdas are useful abstractions for:\n", + " - mathematical functions\n", + " - lookup operations\n", + "- lambdas are often associated with a collection of values within a list\n", + "- Syntax: \n", + "```python \n", + "lambda parameters: expression\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now let's write the same solution using lambda." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'alice': 8, 'alex': 9, 'cindy': 15, 'bob': 20}" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict( sorted(players.items(), key = lambda item: item[1] ))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### What about sorting dictionary by values using lambda?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# same as previous example\n", + "dict(sorted(players.items(), key = ???))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Now let's sort players dict using length of player name." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'bob': 20, 'alex': 9, 'alice': 8, 'cindy': 15}" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(sorted(players.items(), key = lambda item : len(item[0])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Self-practice: Use lambdas to solve the NFL sorting questions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(badgers_in_nfl)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players using their first name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players using their last name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players using their age" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sort players using the length of first name and last name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}