diff --git a/chapters/chap03.ipynb b/chapters/chap03.ipynb new file mode 100644 index 0000000..5a6b255 --- /dev/null +++ b/chapters/chap03.ipynb @@ -0,0 +1,1257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1331faa1", + "metadata": { + "id": "1331faa1" + }, + "source": [ + "You can order print and ebook versions of *Think Python 3e* from\n", + "[Bookshop.org](https://bookshop.org/a/98697/9781098155438) and\n", + "[Amazon](https://www.amazon.com/_/dp/1098155432?smid=ATVPDKIKX0DER&_encoding=UTF8&tag=oreilly20-20&_encoding=UTF8&tag=greenteapre01-20&linkCode=ur2&linkId=e2a529f94920295d27ec8a06e757dc7c&camp=1789&creative=9325)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "103cbe3c", + "metadata": { + "tags": [], + "id": "103cbe3c", + "outputId": "a311a077-01c6-4669-f8c5-42e065d21586", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloaded thinkpython.py\n", + "Downloaded diagram.py\n" + ] + } + ], + "source": [ + "from os.path import basename, exists\n", + "\n", + "def download(url):\n", + " filename = basename(url)\n", + " if not exists(filename):\n", + " from urllib.request import urlretrieve\n", + "\n", + " local, _ = urlretrieve(url, filename)\n", + " print(\"Downloaded \" + str(local))\n", + " return filename\n", + "\n", + "download('https://github.com/AllenDowney/ThinkPython/raw/v3/thinkpython.py');\n", + "download('https://github.com/AllenDowney/ThinkPython/raw/v3/diagram.py');\n", + "\n", + "import thinkpython" + ] + }, + { + "cell_type": "markdown", + "id": "6bd858a8", + "metadata": { + "id": "6bd858a8" + }, + "source": [ + "# Functions\n", + "\n", + "In the previous chapter we used several functions provided by Python, like `int` and `float`, and a few provided by the `math` module, like `sqrt` and `pow`.\n", + "In this chapter, you will learn how to create your own functions and run them.\n", + "And we'll see how one function can call another.\n", + "As examples, we'll display lyrics from Monty Python songs.\n", + "These silly examples demonstrate an important feature -- the ability to write your own functions is the foundation of programming.\n", + "\n", + "This chapter also introduces a new statement, the `for` loop, which is used to repeat a computation." + ] + }, + { + "cell_type": "markdown", + "id": "b4ea99c5", + "metadata": { + "id": "b4ea99c5" + }, + "source": [ + "## Defining new functions\n", + "\n", + "A **function definition** specifies the name of a new function and the sequence of statements that run when the function is called. Here's an example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d28f5c1a", + "metadata": { + "id": "d28f5c1a" + }, + "outputs": [], + "source": [ + "def print_lyrics():\n", + " print(\"I'm a lumberjack, and I'm okay.\")\n", + " print(\"I sleep all night and I work all day.\")" + ] + }, + { + "cell_type": "markdown", + "id": "0174fc41", + "metadata": { + "id": "0174fc41" + }, + "source": [ + "`def` is a keyword that indicates that this is a function definition.\n", + "The name of the function is `print_lyrics`.\n", + "Anything that's a legal variable name is also a legal function name.\n", + "\n", + "The empty parentheses after the name indicate that this function doesn't take any arguments.\n", + "\n", + "The first line of the function definition is called the **header** -- the rest is called the **body**.\n", + "The header has to end with a colon and the body has to be indented. By convention, indentation is always four spaces.\n", + "The body of this function is two print statements; in general, the body of a function can contain any number of statements of any kind.\n", + "\n", + "Defining a function creates a **function object**, which we can display like this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2850a402", + "metadata": { + "id": "2850a402" + }, + "outputs": [], + "source": [ + "print_lyrics" + ] + }, + { + "cell_type": "markdown", + "id": "12bd0879", + "metadata": { + "id": "12bd0879" + }, + "source": [ + "The output indicates that `print_lyrics` is a function that takes no arguments.\n", + "`__main__` is the name of the module that contains `print_lyrics`.\n", + "\n", + "Now that we've defined a function, we can call it the same way we call built-in functions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a048657", + "metadata": { + "id": "9a048657" + }, + "outputs": [], + "source": [ + "print_lyrics()" + ] + }, + { + "cell_type": "markdown", + "id": "8f0fc45d", + "metadata": { + "id": "8f0fc45d" + }, + "source": [ + "When the function runs, it executes the statements in the body, which display the first two lines of \"The Lumberjack Song\"." + ] + }, + { + "cell_type": "markdown", + "id": "6d35193e", + "metadata": { + "id": "6d35193e" + }, + "source": [ + "## Parameters\n", + "\n", + "Some of the functions we have seen require arguments; for example, when you call `abs` you pass a number as an argument.\n", + "Some functions take more than one argument; for example, `math.pow` takes two, the base and the exponent.\n", + "\n", + "Here is a definition for a function that takes an argument." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5d00488", + "metadata": { + "id": "e5d00488" + }, + "outputs": [], + "source": [ + "def print_twice(string):\n", + " print(string)\n", + " print(string)" + ] + }, + { + "cell_type": "markdown", + "id": "1716e3dc", + "metadata": { + "id": "1716e3dc" + }, + "source": [ + "The variable name in parentheses is a **parameter**.\n", + "When the function is called, the value of the argument is assigned to the parameter.\n", + "For example, we can call `print_twice` like this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3ad5f46", + "metadata": { + "id": "a3ad5f46" + }, + "outputs": [], + "source": [ + "print_twice('Dennis Moore, ')" + ] + }, + { + "cell_type": "markdown", + "id": "f02be6d2", + "metadata": { + "id": "f02be6d2" + }, + "source": [ + "Running this function has the same effect as assigning the argument to the parameter and then executing the body of the function, like this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "042dfec1", + "metadata": { + "id": "042dfec1" + }, + "outputs": [], + "source": [ + "string = 'Dennis Moore, '\n", + "print(string)\n", + "print(string)" + ] + }, + { + "cell_type": "markdown", + "id": "ea8b8b6e", + "metadata": { + "id": "ea8b8b6e" + }, + "source": [ + "You can also use a variable as an argument." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f078ad0", + "metadata": { + "id": "8f078ad0" + }, + "outputs": [], + "source": [ + "line = 'Dennis Moore, '\n", + "print_twice(line)" + ] + }, + { + "cell_type": "markdown", + "id": "5c1884ad", + "metadata": { + "id": "5c1884ad" + }, + "source": [ + "In this example, the value of `line` gets assigned to the parameter `string`." + ] + }, + { + "cell_type": "markdown", + "id": "a3e5a790", + "metadata": { + "id": "a3e5a790" + }, + "source": [ + "## Calling functions\n", + "\n", + "Once you have defined a function, you can use it inside another function.\n", + "To demonstrate, we'll write functions that print the lyrics of \"The Spam Song\" ().\n", + "\n", + "> Spam, Spam, Spam, Spam, \n", + "> Spam, Spam, Spam, Spam, \n", + "> Spam, Spam, \n", + "> (Lovely Spam, Wonderful Spam!) \n", + "> Spam, Spam,\n", + "\n", + "We'll start with the following function, which takes two parameters.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e86bb32c", + "metadata": { + "id": "e86bb32c" + }, + "outputs": [], + "source": [ + "def repeat(word, n):\n", + " print(word * n)" + ] + }, + { + "cell_type": "markdown", + "id": "bdd4daa4", + "metadata": { + "id": "bdd4daa4" + }, + "source": [ + "We can use this function to print the first line of the song, like this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec117999", + "metadata": { + "id": "ec117999" + }, + "outputs": [], + "source": [ + "spam = 'Spam, '\n", + "repeat(spam, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "c6f81e09", + "metadata": { + "id": "c6f81e09" + }, + "source": [ + "To display the first two lines, we can define a new function that uses `repeat`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3731ffd8", + "metadata": { + "id": "3731ffd8" + }, + "outputs": [], + "source": [ + "def first_two_lines():\n", + " repeat(spam, 4)\n", + " repeat(spam, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "8058ffe4", + "metadata": { + "id": "8058ffe4" + }, + "source": [ + "And then call it like this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6792e63b", + "metadata": { + "id": "6792e63b" + }, + "outputs": [], + "source": [ + "first_two_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "07ca432a", + "metadata": { + "id": "07ca432a" + }, + "source": [ + "To display the last three lines, we can define another function, which also uses `repeat`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2dcb020a", + "metadata": { + "id": "2dcb020a" + }, + "outputs": [], + "source": [ + "def last_three_lines():\n", + " repeat(spam, 2)\n", + " print('(Lovely Spam, Wonderful Spam!)')\n", + " repeat(spam, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ff8c60e", + "metadata": { + "id": "9ff8c60e" + }, + "outputs": [], + "source": [ + "last_three_lines()" + ] + }, + { + "cell_type": "markdown", + "id": "d6456a19", + "metadata": { + "id": "d6456a19" + }, + "source": [ + "Finally, we can bring it all together with one function that prints the whole verse." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78bf3a7b", + "metadata": { + "id": "78bf3a7b" + }, + "outputs": [], + "source": [ + "def print_verse():\n", + " first_two_lines()\n", + " last_three_lines()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba5da431", + "metadata": { + "id": "ba5da431" + }, + "outputs": [], + "source": [ + "print_verse()" + ] + }, + { + "cell_type": "markdown", + "id": "d088fe68", + "metadata": { + "id": "d088fe68" + }, + "source": [ + "When we run `print_verse`, it calls `first_two_lines`, which calls `repeat`, which calls `print`.\n", + "That's a lot of functions.\n", + "\n", + "Of course, we could have done the same thing with fewer functions, but the point of this example is to show how functions can work together." + ] + }, + { + "cell_type": "markdown", + "id": "c3b16e3f", + "metadata": { + "id": "c3b16e3f" + }, + "source": [ + "## Repetition\n", + "\n", + "If we want to display more than one verse, we can use a `for` statement.\n", + "Here's a simple example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29b7eff3", + "metadata": { + "id": "29b7eff3" + }, + "outputs": [], + "source": [ + "for i in range(2):\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "id": "bf320549", + "metadata": { + "id": "bf320549" + }, + "source": [ + "The first line is a header that ends with a colon.\n", + "The second line is the body, which has to be indented.\n", + "\n", + "The header starts with the keyword `for`, a new variable named `i`, and another keyword, `in`.\n", + "It uses the `range` function to create a sequence of two values, which are `0` and `1`.\n", + "In Python, when we start counting, we usually start from `0`.\n", + "\n", + "When the `for` statement runs, it assigns the first value from `range` to `i` and then runs the `print` function in the body, which displays `0`.\n", + "\n", + "When it gets to the end of the body, it loops back around to the header, which is why this statement is called a **loop**.\n", + "The second time through the loop, it assigns the next value from `range` to `i`, and displays it.\n", + "Then, because that's the last value from `range`, the loop ends.\n", + "\n", + "Here's how we can use a `for` loop to print two verses of the song." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "038ad592", + "metadata": { + "id": "038ad592" + }, + "outputs": [], + "source": [ + "for i in range(2):\n", + " print(\"Verse\", i)\n", + " print_verse()\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "88a46733", + "metadata": { + "id": "88a46733" + }, + "source": [ + "You can put a `for` loop inside a function.\n", + "For example, `print_n_verses` takes a parameter named `n`, which has to be an integer, and displays the given number of verses." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8887637a", + "metadata": { + "id": "8887637a" + }, + "outputs": [], + "source": [ + "def print_n_verses(n):\n", + " for i in range(n):\n", + " print_verse()\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "id": "ad8060fe", + "metadata": { + "id": "ad8060fe" + }, + "source": [ + "In this example, we don't use `i` in the body of the loop, but there has to be a variable name in the header anyway." + ] + }, + { + "cell_type": "markdown", + "id": "b320ec90", + "metadata": { + "id": "b320ec90" + }, + "source": [ + "## Variables and parameters are local\n", + "\n", + "When you create a variable inside a function, it is **local**, which\n", + "means that it only exists inside the function.\n", + "For example, the following function takes two arguments, concatenates them, and prints the result twice." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0db8408e", + "metadata": { + "id": "0db8408e" + }, + "outputs": [], + "source": [ + "def cat_twice(part1, part2):\n", + " cat = part1 + part2\n", + " print_twice(cat)" + ] + }, + { + "cell_type": "markdown", + "id": "3a35a6d0", + "metadata": { + "id": "3a35a6d0" + }, + "source": [ + "Here's an example that uses it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c556e48", + "metadata": { + "id": "1c556e48" + }, + "outputs": [], + "source": [ + "line1 = 'Always look on the '\n", + "line2 = 'bright side of life.'\n", + "cat_twice(line1, line2)" + ] + }, + { + "cell_type": "markdown", + "id": "4ab4e008", + "metadata": { + "id": "4ab4e008" + }, + "source": [ + "When `cat_twice` runs, it creates a local variable named `cat`, which is destroyed when the function ends.\n", + "If we try to display it, we get a `NameError`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73f03eea", + "metadata": { + "tags": [], + "id": "73f03eea" + }, + "outputs": [], + "source": [ + "%%expect NameError\n", + "\n", + "print(cat)" + ] + }, + { + "cell_type": "markdown", + "id": "3ae36c29", + "metadata": { + "id": "3ae36c29" + }, + "source": [ + "Outside of the function, `cat` is not defined.\n", + "\n", + "Parameters are also local.\n", + "For example, outside `cat_twice`, there is no such thing as `part1` or `part2`." + ] + }, + { + "cell_type": "markdown", + "id": "eabac8a6", + "metadata": { + "id": "eabac8a6" + }, + "source": [ + "## Stack diagrams\n", + "\n", + "To keep track of which variables can be used where, it is sometimes useful to draw a **stack diagram**.\n", + "Like state diagrams, stack diagrams show the value of each variable, but they also show the function each variable belongs to.\n", + "\n", + "Each function is represented by a **frame**.\n", + "A frame is a box with the name of a function on the outside and the parameters and local variables of the function on the inside.\n", + "\n", + "Here's the stack diagram for the previous example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83df4e32", + "metadata": { + "tags": [], + "id": "83df4e32" + }, + "outputs": [], + "source": [ + "from diagram import make_frame, Stack\n", + "\n", + "d1 = dict(line1=line1, line2=line2)\n", + "frame1 = make_frame(d1, name='__main__', dy=-0.3, loc='left')\n", + "\n", + "d2 = dict(part1=line1, part2=line2, cat=line1+line2)\n", + "frame2 = make_frame(d2, name='cat_twice', dy=-0.3,\n", + " offsetx=0.03, loc='left')\n", + "\n", + "d3 = dict(string=line1+line2)\n", + "frame3 = make_frame(d3, name='print_twice',\n", + " offsetx=0.04, offsety=-0.3, loc='left')\n", + "\n", + "d4 = {\"?\": line1+line2}\n", + "frame4 = make_frame(d4, name='print',\n", + " offsetx=-0.22, offsety=0, loc='left')\n", + "\n", + "stack = Stack([frame1, frame2, frame3, frame4], dy=-0.8)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcd5e1df", + "metadata": { + "tags": [], + "id": "bcd5e1df" + }, + "outputs": [], + "source": [ + "from diagram import diagram, adjust\n", + "\n", + "\n", + "width, height, x, y = [3.77, 2.9, 1.1, 2.65]\n", + "ax = diagram(width, height)\n", + "bbox = stack.draw(ax, x, y)\n", + "# adjust(x, y, bbox)\n", + "\n", + "import matplotlib.pyplot as plt\n", + "plt.savefig('chap03_stack_diagram.png', dpi=300)" + ] + }, + { + "cell_type": "markdown", + "id": "854fee12", + "metadata": { + "id": "854fee12" + }, + "source": [ + "The frames are arranged in a stack that indicates which function called\n", + "which, and so on. Reading from the bottom, `print` was called by `print_twice`, which was called by `cat_twice`, which was called by `__main__` -- which is a special name for the topmost frame.\n", + "When you create a variable outside of any function, it belongs to `__main__`.\n", + "\n", + "In the frame for `print`, the question mark indicates that we don't know the name of the parameter.\n", + "If you are curious, ask a virtual assistant, \"What are the parameters of the Python print function?\"" + ] + }, + { + "cell_type": "markdown", + "id": "5690cfc0", + "metadata": { + "id": "5690cfc0" + }, + "source": [ + "## Tracebacks\n", + "\n", + "When a runtime error occurs in a function, Python displays the name of the function that was running, the name of the function that called it, and so on, up the stack.\n", + "To see an example, I'll define a version of `print_twice` that contains an error -- it tries to print `cat`, which is a local variable in another function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "886519cf", + "metadata": { + "id": "886519cf" + }, + "outputs": [], + "source": [ + "def print_twice(string):\n", + " print(cat) # NameError\n", + " print(cat)" + ] + }, + { + "cell_type": "markdown", + "id": "d7c0713b", + "metadata": { + "id": "d7c0713b" + }, + "source": [ + "Now here's what happens when we run `cat_twice`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1fe8ee82", + "metadata": { + "tags": [], + "id": "1fe8ee82" + }, + "outputs": [], + "source": [ + "# This cell tells Jupyter to provide detailed debugging information\n", + "# when a runtime error occurs, including a traceback.\n", + "\n", + "%xmode Verbose" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9082f88", + "metadata": { + "tags": [], + "id": "d9082f88" + }, + "outputs": [], + "source": [ + "%%expect NameError\n", + "\n", + "cat_twice(line1, line2)" + ] + }, + { + "cell_type": "markdown", + "id": "2f4defcf", + "metadata": { + "id": "2f4defcf" + }, + "source": [ + "The error message includes a **traceback**, which shows the function that was running when the error occurred, the function that called it, and so on.\n", + "In this example, it shows that `cat_twice` called `print_twice`, and the error occurred in a `print_twice`.\n", + "\n", + "The order of the functions in the traceback is the same as the order of the frames in the stack diagram.\n", + "The function that was running is at the bottom." + ] + }, + { + "cell_type": "markdown", + "id": "374b4696", + "metadata": { + "id": "374b4696" + }, + "source": [ + "## Why functions?\n", + "\n", + "It may not be clear yet why it is worth the trouble to divide a program into\n", + "functions.\n", + "There are several reasons:\n", + "\n", + "- Creating a new function gives you an opportunity to name a group of\n", + " statements, which makes your program easier to read and debug.\n", + "\n", + "- Functions can make a program smaller by eliminating repetitive code.\n", + " Later, if you make a change, you only have to make it in one place.\n", + "\n", + "- Dividing a long program into functions allows you to debug the parts\n", + " one at a time and then assemble them into a working whole.\n", + "\n", + "- Well-designed functions are often useful for many programs. Once you\n", + " write and debug one, you can reuse it." + ] + }, + { + "cell_type": "markdown", + "id": "c6dd486e", + "metadata": { + "id": "c6dd486e" + }, + "source": [ + "## Debugging\n", + "\n", + "Debugging can be frustrating, but it is also challenging, interesting, and sometimes even fun.\n", + "And it is one of the most important skills you can learn.\n", + "\n", + "In some ways debugging is like detective work.\n", + "You are given clues and you have to infer the events that led to the\n", + "results you see.\n", + "\n", + "Debugging is also like experimental science.\n", + "Once you have an idea about what is going wrong, you modify your program and try again.\n", + "If your hypothesis was correct, you can predict the result of the modification, and you take a step closer to a working program.\n", + "If your hypothesis was wrong, you have to come up with a new one.\n", + "\n", + "For some people, programming and debugging are the same thing; that is, programming is the process of gradually debugging a program until it does what you want.\n", + "The idea is that you should start with a working program and make small modifications, debugging them as you go.\n", + "\n", + "If you find yourself spending a lot of time debugging, that is often a sign that you are writing too much code before you start tests.\n", + "If you take smaller steps, you might find that you can move faster." + ] + }, + { + "cell_type": "markdown", + "id": "d4e95e63", + "metadata": { + "id": "d4e95e63" + }, + "source": [ + "## Glossary\n", + "\n", + "**function definition:**\n", + "A statement that creates a function.\n", + "\n", + "**header:**\n", + " The first line of a function definition.\n", + "\n", + "**body:**\n", + " The sequence of statements inside a function definition.\n", + "\n", + "**function object:**\n", + "A value created by a function definition.\n", + "The name of the function is a variable that refers to a function object.\n", + "\n", + "**parameter:**\n", + " A name used inside a function to refer to the value passed as an argument.\n", + "\n", + "**loop:**\n", + " A statement that runs one or more statements, often repeatedly.\n", + "\n", + "**local variable:**\n", + "A variable defined inside a function, and which can only be accessed inside the function.\n", + "\n", + "**stack diagram:**\n", + "A graphical representation of a stack of functions, their variables, and the values they refer to.\n", + "\n", + "**frame:**\n", + " A box in a stack diagram that represents a function call.\n", + " It contains the local variables and parameters of the function.\n", + "\n", + "**traceback:**\n", + " A list of the functions that are executing, printed when an exception occurs." + ] + }, + { + "cell_type": "markdown", + "id": "eca485f2", + "metadata": { + "id": "eca485f2" + }, + "source": [ + "## Exercises" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3f77b428", + "metadata": { + "tags": [], + "id": "3f77b428" + }, + "outputs": [], + "source": [ + "# This cell tells Jupyter to provide detailed debugging information\n", + "# when a runtime error occurs. Run it before working on the exercises.\n", + "\n", + "%xmode Verbose" + ] + }, + { + "cell_type": "markdown", + "id": "82951027", + "metadata": { + "id": "82951027" + }, + "source": [ + "### Ask a virtual assistant\n", + "\n", + "The statements in a function or a `for` loop are indented by four spaces, by convention.\n", + "But not everyone agrees with that convention.\n", + "If you are curious about the history of this great debate, ask a virtual assistant to \"tell me about spaces and tabs in Python\".\n", + "\n", + "Virtual assistant are pretty good at writing small functions.\n", + "\n", + "1. Ask your favorite VA to \"Write a function called repeat that takes a string and an integer and prints the string the given number of times.\"\n", + "\n", + "2. If the result uses a `for` loop, you could ask, \"Can you do it without a for loop?\"\n", + "\n", + "3. Pick any other function in this chapter and ask a VA to write it. The challenge is to describe the function precisely enough to get what you want. Use the vocabulary you have learned so far in this book.\n", + "\n", + "Virtual assistants are also pretty good at debugging functions.\n", + "\n", + "1. Ask a VA what's wrong with this version of `print_twice`.\n", + "\n", + " ```\n", + " def print_twice(string):\n", + " print(cat)\n", + " print(cat)\n", + " ```\n", + " \n", + "And if you get stuck on any of the exercises below, consider asking a VA for help." + ] + }, + { + "cell_type": "markdown", + "id": "b7157b09", + "metadata": { + "id": "b7157b09" + }, + "source": [ + "### Exercise\n", + "\n", + "Write a function named `print_right` that takes a string named `text` as a parameter and prints the string with enough leading spaces that the last letter of the string is in the 40th column of the display." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6004271", + "metadata": { + "id": "a6004271" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "428fbee5", + "metadata": { + "id": "428fbee5" + }, + "source": [ + "Hint: Use the `len` function, the string concatenation operator (`+`) and the string repetition operator (`*`).\n", + "\n", + "Here's an example that shows how it should work." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f142ce6a", + "metadata": { + "tags": [], + "id": "f142ce6a" + }, + "outputs": [], + "source": [ + "print_right(\"Monty\")\n", + "print_right(\"Python's\")\n", + "print_right(\"Flying Circus\")" + ] + }, + { + "cell_type": "markdown", + "id": "b47467fa", + "metadata": { + "id": "b47467fa" + }, + "source": [ + "### Exercise\n", + "\n", + "Write a function called `triangle` that takes a string and an integer and draws a pyramid with the given height, made up using copies of the string. Here's an example of a pyramid with `5` levels, using the string `'L'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7aa95014", + "metadata": { + "id": "7aa95014" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8146a0d", + "metadata": { + "scrolled": true, + "tags": [], + "id": "b8146a0d" + }, + "outputs": [], + "source": [ + "triangle('L', 5)" + ] + }, + { + "cell_type": "markdown", + "id": "4a28f635", + "metadata": { + "id": "4a28f635" + }, + "source": [ + "### Exercise\n", + "\n", + "Write a function called `rectangle` that takes a string and two integers and draws a rectangle with the given width and height, made up using copies of the string. Here's an example of a rectangle with width `5` and height `4`, made up of the string `'H'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcedab79", + "metadata": { + "id": "bcedab79" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73b0c0f6", + "metadata": { + "scrolled": true, + "tags": [], + "id": "73b0c0f6" + }, + "outputs": [], + "source": [ + "rectangle('H', 5, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "44a5de6f", + "metadata": { + "id": "44a5de6f" + }, + "source": [ + "### Exercise\n", + "\n", + "The song \"99 Bottles of Beer\" starts with this verse:\n", + "\n", + "> 99 bottles of beer on the wall \n", + "> 99 bottles of beer \n", + "> Take one down, pass it around \n", + "> 98 bottles of beer on the wall \n", + "\n", + "Then the second verse is the same, except that it starts with 98 bottles and ends with 97. The song continues -- for a very long time -- until there are 0 bottles of beer.\n", + "\n", + "Write a function called `bottle_verse` that takes a number as a parameter and displays the verse that starts with the given number of bottles.\n", + "\n", + "Hint: Consider starting with a function that can print the first, second, or last line of the verse, and then use it to write `bottle_verse`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53424b43", + "metadata": { + "id": "53424b43" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61010ffb", + "metadata": { + "id": "61010ffb" + }, + "outputs": [], + "source": [ + "# Solution goes here" + ] + }, + { + "cell_type": "markdown", + "id": "ee0076dd", + "metadata": { + "tags": [], + "id": "ee0076dd" + }, + "source": [ + "Use this function call to display the first verse." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47a91c7d", + "metadata": { + "tags": [], + "id": "47a91c7d" + }, + "outputs": [], + "source": [ + "bottle_verse(99)" + ] + }, + { + "cell_type": "markdown", + "id": "42c237c6", + "metadata": { + "tags": [], + "id": "42c237c6" + }, + "source": [ + "If you want to print the whole song, you can use this `for` loop, which counts down from `99` to `1`.\n", + "You don't have to completely understand this example---we'll learn more about `for` loops and the `range` function later." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "336cdfa2", + "metadata": { + "tags": [], + "id": "336cdfa2" + }, + "outputs": [], + "source": [ + "for n in range(99, 0, -1):\n", + " bottle_verse(n)\n", + " print()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b02510c", + "metadata": { + "id": "4b02510c" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "a7f4edf8", + "metadata": { + "tags": [], + "id": "a7f4edf8" + }, + "source": [ + "[Think Python: 3rd Edition](https://allendowney.github.io/ThinkPython/index.html)\n", + "\n", + "Copyright 2024 [Allen B. Downey](https://allendowney.com)\n", + "\n", + "Code license: [MIT License](https://mit-license.org/)\n", + "\n", + "Text license: [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "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.11" + }, + "colab": { + "provenance": [] + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file