diff --git a/00_start_up_review_and_exercises.ipynb b/00_start_up_review_and_exercises.ipynb index e1a435f..2f4c334 100644 --- a/00_start_up_review_and_exercises.ipynb +++ b/00_start_up_review_and_exercises.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 0 of the book. Then work through the ten review questions." + "Read [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) of the book. Then work through the ten review questions." ] }, { diff --git a/01_elements.ipynb b/01_elements.ipynb index e1d8b09..382d757 100644 --- a/01_elements.ipynb +++ b/01_elements.ipynb @@ -24,7 +24,7 @@ "\n", "It is intuitively best to take the very mindset of a small child when learing a foreign language and we do so for learning the Python language as well. This first chapter introduces simplistic examples and we better just accept them as they are without knowing any of the \"grammar\" rules yet. Then, we analyze them in parts and slowly build up our understanding.\n", "\n", - "Consequently, if parts of this chapter do not make sense right away, let's not worry too much. Besides introducing some basics (that we need to understand), it also serves as an outlook for what is to come. So, many terms and concepts referenced here will be covered in great detail in following chapters." + "Consequently, if parts of this chapter do not make sense right away, let's not worry too much. Besides introducing some basics that we need to understand, it also serves as an outlook for what is to come. So, many terms and concepts used here will be covered in great detail in following chapters." ] }, { @@ -46,7 +46,7 @@ } }, "source": [ - "As our introductory example, we want to calculate the average of all even numbers from one through ten.\n", + "As our introductory example, we want to calculate the *average* of all *even* numbers from *one* through *ten*.\n", "\n", "While we could come up with an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) (i.e., derive some equation with \"pen and paper\" from, e.g., one of [Faulhaber's formulas](https://en.wikipedia.org/wiki/Faulhaber%27s_formula)), we instead solve the task programmatically.\n", "\n", @@ -75,7 +75,7 @@ } }, "source": [ - "To verify that something happened in our computer's memory, we simply **reference** `numbers` and observe that Python indeed knows about." + "To verify that something happened in our computer's memory, we simply **reference** `numbers` and observe that Python indeed knows about it." ] }, { @@ -110,17 +110,17 @@ } }, "source": [ - "So far, so good. Let's see how the desired result could be expressed as a **sequence of instructions** in Python.\n", + "So far, so good. Let's see how the desired **computation** could be expressed as a **sequence of instructions** in Python.\n", "\n", "Intuitively, the line `for number in numbers` describes a \"loop\" over all the numbers in the `numbers` list, one at a time.\n", "\n", "The `if number % 2 == 0` may look disturbing at first sight. Both the `%` and `==` must have an unintuitive meaning here. Luckily, the **comment** in the same line after the `#` symbol has the answer: The program only does something if the current `number` is even.\n", "\n", - "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` were initially set to $0$ and the single `=` reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", + "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` were initially set to $0$ and the single `=` symbol reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", "\n", "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by the count of all even numbers, which is exactly what we are looking for.\n", "\n", - "We also observe how the lines of code \"within\" the `for` and `if` **statements** are *indented* and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." + "We also observe how the lines of code \"within\" the `for` and `if` **statements** are **indented** and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." ] }, { @@ -200,7 +200,7 @@ "source": [ "Note how only two of the previous four code cells generate an **output** while two remained \"silent\" (i.e., there is no \"**Out[...]**\" after running the cell).\n", "\n", - "By default, Jupyter notebooks show the value of a cell's last so-called **expression**. This output can be suppressed by ending the last line with a semicolon `;`." + "By default, Jupyter notebooks show the value of a cell's last **expression**. This output can be suppressed by ending the last line with a semicolon `;`." ] }, { @@ -249,7 +249,7 @@ } }, "source": [ - "To visualize something before the end of the cell, we can use the [print()](https://docs.python.org/3/library/functions.html#print) built-in **function**." + "To visualize something before the end of the cell, we use the [print()](https://docs.python.org/3/library/functions.html#print) built-in **function**." ] }, { @@ -327,11 +327,13 @@ } }, "source": [ - "Python comes with basic mathematical operators built in. **[Operators](https://docs.python.org/3/reference/lexical_analysis.html#operators)** are built-in **tokens** that have a special meaning to the Python interpreter. Most operators either \"operate\" with the object immediately following them (= **unary** operators; e.g., negation) or somehow \"process\" the two objects \"around\" them (= **binary** operators; e.g., addition). But we will see some exceptions from that as well.\n", + "Python comes with many **[operators](https://docs.python.org/3/reference/lexical_analysis.html#operators)** built in: They are **tokens** (i.e., \"symbols\") that have a special meaning to the Python interpreter.\n", "\n", - "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed lead to *new* objects being created in memory, they are immediately \"forgotten\" as they are not stored in a **variable** (like `numbers` above). We will revisit this idea further below when we compare **expressions** with **statements**.\n", + "The arithmetic operators either \"operate\" with the number immediately following them (= **unary** operators; e.g., negation) or \"process\" the two numbers \"around\" them (= **binary** operators; e.g., addition). But we will see many exceptions from that as well.\n", "\n", - "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators resemble what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." + "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed create *new* numbers in memory (e.g., `77 + 13` creates `90`), they are immediately \"forgotten\" as they are not stored in a **variable** like `numbers` or `average` above. We will continue this thought further below when we compare **expressions** with **statements**.\n", + "\n", + "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators are designed to resemble what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." ] }, { @@ -425,7 +427,7 @@ } }, "source": [ - "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle difference between the $42$ and the $42.0$. This is a first illustration of the concept of a **data type**." + "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle *difference* between the $42$ and the $42.0$. This is a first illustration of the concept of a **data type**." ] }, { @@ -543,7 +545,7 @@ } }, "source": [ - "To obtain the remainder of a division, we can use the **modulo operator** `%`." + "To obtain the remainder of a division, we use the **modulo operator** `%`." ] }, { @@ -613,7 +615,7 @@ } }, "source": [ - "Modulo division can be useful if we, for example, need to get the last couple of digits of a large integer." + "Modulo division is also useful if we, for example, need to get the last couple of digits of a large integer." ] }, { @@ -672,7 +674,7 @@ } }, "source": [ - "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator any more (but a function). Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." + "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator but a function. Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." ] }, { @@ -707,7 +709,7 @@ } }, "source": [ - "Raising a number to a power is performed with the **exponentiation operator** `**`. Note that this is different from the `^` operator many other programming languages might use and that also exists in Python with a *different* meaning." + "Raising a number to a power is performed with the **exponentiation operator** `**`. This is different from the `^` operator many other programming languages use and that also exists in Python with a *different* meaning." ] }, { @@ -742,7 +744,7 @@ } }, "source": [ - "The normal [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule) but parentheses help avoid confusion." + "The normal [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule)." ] }, { @@ -777,7 +779,7 @@ } }, "source": [ - "The parentheses here serve as a **delimiter**." + "Parentheses help avoid confusion and take the role of a **delimiter** here." ] }, { @@ -871,7 +873,7 @@ } }, "source": [ - "There are plenty more mathematical and non-mathematical operators that are introduced throughout this book together with the concepts they implement or support. Some of these are already shown in the next section." + "There are many more non-mathematical operators that are introduced throughout this book together with the concepts they implement. Some of these are already shown in the next section." ] }, { @@ -949,7 +951,7 @@ { "data": { "text/plain": [ - "139940106427216" + "140658972730512" ] }, "execution_count": 27, @@ -973,7 +975,7 @@ { "data": { "text/plain": [ - "139940106602344" + "140658972907392" ] }, "execution_count": 28, @@ -997,7 +999,7 @@ { "data": { "text/plain": [ - "139940105769456" + "140658972586992" ] }, "execution_count": 29, @@ -1017,7 +1019,7 @@ } }, "source": [ - "These addresses are really not that meaningful for anything other than checking if two variables actually **point** at the same object. This may be helpful as different objects can of course have the same value." + "These addresses are really not that meaningful for anything other than checking if two variables actually **point** at the same object. This may be helpful as, for example, different objects in memory may of course have the same value." ] }, { @@ -1041,7 +1043,7 @@ } }, "source": [ - "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in Chapter 3." + "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)." ] }, { @@ -1780,7 +1782,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 50, @@ -1801,7 +1803,7 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., Chapter 2) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error prone." + "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error prone." ] }, { @@ -2003,7 +2005,7 @@ "\n", "At the same time, for a beginner's course it is often easier to just code in a linear fashion.\n", "\n", - "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., Chapter 2) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." + "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." ] }, { @@ -2868,7 +2870,7 @@ "\n", "In general, the assignment statement creates (or overwrites) a variable and makes it point to whatever object is on the right-hand side *only if* the left-hand side is a *pure* name (i.e., it contains no operators like the indexing operator in the example). Otherwise, it mutates some already existing object. And we always have to expect that the latter might have more than one variable pointing at it.\n", "\n", - "In the beginning, visualizing the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Adel%20x,%20y%0Ax%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x.copy%28%29%0Ax%5B0%5D%20%3D%2099&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) will assist in understanding what is going on." + "Visualizing what is going on in the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Aprint%28y%5B0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) might be helpful for a beginner." ] }, { @@ -2903,7 +2905,7 @@ } }, "source": [ - "Variable names may contain upper and lower case letters, numbers, and underscores (\"\\_\") and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", + "Variable names may contain upper and lower case letters, numbers, and underscores (\"\\_\") and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's built-in **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", "\n", "Variable names are usually chosen such that they do not need any more documentation and are self-explanatory. A very common convention is to use so-called **[snake\\_case](https://en.wikipedia.org/wiki/Snake_case)**: Keep everything lowercase and use underscores to seperate words.\n", "\n", @@ -3150,7 +3152,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 93, @@ -3188,7 +3190,7 @@ "\n", "What we said about individual operators above, namely that they have *no* side effects, should have been put here to begin with. The examples in the section on operators above were actually all expressions!\n", "\n", - "The simplest possible expression contains only one variable (or literal)." + "The simplest possible expressions contain only one variable or literal." ] }, { @@ -3218,6 +3220,33 @@ { "cell_type": "code", "execution_count": 95, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For sure, we need to include operators to achieve something useful." + ] + }, + { + "cell_type": "code", + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "-" @@ -3230,7 +3259,7 @@ "165" ] }, - "execution_count": 95, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3247,12 +3276,12 @@ } }, "source": [ - "The definition of an expression is **recursive**. So here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form the full expression." + "The definition of an expression is **recursive**. So, here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form the full expression." ] }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "-" @@ -3265,7 +3294,7 @@ "4492125" ] }, - "execution_count": 96, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3282,12 +3311,12 @@ } }, "source": [ - "Here, the variable `y` is combined with the literal `2` by the indexing operator `[]`. The resulting expression evaluates to the " + "Here, the variable `y` is combined with the literal `2` by the indexing operator `[]`. The resulting expression evaluates to the third element in the `y` list." ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "-" @@ -3300,7 +3329,7 @@ "3" ] }, - "execution_count": 97, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3317,12 +3346,12 @@ } }, "source": [ - "When not used as a **delimiter**, parentheses also constitute an operator, namely the **call operator** `()`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." + "When not used as a delimiter, parentheses also constitute an operator, namely the **call operator** `()`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." ] }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" @@ -3335,7 +3364,7 @@ "104" ] }, - "execution_count": 98, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3368,7 +3397,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "slide" @@ -3382,7 +3411,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3395,7 +3424,7 @@ "'Hi class'" ] }, - "execution_count": 100, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -3417,7 +3446,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3430,7 +3459,7 @@ "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" ] }, - "execution_count": 101, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -3458,14 +3487,14 @@ } }, "source": [ - "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that changes the state of the program's memory or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", + "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that changes the state of a program or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", "\n", "Most notably of course are the `=` and `del` statements." ] }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "slide" @@ -3478,7 +3507,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "-" @@ -3497,12 +3526,12 @@ } }, "source": [ - "The [print()](https://docs.python.org/3/library/functions.html#print) function is regarded a \"statement\" as well. In fact, it used to be a real statement in Python 2 and has all the necessary properties. It is a bit of a corner case as expressions are also \"printed\" in a Jupyter notebook when evaluated last in a code cell." + "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function is regarded a \"statement\" as well. In fact, it used to be a real statement in Python 2 and has all the necessary properties. It is a bit of a corner case as expressions are also \"printed\" in a Jupyter notebook when evaluated last in a code cell." ] }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "skip" @@ -3549,7 +3578,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "slide" @@ -3577,7 +3606,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3590,7 +3619,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "-" @@ -3690,14 +3719,14 @@ " - ignored by Python\n", "\n", "\n", - "- functions (cf., Chapter 2)\n", + "- functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb))\n", " - named sequences of instructions\n", " - the smaller parts in a larger program\n", " - make a program more modular and thus easier to understand\n", "\n", "\n", - "- flow control (cf., Chapter 3)\n", - " - expression of **logic** or an **algorithm**\n", + "- flow control (cf., [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb))\n", + " - expression of **business logic** or an **algorithm**\n", " - conditional execution of a small **branch** within a program (i.e., `if` statements)\n", " - repetitive execution of parts of a program (i.e., `for`-loops and `while`-loops)" ] diff --git a/01_elements_review_and_exercises.ipynb b/01_elements_review_and_exercises.ipynb index 2207ccc..8940f24 100644 --- a/01_elements_review_and_exercises.ipynb +++ b/01_elements_review_and_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 1 of the book. Then work through the ten review questions." + "Read [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) of the book. Then work through the ten review questions." ] }, { @@ -275,78 +275,6 @@ "for number in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\n", " print(...)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Fizz Buzz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The kids game [Fizz Buzz](https://en.wikipedia.org/wiki/Fizz_buzz) is said to be often used in job interviews for entry level positions. However, opinions vary as to how good of a test it actually is ([source](https://news.ycombinator.com/item?id=16446774)).\n", - "\n", - "In its simplest form, a group of people start counting upwards in an alternating fashion. Whenever a number is divisible by $3$, the person must say \"Fizz\" instead of the number. The same holds for numbers divisible by $5$ when the person must say \"Buzz\". If a number is divisible by both numbers, one must say \"FizzBuzz\". Probably, this game would also make a good drinking game with the \"right\" beverages.\n", - "\n", - "With just Chapter 1, we actually do not yet know all of Python's language constructs we need to write an implementation of Fizz Buzz in a Pythonic way. Yet, we will tweak what we know a bit and make it work." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.1**: First, create a list `numbers` with the numbers from 1 through 100. You could type all numbers manually but there is of course a smarter way. The built-in [range()](https://docs.python.org/3/library/functions.html#func-range) may be useful here. Read how it works in the documentation. To make the output of [range()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "numbers = ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.2**: Loop over the `numbers` list and replace numbers for which one of the two (or both) conditions apply with text strings `\"Fizz\"`, `\"Buzz\"`, or `\"FizzBuzz\"` using the indexing operator `[...]` and the assignment statement `=`. In the chapter we saw that Python starts indexing with `0` as the first element. So in each iteration of the `for`-loop you have to determine the *index* as well as checking the actual `number`.\n", - "\n", - "Also note that for numbers divisible by both $3$ and $5$ we need some sort of a \"third\" condition check: As we only know about the `if` statement so far (and have not heard about `elif` and `else` from Chapter 3), there will be three `if` statements in total within the loop. And the order of them matters!\n", - "\n", - "Hint: Is there a single condition that checks for both $3$ and $5$?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in numbers:\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in numbers:\n", - " ..." - ] } ], "metadata": { diff --git a/02_functions.ipynb b/02_functions.ipynb index 2c284f9..681838a 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "In Chapter 1 we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", "\n", "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be re-using the same parts inside core Python every time we use them.\n", "\n", @@ -45,7 +45,7 @@ } }, "source": [ - "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** can be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from Chapter 1 in its final Pythonic version and transform it into the function `average_evens()` below. \n", + "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** can be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) in its final Pythonic version and transform it into the function `average_evens()` below. \n", "\n", "A function's **name** must be chosen according to the same naming rules as for ordinary variables. In fact, Python manages function names just like variables. In this book, we further adopt the convention of ending function names with parentheses \"`()`\" in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are not actually part of the name but must always be written out in the `def` statement for syntactic reasons.\n", "\n", @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "139655430681056" + "140693945143776" ] }, "execution_count": 3, @@ -289,7 +289,7 @@ } }, "source": [ - "Once defined we can **call** (i.e., \"execute\") a function with the **call operator** `()`. The formal parameters are filled in by passing variables or expressions as **arguments** to the function within the parentheses." + "We can **call** (i.e., \"execute\") a function with the **call operator** `()` as often as we wish. The formal parameters are filled in by passing variables or expressions as **arguments** to the function within the parentheses." ] }, { @@ -407,7 +407,7 @@ } }, "source": [ - "Notice how the parameters listed in a function's definition (i.e., `numbers`) and variables created inside it during execution (i.e., `evens` and `average`) are **local** to that function. That means they are only mapped to an object in memory while the function is being executed and de-referenced immediately when the function returns. We say they **go out of scope** once the function terminates." + "Notice how the parameters listed in a function's definition (i.e., `numbers`) and variables created inside it during execution (i.e., `evens` and `average`) are **local** to that function. That means they only point to an object in memory *while* the function is being executed and de-referenced immediately when the function returns. We say they **go out of scope** once the function terminates." ] }, { @@ -485,6 +485,17 @@ "average" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` is passed in as the `numbers` argument, there are *two* pointers to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-18). When the function returns, only the global frame is left (cf., steps 21-22)." + ] + }, { "cell_type": "markdown", "metadata": { @@ -504,7 +515,7 @@ } }, "source": [ - "On the contrary, while a function is being executed, it can \"see\" the variables of the **enclosing scope** (i.e., \"outside\" of it). This is a common source of *semantic* errors. Consider the following stylized (and incorrect) example `average_wrong()`. The error is hard to spot with eyes: The function never references the `numbers` parameter but the `nums` variable in the **global scope** instead." + "On the contrary, while a function is being executed, it can reference the variables of **enclosing scopes** (i.e., \"outside\" of it). This is a common source of *semantic* errors. Consider the following stylized (and incorrect) example `average_wrong()`. The error is hard to spot with eyes: The function never references the `numbers` parameter but the `nums` variable in the **global scope** instead." ] }, { @@ -526,11 +537,22 @@ " Returns:\n", " float: average\n", " \"\"\"\n", - " evens = [n for n in nums if n % 2 == 0] # should reference numbers, not nums\n", + " evens = [n for n in nums if n % 2 == 0] # should reference numbers not nums\n", " average = sum(evens) / len(evens)\n", " return average" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nums` in the global scope is of course unchanged." + ] + }, { "cell_type": "code", "execution_count": 16, @@ -555,6 +577,17 @@ "nums" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes a function might return a correct solution for *some* inputs ..." + ] + }, { "cell_type": "code", "execution_count": 17, @@ -576,7 +609,18 @@ } ], "source": [ - "average_wrong(nums) # the result is correct by accident!" + "average_wrong(nums) # this is correct by accident" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but still be wrong *in general*." ] }, { @@ -611,7 +655,9 @@ } }, "source": [ - "Also, observe how both `average_evens()` and `average_wrong()` use the same names for their respective parameters and variables internally. For sure, Python is smart enough to not mix them up. This is because each function call creates a temporary **[namespace](https://en.wikipedia.org/wiki/Namespace)** that *isolates* the local scope's names for usage only from within the function. As we saw in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`)." + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually points to takes *12* computational steps, namely one for setting up an empty `list` object, *ten* for filling it with elements derived from `nums` in the global scope, and one to make `evens` point at it (cf., steps 6-18).\n", + "\n", + "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside one function call but for potentially many calls occuring simultaneously as we will see in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`) and a frame is just a special kind of namespace." ] }, { @@ -633,9 +679,9 @@ } }, "source": [ - "Code gets even more confusing when variables by the same name from different scopes collide. In particular, what should we expect to happen if a function changes a globally defined variable internally?\n", + "Code gets even more confusing when variables by the *same* name from *different* scopes collide. In particular, what should we expect to happen if a function \"changes\" a globally defined variable internally?\n", "\n", - "`average_odds()` below works like `average_evens()` above except that it **[casts](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"converts\") the elements of `numbers` as objects of type `int` with the [int()](https://docs.python.org/3/library/functions.html#int) built-in first before filtering and averaging them. In doing so, it introduces an *internal* variable `nums` whose name collides with the one in the global scope. The **inequality operator** `!=` is just the **reversed** version of `==`." + "`average_odds()` below works like `average_evens()` above except that it **[casts](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"converts\") the elements of `numbers` as objects of type `int` with the [int()](https://docs.python.org/3/library/functions.html#int) built-in first before filtering and averaging them. In doing so, it introduces an *internal* variable `nums` whose name collides with the one in the global scope. To filter for odd numbers, we use the **inequality operator** `!=` that is just the **reversed** version of `==`." ] }, { @@ -652,7 +698,7 @@ " \"\"\"Calculate the average of all odd numbers in a list.\n", "\n", " Args:\n", - " numbers (list): a list of numbers; must be integers\n", + " numbers (list): a list of numbers; must be integer-like\n", "\n", " Returns:\n", " float: average\n", @@ -671,7 +717,7 @@ } }, "source": [ - "`nums` in the global scope is of course the same list from above." + "`nums` in the global scope is still unchanged." ] }, { @@ -706,7 +752,7 @@ } }, "source": [ - "As good practice, let's first use inputs for which we can calculate the answer in our heads to verify that `average_odds()` is correct." + "As good practice, let's first use inputs for which we can calculate the answer in our heads to \"verify\" that `average_odds()` is \"correct\"." ] }, { @@ -730,7 +776,7 @@ } ], "source": [ - "average_odds([1, 100, 3, 100, 5]) # verify the function's correctness with predictable inputs" + "average_odds([1.0, 10.0, 3.0, 10.0, 5.0]) # verify correctness with predictable inputs" ] }, { @@ -776,7 +822,7 @@ } }, "source": [ - "Python, however, is again smart enough to keep the two `nums` variables apart. So the global `nums` is still pointing to the very same list object as before." + "Python, however, is again smart enough to keep all the involved `nums` variables apart. So the global `nums` is still pointing to the very same `list` object as before." ] }, { @@ -811,7 +857,9 @@ } }, "source": [ - "The reason why everything works just fine is that *every time* we (re-)assign an object to a variable inside a function with the `=` statement, this is done in the local scope by default. There are ways to change variables existing in an outer scope from within a function but we save that for a later chapter.\n", + "The reason why everything just works is that *every time* we (re-)assign an object to a variable *inside* a function with the `=` statement, this is done in the *local* scope by default. There are ways to change variables existing in an outer scope from within a function but this is a rather advanced topic on its own.\n", + "\n", + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_odds%28numbers%29%3A%0A%20%20%20%20nums%20%3D%20%5Bint%28n%29%20for%20n%20in%20numbers%5D%0A%20%20%20%20odds%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20!%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28odds%29%20/%20len%28odds%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_odds%28%5B1.0,%2010.0,%203.0,%2010.0,%205.0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `nums` variables exist in *different* scopes pointing to *different* objects (cf., steps 14-25) when we execute `average_odds([1.0, 10.0, 3.0, 10.0, 5.0])`.\n", "\n", "Variables whose names collide with the ones of variables in enclosing scopes - and the global scope is just the most enclosing scope - are said to **shadow** them.\n", "\n", @@ -1122,7 +1170,7 @@ } }, "source": [ - "So far we have only specified one parameter in each of our user-defined functions. In Chapter 1, however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) taking two arguments. Obviously, the order of the numbers passed in mattered. Whenever we call a function and list its arguments in a comma seperated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." + "So far we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) taking two arguments. Obviously, the order of the numbers passed in mattered. Whenever we call a function and list its arguments in a comma seperated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." ] }, { @@ -2036,7 +2084,7 @@ { "data": { "text/plain": [ - "139655519584648" + "140694050824664" ] }, "execution_count": 58, @@ -2308,7 +2356,7 @@ "source": [ "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we can pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", "\n", - "So just as a reminder from the expression vs. statement discussion in Chapter 1: An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We can execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We can execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", "\n", "So, regarding the very next cell in particular: Although the `2 ** 2` creates a *new* object `4` in memory that is then immediately passed into the [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function, once that function call returns, \"all is lost\" and the newly created `4` object is forgotten again, as well as the return value of [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt)." ] @@ -2649,7 +2697,7 @@ { "data": { "text/plain": [ - "0.12717011866176486" + "0.15268128055183228" ] }, "execution_count": 75, @@ -2684,7 +2732,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, @@ -2733,7 +2781,7 @@ { "data": { "text/plain": [ - "8" + "2" ] }, "execution_count": 78, @@ -2881,7 +2929,7 @@ "source": [ "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We will look at it in depth in Chapter 9.\n", "\n", - "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in Chapter 0, to execute terminal commands from within a Jupyter notebook, we just need to start a code cell with an exclamation mark.\n", + "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb), to execute terminal commands from within a Jupyter notebook, we just need to start a code cell with an exclamation mark.\n", "\n", "If you are running this notebook with an installation of the [Anaconda Distribution](https://www.anaconda.com/distribution/), then [numpy](http://www.numpy.org/) is probably already installed. Running the cell below, will just confirm that." ] diff --git a/02_functions_review_and_exercises.ipynb b/02_functions_review_and_exercises.ipynb index 647e47f..dcd0a9c 100644 --- a/02_functions_review_and_exercises.ipynb +++ b/02_functions_review_and_exercises.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 2 of the book. Then work through the ten review questions." + "Read [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) of the book. Then work through the ten review questions." ] }, { diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb index acd509d..6d70f07 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "We analyzed every aspect of the `average_evens()` function in Chapter 2 except for the `if` part. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` can occur within both a **statement** as in our introductory example in Chapter 1 but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", + "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if` part. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` can occur within both a **statement** as in our introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", "\n", "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling and raising **exceptions**." ] @@ -139,7 +139,7 @@ } }, "source": [ - "There are, however, cases where even well-behaved Python does not make us happy. Chapter 5 will provide more insights on that." + "There are, however, cases where even well-behaved Python does not make us happy. Chapter 5 will provide more insights on this \"bug\"." ] }, { @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "94709180875744" + "94163040564192" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "94709180875712" + "94163040564160" ] }, "execution_count": 6, @@ -281,9 +281,9 @@ } }, "source": [ - "Let's not confuse the boolean `False` with `None`, another special built-in object! We saw the latter before in Chapter 2 as the *implicit* return value of a function without a `return` statement.\n", + "Let's not confuse the boolean `False` with `None`, another special built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) as the *implicit* return value of a function without a `return` statement.\n", "\n", - "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer. But for Python, there are no \"maybe\" or \"unknown\" objects as we will see further below!\n", + "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects as we will see further below!\n", "\n", "Whereas `False` is of type `bool`, `None` is of type `NoneType`. So, they are totally unrelated. On the contrary, as both `True` and `False` are of the same type, we could call them \"siblings\"." ] @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "94709180862704" + "94163040551152" ] }, "execution_count": 10, @@ -357,7 +357,7 @@ } }, "source": [ - "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object will never mutate its value in place (i.e., to re-use the bag analogy from Chapter 1, no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C we would have to code this singleton logic ourselves but Python has this already built in for *some* types.\n", + "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object will *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C we would have to code this singleton logic ourselves but Python has this already built in for *some* types.\n", "\n", "We can verify this with either the `is` operator or by comparing memory addresses." ] @@ -512,7 +512,7 @@ } ], "source": [ - "42 != 123 # = \"not equal to\"; other programming languages sometimes use \"<>\" instead" + "42 != 123 # = \"not equal to\"; other languages may use \"<>\"" ] }, { @@ -672,7 +672,7 @@ } }, "source": [ - "Relational operators have a *higher precedence* over logical operators (cf., the [reference](https://docs.python.org/3/reference/expressions.html#operator-precedence)). So the following expression means what we intuitively think it does." + "Relational operators have a **[higher precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence)** over logical operators. So the following expression means what we intuitively think it does." ] }, { @@ -825,7 +825,7 @@ } }, "source": [ - "For even better readability, [some practitioner](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html) suggest to never use the `>` and `>=` operators (note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) and `&&` means `and` and `||` means `or`).\n", + "For even better readability, [some practitioners](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html) suggest to *never* use the `>` and `>=` operators (note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) and `&&` means `and` and `||` means `or`).\n", "\n", "Python allows **chaining** relational operators that are combined with the `and` operator. For example, the following two cells implement the same logic where the second is a lot easier to read." ] @@ -899,7 +899,7 @@ "source": [ "The operands of the logical operators do not actually have to be *boolean* expressions as defined above but may be *any* kind of expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts the resulting object as such.\n", "\n", - "For example, any non-zero numeric object effectively becomes `True`. While this behavior allows writing more concise and thus \"beautiful\" code, it is also a common source of confusion." + "For example, any non-zero numeric object becomes `True`. While this behavior allows writing conciser and thus more \"beautiful\" code, it is also a common source of confusion." ] }, { @@ -1028,7 +1028,7 @@ } }, "source": [ - "In a boolean context `None` is casted as `False`. So, `None` is really *not* a \"maybe\" answer but a \"no\"." + "In a boolean context, `None` is casted as `False`! So, `None` is really *not* a \"maybe\" answer but a \"no\"." ] }, { @@ -1144,9 +1144,9 @@ } }, "source": [ - "In order to write useful programs, we need to control the flow of execution, for example, to react to user input.\n", + "In order to write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program does that is referred to as **business logic**.\n", "\n", - "One major language construct to do so is the **conditional statement** or `if` **statement** (cf., the [reference](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)). It consists of:\n", + "One major language construct to do so is the **[conditional statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)** or `if` statement. It consists of:\n", "\n", "- *one* mandatory `if`-clause,\n", "- an *arbitrary* number of `elif`-clauses (i.e. \"else if\"), and\n", @@ -1350,7 +1350,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "z is positive\n" + "z is odd\n" ] } ], @@ -1602,7 +1602,7 @@ "source": [ "In the previous two chapters we already encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurence of such exceptions. All we need for that is a way to formulate a condition for that.\n", "\n", - "For sure, this is such a common thing to do that Python provides its own language construct for it, namely the `try` statement (cf., the [reference](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement)).\n", + "For sure, this is such a common thing to do that Python provides its own language construct for it, namely the `try` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement).\n", "\n", "In its simplest form, it comes with just two branches: `try` and `except`. The following basically tells Python to execute the code in the `try`-branch and if *anything* goes wrong, continue in the `except`-branch instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-branch is *not* executed." ] @@ -1711,7 +1711,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Yes, division worked smoothly.\n", + "Oops. Division by 0. How does that work?\n", "I am always printed\n" ] } @@ -1748,7 +1748,7 @@ } }, "source": [ - "- **boolean expressions** evaluate either to `True` or `False`\n", + "- **boolean expressions** evaluate to either `True` or `False`\n", "- **relational operators** compare operands according to \"human\" interpretations\n", "- **logical operators** combine boolean sub-expressions to more \"complex\" expressions\n", "- the **conditional statement** is a *major* concept to **control** the **flow of execution** depending on some **conditions**\n", diff --git a/03_conditionals_review_and_exercises.ipynb b/03_conditionals_review_and_exercises.ipynb index f60bc2b..d39c551 100644 --- a/03_conditionals_review_and_exercises.ipynb +++ b/03_conditionals_review_and_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 3 of the book. Then work through the seven review questions." + "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) of the book. Then work through the seven review questions." ] }, { @@ -211,11 +211,6 @@ "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", "\n" ] }, @@ -235,28 +230,36 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "markdown", @@ -270,14 +273,18 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "markdown", @@ -304,14 +311,23 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Fizz Buzz revisited" + "### Fizz Buzz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "When you worked on the Fizz Buzz exercise in Chapter 1, you actually did not know about the `elif` and `else` keywords yet. Well, now you do." + "The kids game [Fizz Buzz](https://en.wikipedia.org/wiki/Fizz_buzz) is said to be often used in job interviews for entry level positions. However, opinions vary as to how good of a test it actually is ([source](https://news.ycombinator.com/item?id=16446774)).\n", + "\n", + "In its simplest form, a group of people start counting upwards in an alternating fashion. Whenever a number is divisible by $3$, the person must say \"Fizz\" instead of the number. The same holds for numbers divisible by $5$ when the person must say \"Buzz\". If a number is divisible by both numbers, one must say \"FizzBuzz\". Probably, this game would also make a good drinking game with the \"right\" beverages." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9.1**: First, create a list `numbers` with the numbers from 1 through 100. You could type all numbers manually but there is of course a smarter way. The built-in [range()](https://docs.python.org/3/library/functions.html#func-range) may be useful here. Read how it works in the documentation. To make the output of [range()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to wrap it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." ] }, { @@ -320,16 +336,20 @@ "metadata": {}, "outputs": [], "source": [ - "numbers = list(range(1, 101))" + "numbers = ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9**: Copy and paste your answer to **Q11.2** in Chapter 1 here and instead of three consecutive `if` statements re-write it with *one* compound `if` statement.\n", + "**Q9.2**: Loop over the `numbers` list and replace numbers for which one of the two (or both) conditions apply with text strings `\"Fizz\"`, `\"Buzz\"`, or `\"FizzBuzz\"` using the indexing operator `[]` and the assignment statement `=`.\n", "\n", - "This code will then be a lot more robust as the order of the three `if` statements cannot be screwed up." + "In Chapter 1 we saw that Python starts indexing with `0` as the first element. Keep that in mind.\n", + "\n", + "So in each iteration of the `for`-loop you have to determine an `index` variable as well as checking the actual `number` for its divisors.\n", + "\n", + "Hint: the order of the conditions is important!" ] }, { @@ -338,17 +358,31 @@ "metadata": {}, "outputs": [], "source": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n" + "for number in numbers:\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\n", + " print(...)" ] } ], diff --git a/04_iteration.ipynb b/04_iteration.ipynb new file mode 100644 index 0000000..755b4a5 --- /dev/null +++ b/04_iteration.ipynb @@ -0,0 +1,6529 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 4: Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While controlling the flow of execution with an `if` statement is definitely a must-have building block in any programming language, it alone does not allow us to run a block of code repetitively and we need to be able to do exactly that in order to write useful software.\n", + "\n", + "You might think that the `for` statement shown in some examples before is the missing piece in the puzzle. However, we can actually live without it and postpone its official introduction until the second half of this chapter.\n", + "\n", + "Instead, we dive into the big idea of **iteration** by studying the concept of **recursion** first. This is quite the opposite of many introductory books on programming that only treat the latter as a nice-to-have artifact, if at all. Yet, understanding recursion sharpens one's mind and contributes to seeing problems from a different angle." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Recursion" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A function that calls itself is **recursive** and the process of executing such a function it is called **recursion**.\n", + "\n", + "Recursive functions contain some form of a conditional check (e.g., `if` statement) to identify a **base case** that ends the recursion *when* reached. Otherwise, the function would keep calling itself forever.\n", + "\n", + "The meaning of the word *recursive* is similar to *circular*. However, a truly circular definition is not very helpful and we think of a recursive function as kind of a \"circular function with a way out at the end\"." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Trivial Example: Countdown" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A rather trivial toy example illustrates the important aspects concretely: If called with any positive integer as its `n` argument, `countdown()` just prints that number and calls itself with the *new* `n` being the *old* `n` minus `1`. This continues until `countdown()` is called with `n=0`. Then, the flow of execution hits the base case and the function calls stop." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def countdown(n):\n", + " \"\"\"Print a countdown until the party starts.\n", + "\n", + " Args:\n", + " n (int): seconds until the party begins\n", + " \"\"\"\n", + " if n == 0: # base case\n", + " print(\"Happy new Year!\")\n", + " else:\n", + " print(n)\n", + " countdown(n - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "2\n", + "1\n", + "Happy new Year!\n" + ] + } + ], + "source": [ + "countdown(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As trivial as this seems at first sight, a lot of complexity is hidden in this implementation. In particular, the order in which objects are created and de-referenced in memory might not be obvious right away as [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20countdown%28n%20-%201%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows: Each time `countdown()` is called, Python creates a *new* frame in the part of the memory where it manages all the names. This way Python *isolates* all the different `n` variables from each other. As new frames are created until we reach the base case after which the frames are destroyed in the *reversed* order, this is called a **[stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))** of frames in computer science terminology. In simple words, a stack is a last-in-first-out (LIFO) task queue. Each frame has a single parent frame, namely the one whose recursive function call created it." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Recursion in Mathematics" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Recursion plays an important role in mathematics as well and we likely know about it from some introductory course, for example, in [combinatorics](https://en.wikipedia.org/wiki/Combinatorics)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Easy Example: [Factorial](https://en.wikipedia.org/wiki/Factorial)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The factorial function, denoted with the symbol $!$, is defined as follows for all non-negative integers:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$$0! = 1$$\n", + "$$n! = n*(n-1)!$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever we can find a recursive way of formulating an idea, we can immediately translate it into Python in a *naive* way (i.e., we create a *correct* program that may *not* be an *efficient* implementation yet).\n", + "\n", + "Below is a first version of `factorial()`: The `return` statement does not have to be a function's last code line and we can certainly have several `return` statements as well." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for\n", + "\n", + " Returns:\n", + " factorial (int)\n", + " \"\"\"\n", + " if n == 0:\n", + " return 1\n", + " else:\n", + " recurse = factorial(n - 1)\n", + " result = n * recurse\n", + " return result" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we read such code, it is often easier not to follow every function call (i.e., `factorial(n - 1)` here) in one's mind but assume we receive a return value as specified in the documentation. Some call this approach **[leap of faith](http://greenteapress.com/thinkpython2/html/thinkpython2007.html#sec75)**. In fact, we practice this anyways whenever we call built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print) or [len()](https://docs.python.org/3/library/functions.html#len)) where we would have to read C code in many cases.\n", + "\n", + "To visualize *all* the computational steps of the examplary `factorial(3)`, we use [PythonTutor](http://pythontutor.com/visualize.html#code=def%20factorial%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20recurse%20%3D%20factorial%28n%20-%201%29%0A%20%20%20%20%20%20%20%20result%20%3D%20n%20*%20recurse%0A%20%20%20%20%20%20%20%20return%20result%0A%0Asolution%20%3D%20factorial%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false): The recursion again creates a stack of frames in memory. In contrast to the previous trivial example, each frame leaves a return value in memory after it is destroyed. This return value is then assigned to the `recurse` variable within the parent frame and used to compute `result`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A Pythonista would formulate `factorial()` in a conciser way using the so-called **early exit** pattern: No `else`-clause is needed as reaching a `return` statement immediately ends a function call. Furthermore, we do not really need the temporary variables `recurse` and `result`.\n", + "\n", + "As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20factorial%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20n%20*%20factorial%28n%20-%201%29%0A%0Asolution%20%3D%20factorial%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, this implementation is more efficient as it only requires 18 computational steps instead of 24 to calculate `factorial(3)`, an improvement of 25 percent! " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for\n", + "\n", + " Returns:\n", + " factorial (int)\n", + " \"\"\"\n", + " if n == 0:\n", + " return 1\n", + " return n * factorial(n - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Note that the [math](https://docs.python.org/3/library/math.html) module in the standard library provides a [factorial()](https://docs.python.org/3/library/math.html#math.factorial) function as well and we should therefore *never* implement it ourselves in a real code base." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function factorial in module math:\n", + "\n", + "factorial(x, /)\n", + " Find x!.\n", + " \n", + " Raise a ValueError if x is negative or non-integral.\n", + "\n" + ] + } + ], + "source": [ + "help(math.factorial)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Involved\" Example: [Euclid's Algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As famous philosopher Euclid already shows in his \"Elements\" (ca. 300 BC), the greatest common divisor of two integers, i.e., the largest number that divides both integers without a remainder, can be efficiently computed with the following code. This example illustrates that a recursive solution to a problem is not always easy to understand." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def gcd(a, b):\n", + " \"\"\"Calculate the greatest common divisor of two numbers.\n", + "\n", + " Args:\n", + " a (int): first number\n", + " b (int): second number\n", + "\n", + " Returns:\n", + " gcd (int)\n", + " \"\"\"\n", + " if b == 0:\n", + " return a \n", + " return gcd(b, a % b)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(12, 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Euclid's algorithm is stunningly fast, even for large numbers. Its speed comes from the use of the modulo operation `%`. However, this is *not* true for recusion in general, which can result in very slow programs if not applied correctly." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(112233445566778899, 987654321)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As expected, for two [prime numbers](https://en.wikipedia.org/wiki/List_of_prime_numbers) the greatest common divisor is of course $1$." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(2, 7919)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [math](https://docs.python.org/3/library/math.html) module in the standard library provides a [gcd()](https://docs.python.org/3/library/math.html#math.gcd) function as well and therefore we should again *never* implement it on our own." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function gcd in module math:\n", + "\n", + "gcd(x, y, /)\n", + " greatest common divisor of x and y\n", + "\n" + ] + } + ], + "source": [ + "help(math.gcd)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(12, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(112233445566778899, 987654321)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(2, 7919)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Easy at first Glance\" Example: [Fibonacci Numbers](https://en.wikipedia.org/wiki/Fibonacci_number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The Fibonacci numbers are an infinite sequence of non-negative integers that are calculated such that every number is the sum of its two predecessors where the first two numbers of the sequence are defined to be $0$ and $1$. For example, the first 13 numbers are:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's write a function `fibonacci()` that calculates the $i$th Fibonacci number where $0$ will be the $0$th number. Looking at the numbers in a **backwards** fashion (i.e., from right to left), we realize that the return value for `fibonacci(i)` can be reduced to the sum of the return values for `fibonacci(i - 1)` and `fibonacci(i - 2)` disregarding the *two* base cases." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def fibonacci(i):\n", + " \"\"\"Calculate the ith Fibonacci number.\n", + "\n", + " Args:\n", + " i (int): index of the Fibonacci number to calculate\n", + "\n", + " Returns:\n", + " ith_fibonacci (int)\n", + " \"\"\"\n", + " if i == 0:\n", + " return 0\n", + " elif i == 1:\n", + " return 1\n", + " return fibonacci(i - 1) + fibonacci(i - 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12) # = 13th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Efficiency of Algorithms" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This implementation is *highly* **inefficient** as small Fibonacci numbers can already take a very long time to compute. The reason for this is **exponential growth** in the number of function calls. As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20fibonacci%28i%29%3A%0A%20%20%20%20if%20i%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20elif%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20fibonacci%28i%20-%201%29%20%2B%20fibonacci%28i%20-%202%29%0A%0Arv%20%3D%20fibonacci%285%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, `fibonacci()` is called again and again with the same `i` arguments.\n", + "\n", + "To understand this in detail, we would have to study algorithms and data structures (e.g., with [this book](https://www.amazon.de/Introduction-Algorithms-Press-Thomas-Cormen/dp/0262033844/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1JNE8U0VZGU0O&qid=1569837169&s=gateway&sprefix=algorithms+an%2Caps%2C180&sr=8-1)), a discipline within computer science, and dive into the analysis of **[time complexity of algorithms](https://en.wikipedia.org/wiki/Time_complexity)**.\n", + "\n", + "Luckily, in the Fibonacci case the inefficiency can be resolved with a **caching** (i.e., \"re-use\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We will do so in Chapter 8 after introducing the [dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) data type.\n", + "\n", + "Let's measure the average run times for `fibonacci()` and varying `i` arguments with the `%%timeit` [cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit) that comes with Jupyter." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 5.01 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "55 µs ± 44.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit -n 100\n", + "fibonacci(12)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.63 ms ± 68 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit -n 100\n", + "fibonacci(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "192 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "fibonacci(30)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.21 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "fibonacci(35)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5.62 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "fibonacci(37)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Infinite Recursion" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If a recursion does not reach its base case, it will theoretically run forever. Luckily, Python detects that and saves the computer from crashing by raising a `RecursionError`.\n", + "\n", + "The simplest possible infinite recursion is generated like so." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def run_forever():\n", + " \"\"\"Also a pointless function should have a docstring.\"\"\"\n", + " run_forever()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mrun_forever\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"\"\"Also a pointless function should have a docstring.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "... last 1 frames repeated, from the frame below ...\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mrun_forever\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"\"\"Also a pointless function should have a docstring.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" + ] + } + ], + "source": [ + "run_forever()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, even the trivial `countdown()` function from above is not immune to an infinite recursion. Let's call it with `3.1` instead of `3`. What goes wrong here?" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.1\n", + "2.1\n", + "1.1\n", + "0.10000000000000009\n", + "-0.8999999999999999\n", + "-1.9\n", + "-2.9\n", + "-3.9\n", + "-4.9\n", + "-5.9\n", + "-6.9\n", + "-7.9\n", + "-8.9\n", + "-9.9\n", + "-10.9\n", + "-11.9\n", + "-12.9\n", + "-13.9\n", + "-14.9\n", + "-15.9\n", + "-16.9\n", + "-17.9\n", + "-18.9\n", + "-19.9\n", + "-20.9\n", + "-21.9\n", + "-22.9\n", + "-23.9\n", + "-24.9\n", + "-25.9\n", + "-26.9\n", + "-27.9\n", + "-28.9\n", + "-29.9\n", + "-30.9\n", + "-31.9\n", + "-32.9\n", + "-33.9\n", + "-34.9\n", + "-35.9\n", + "-36.9\n", + "-37.9\n", + "-38.9\n", + "-39.9\n", + "-40.9\n", + "-41.9\n", + "-42.9\n", + "-43.9\n", + "-44.9\n", + "-45.9\n", + "-46.9\n", + "-47.9\n", + "-48.9\n", + "-49.9\n", + "-50.9\n", + "-51.9\n", + "-52.9\n", + "-53.9\n", + "-54.9\n", + "-55.9\n", + "-56.9\n", + "-57.9\n", + "-58.9\n", + "-59.9\n", + "-60.9\n", + "-61.9\n", + "-62.9\n", + "-63.9\n", + "-64.9\n", + "-65.9\n", + "-66.9\n", + "-67.9\n", + "-68.9\n", + "-69.9\n", + "-70.9\n", + "-71.9\n", + "-72.9\n", + "-73.9\n", + "-74.9\n", + "-75.9\n", + "-76.9\n", + "-77.9\n", + "-78.9\n", + "-79.9\n", + "-80.9\n", + "-81.9\n", + "-82.9\n", + "-83.9\n", + "-84.9\n", + "-85.9\n", + "-86.9\n", + "-87.9\n", + "-88.9\n", + "-89.9\n", + "-90.9\n", + "-91.9\n", + "-92.9\n", + "-93.9\n", + "-94.9\n", + "-95.9\n", + "-96.9\n", + "-97.9\n", + "-98.9\n", + "-99.9\n", + "-100.9\n", + "-101.9\n", + "-102.9\n", + "-103.9\n", + "-104.9\n", + "-105.9\n", + "-106.9\n", + "-107.9\n", + "-108.9\n", + "-109.9\n", + "-110.9\n", + "-111.9\n", + "-112.9\n", + "-113.9\n", + "-114.9\n", + "-115.9\n", + "-116.9\n", + "-117.9\n", + "-118.9\n", + "-119.9\n", + "-120.9\n", + "-121.9\n", + "-122.9\n", + "-123.9\n", + "-124.9\n", + "-125.9\n", + "-126.9\n", + "-127.9\n", + "-128.9\n", + "-129.9\n", + "-130.9\n", + "-131.9\n", + "-132.9\n", + "-133.9\n", + "-134.9\n", + "-135.9\n", + "-136.9\n", + "-137.9\n", + "-138.9\n", + "-139.9\n", + "-140.9\n", + "-141.9\n", + "-142.9\n", + "-143.9\n", + "-144.9\n", + "-145.9\n", + "-146.9\n", + "-147.9\n", + "-148.9\n", + "-149.9\n", + "-150.9\n", + "-151.9\n", + "-152.9\n", + "-153.9\n", + "-154.9\n", + "-155.9\n", + "-156.9\n", + "-157.9\n", + "-158.9\n", + "-159.9\n", + "-160.9\n", + "-161.9\n", + "-162.9\n", + "-163.9\n", + "-164.9\n", + "-165.9\n", + "-166.9\n", + "-167.9\n", + "-168.9\n", + "-169.9\n", + "-170.9\n", + "-171.9\n", + "-172.9\n", + "-173.9\n", + "-174.9\n", + "-175.9\n", + "-176.9\n", + "-177.9\n", + "-178.9\n", + "-179.9\n", + "-180.9\n", + "-181.9\n", + "-182.9\n", + "-183.9\n", + "-184.9\n", + "-185.9\n", + "-186.9\n", + "-187.9\n", + "-188.9\n", + "-189.9\n", + "-190.9\n", + "-191.9\n", + "-192.9\n", + "-193.9\n", + "-194.9\n", + "-195.9\n", + "-196.9\n", + "-197.9\n", + "-198.9\n", + "-199.9\n", + "-200.9\n", + "-201.9\n", + "-202.9\n", + "-203.9\n", + "-204.9\n", + "-205.9\n", + "-206.9\n", + "-207.9\n", + "-208.9\n", + "-209.9\n", + "-210.9\n", + "-211.9\n", + "-212.9\n", + "-213.9\n", + "-214.9\n", + "-215.9\n", + "-216.9\n", + "-217.9\n", + "-218.9\n", + "-219.9\n", + "-220.9\n", + "-221.9\n", + "-222.9\n", + "-223.9\n", + "-224.9\n", + "-225.9\n", + "-226.9\n", + "-227.9\n", + "-228.9\n", + "-229.9\n", + "-230.9\n", + "-231.9\n", + "-232.9\n", + "-233.9\n", + "-234.9\n", + "-235.9\n", + "-236.9\n", + "-237.9\n", + "-238.9\n", + "-239.9\n", + "-240.9\n", + "-241.9\n", + "-242.9\n", + "-243.9\n", + "-244.9\n", + "-245.9\n", + "-246.9\n", + "-247.9\n", + "-248.9\n", + "-249.9\n", + "-250.9\n", + "-251.9\n", + "-252.9\n", + "-253.9\n", + "-254.9\n", + "-255.9\n", + "-256.9\n", + "-257.9\n", + "-258.9\n", + "-259.9\n", + "-260.9\n", + "-261.9\n", + "-262.9\n", + "-263.9\n", + "-264.9\n", + "-265.9\n", + "-266.9\n", + "-267.9\n", + "-268.9\n", + "-269.9\n", + "-270.9\n", + "-271.9\n", + "-272.9\n", + "-273.9\n", + "-274.9\n", + "-275.9\n", + "-276.9\n", + "-277.9\n", + "-278.9\n", + "-279.9\n", + "-280.9\n", + "-281.9\n", + "-282.9\n", + "-283.9\n", + "-284.9\n", + "-285.9\n", + "-286.9\n", + "-287.9\n", + "-288.9\n", + "-289.9\n", + "-290.9\n", + "-291.9\n", + "-292.9\n", + "-293.9\n", + "-294.9\n", + "-295.9\n", + "-296.9\n", + "-297.9\n", + "-298.9\n", + "-299.9\n", + "-300.9\n", + "-301.9\n", + "-302.9\n", + "-303.9\n", + "-304.9\n", + "-305.9\n", + "-306.9\n", + "-307.9\n", + "-308.9\n", + "-309.9\n", + "-310.9\n", + "-311.9\n", + "-312.9\n", + "-313.9\n", + "-314.9\n", + "-315.9\n", + "-316.9\n", + "-317.9\n", + "-318.9\n", + "-319.9\n", + "-320.9\n", + "-321.9\n", + "-322.9\n", + "-323.9\n", + "-324.9\n", + "-325.9\n", + "-326.9\n", + "-327.9\n", + "-328.9\n", + "-329.9\n", + "-330.9\n", + "-331.9\n", + "-332.9\n", + "-333.9\n", + "-334.9\n", + "-335.9\n", + "-336.9\n", + "-337.9\n", + "-338.9\n", + "-339.9\n", + "-340.9\n", + "-341.9\n", + "-342.9\n", + "-343.9\n", + "-344.9\n", + "-345.9\n", + "-346.9\n", + "-347.9\n", + "-348.9\n", + "-349.9\n", + "-350.9\n", + "-351.9\n", + "-352.9\n", + "-353.9\n", + "-354.9\n", + "-355.9\n", + "-356.9\n", + "-357.9\n", + "-358.9\n", + "-359.9\n", + "-360.9\n", + "-361.9\n", + "-362.9\n", + "-363.9\n", + "-364.9\n", + "-365.9\n", + "-366.9\n", + "-367.9\n", + "-368.9\n", + "-369.9\n", + "-370.9\n", + "-371.9\n", + "-372.9\n", + "-373.9\n", + "-374.9\n", + "-375.9\n", + "-376.9\n", + "-377.9\n", + "-378.9\n", + "-379.9\n", + "-380.9\n", + "-381.9\n", + "-382.9\n", + "-383.9\n", + "-384.9\n", + "-385.9\n", + "-386.9\n", + "-387.9\n", + "-388.9\n", + "-389.9\n", + "-390.9\n", + "-391.9\n", + "-392.9\n", + "-393.9\n", + "-394.9\n", + "-395.9\n", + "-396.9\n", + "-397.9\n", + "-398.9\n", + "-399.9\n", + "-400.9\n", + "-401.9\n", + "-402.9\n", + "-403.9\n", + "-404.9\n", + "-405.9\n", + "-406.9\n", + "-407.9\n", + "-408.9\n", + "-409.9\n", + "-410.9\n", + "-411.9\n", + "-412.9\n", + "-413.9\n", + "-414.9\n", + "-415.9\n", + "-416.9\n", + "-417.9\n", + "-418.9\n", + "-419.9\n", + "-420.9\n", + "-421.9\n", + "-422.9\n", + "-423.9\n", + "-424.9\n", + "-425.9\n", + "-426.9\n", + "-427.9\n", + "-428.9\n", + "-429.9\n", + "-430.9\n", + "-431.9\n", + "-432.9\n", + "-433.9\n", + "-434.9\n", + "-435.9\n", + "-436.9\n", + "-437.9\n", + "-438.9\n", + "-439.9\n", + "-440.9\n", + "-441.9\n", + "-442.9\n", + "-443.9\n", + "-444.9\n", + "-445.9\n", + "-446.9\n", + "-447.9\n", + "-448.9\n", + "-449.9\n", + "-450.9\n", + "-451.9\n", + "-452.9\n", + "-453.9\n", + "-454.9\n", + "-455.9\n", + "-456.9\n", + "-457.9\n", + "-458.9\n", + "-459.9\n", + "-460.9\n", + "-461.9\n", + "-462.9\n", + "-463.9\n", + "-464.9\n", + "-465.9\n", + "-466.9\n", + "-467.9\n", + "-468.9\n", + "-469.9\n", + "-470.9\n", + "-471.9\n", + "-472.9\n", + "-473.9\n", + "-474.9\n", + "-475.9\n", + "-476.9\n", + "-477.9\n", + "-478.9\n", + "-479.9\n", + "-480.9\n", + "-481.9\n", + "-482.9\n", + "-483.9\n", + "-484.9\n", + "-485.9\n", + "-486.9\n", + "-487.9\n", + "-488.9\n", + "-489.9\n", + "-490.9\n", + "-491.9\n", + "-492.9\n", + "-493.9\n", + "-494.9\n", + "-495.9\n", + "-496.9\n", + "-497.9\n", + "-498.9\n", + "-499.9\n", + "-500.9\n", + "-501.9\n", + "-502.9\n", + "-503.9\n", + "-504.9\n", + "-505.9\n", + "-506.9\n", + "-507.9\n", + "-508.9\n", + "-509.9\n", + "-510.9\n", + "-511.9\n", + "-512.9\n", + "-513.9\n", + "-514.9\n", + "-515.9\n", + "-516.9\n", + "-517.9\n", + "-518.9\n", + "-519.9\n", + "-520.9\n", + "-521.9\n", + "-522.9\n", + "-523.9\n", + "-524.9\n", + "-525.9\n", + "-526.9\n", + "-527.9\n", + "-528.9\n", + "-529.9\n", + "-530.9\n", + "-531.9\n", + "-532.9\n", + "-533.9\n", + "-534.9\n", + "-535.9\n", + "-536.9\n", + "-537.9\n", + "-538.9\n", + "-539.9\n", + "-540.9\n", + "-541.9\n", + "-542.9\n", + "-543.9\n", + "-544.9\n", + "-545.9\n", + "-546.9\n", + "-547.9\n", + "-548.9\n", + "-549.9\n", + "-550.9\n", + "-551.9\n", + "-552.9\n", + "-553.9\n", + "-554.9\n", + "-555.9\n", + "-556.9\n", + "-557.9\n", + "-558.9\n", + "-559.9\n", + "-560.9\n", + "-561.9\n", + "-562.9\n", + "-563.9\n", + "-564.9\n", + "-565.9\n", + "-566.9\n", + "-567.9\n", + "-568.9\n", + "-569.9\n", + "-570.9\n", + "-571.9\n", + "-572.9\n", + "-573.9\n", + "-574.9\n", + "-575.9\n", + "-576.9\n", + "-577.9\n", + "-578.9\n", + "-579.9\n", + "-580.9\n", + "-581.9\n", + "-582.9\n", + "-583.9\n", + "-584.9\n", + "-585.9\n", + "-586.9\n", + "-587.9\n", + "-588.9\n", + "-589.9\n", + "-590.9\n", + "-591.9\n", + "-592.9\n", + "-593.9\n", + "-594.9\n", + "-595.9\n", + "-596.9\n", + "-597.9\n", + "-598.9\n", + "-599.9\n", + "-600.9\n", + "-601.9\n", + "-602.9\n", + "-603.9\n", + "-604.9\n", + "-605.9\n", + "-606.9\n", + "-607.9\n", + "-608.9\n", + "-609.9\n", + "-610.9\n", + "-611.9\n", + "-612.9\n", + "-613.9\n", + "-614.9\n", + "-615.9\n", + "-616.9\n", + "-617.9\n", + "-618.9\n", + "-619.9\n", + "-620.9\n", + "-621.9\n", + "-622.9\n", + "-623.9\n", + "-624.9\n", + "-625.9\n", + "-626.9\n", + "-627.9\n", + "-628.9\n", + "-629.9\n", + "-630.9\n", + "-631.9\n", + "-632.9\n", + "-633.9\n", + "-634.9\n", + "-635.9\n", + "-636.9\n", + "-637.9\n", + "-638.9\n", + "-639.9\n", + "-640.9\n", + "-641.9\n", + "-642.9\n", + "-643.9\n", + "-644.9\n", + "-645.9\n", + "-646.9\n", + "-647.9\n", + "-648.9\n", + "-649.9\n", + "-650.9\n", + "-651.9\n", + "-652.9\n", + "-653.9\n", + "-654.9\n", + "-655.9\n", + "-656.9\n", + "-657.9\n", + "-658.9\n", + "-659.9\n", + "-660.9\n", + "-661.9\n", + "-662.9\n", + "-663.9\n", + "-664.9\n", + "-665.9\n", + "-666.9\n", + "-667.9\n", + "-668.9\n", + "-669.9\n", + "-670.9\n", + "-671.9\n", + "-672.9\n", + "-673.9\n", + "-674.9\n", + "-675.9\n", + "-676.9\n", + "-677.9\n", + "-678.9\n", + "-679.9\n", + "-680.9\n", + "-681.9\n", + "-682.9\n", + "-683.9\n", + "-684.9\n", + "-685.9\n", + "-686.9\n", + "-687.9\n", + "-688.9\n", + "-689.9\n", + "-690.9\n", + "-691.9\n", + "-692.9\n", + "-693.9\n", + "-694.9\n", + "-695.9\n", + "-696.9\n", + "-697.9\n", + "-698.9\n", + "-699.9\n", + "-700.9\n", + "-701.9\n", + "-702.9\n", + "-703.9\n", + "-704.9\n", + "-705.9\n", + "-706.9\n", + "-707.9\n", + "-708.9\n", + "-709.9\n", + "-710.9\n", + "-711.9\n", + "-712.9\n", + "-713.9\n", + "-714.9\n", + "-715.9\n", + "-716.9\n", + "-717.9\n", + "-718.9\n", + "-719.9\n", + "-720.9\n", + "-721.9\n", + "-722.9\n", + "-723.9\n", + "-724.9\n", + "-725.9\n", + "-726.9\n", + "-727.9\n", + "-728.9\n", + "-729.9\n", + "-730.9\n", + "-731.9\n", + "-732.9\n", + "-733.9\n", + "-734.9\n", + "-735.9\n", + "-736.9\n", + "-737.9\n", + "-738.9\n", + "-739.9\n", + "-740.9\n", + "-741.9\n", + "-742.9\n", + "-743.9\n", + "-744.9\n", + "-745.9\n", + "-746.9\n", + "-747.9\n", + "-748.9\n", + "-749.9\n", + "-750.9\n", + "-751.9\n", + "-752.9\n", + "-753.9\n", + "-754.9\n", + "-755.9\n", + "-756.9\n", + "-757.9\n", + "-758.9\n", + "-759.9\n", + "-760.9\n", + "-761.9\n", + "-762.9\n", + "-763.9\n", + "-764.9\n", + "-765.9\n", + "-766.9\n", + "-767.9\n", + "-768.9\n", + "-769.9\n", + "-770.9\n", + "-771.9\n", + "-772.9\n", + "-773.9\n", + "-774.9\n", + "-775.9\n", + "-776.9\n", + "-777.9\n", + "-778.9\n", + "-779.9\n", + "-780.9\n", + "-781.9\n", + "-782.9\n", + "-783.9\n", + "-784.9\n", + "-785.9\n", + "-786.9\n", + "-787.9\n", + "-788.9\n", + "-789.9\n", + "-790.9\n", + "-791.9\n", + "-792.9\n", + "-793.9\n", + "-794.9\n", + "-795.9\n", + "-796.9\n", + "-797.9\n", + "-798.9\n", + "-799.9\n", + "-800.9\n", + "-801.9\n", + "-802.9\n", + "-803.9\n", + "-804.9\n", + "-805.9\n", + "-806.9\n", + "-807.9\n", + "-808.9\n", + "-809.9\n", + "-810.9\n", + "-811.9\n", + "-812.9\n", + "-813.9\n", + "-814.9\n", + "-815.9\n", + "-816.9\n", + "-817.9\n", + "-818.9\n", + "-819.9\n", + "-820.9\n", + "-821.9\n", + "-822.9\n", + "-823.9\n", + "-824.9\n", + "-825.9\n", + "-826.9\n", + "-827.9\n", + "-828.9\n", + "-829.9\n", + "-830.9\n", + "-831.9\n", + "-832.9\n", + "-833.9\n", + "-834.9\n", + "-835.9\n", + "-836.9\n", + "-837.9\n", + "-838.9\n", + "-839.9\n", + "-840.9\n", + "-841.9\n", + "-842.9\n", + "-843.9\n", + "-844.9\n", + "-845.9\n", + "-846.9\n", + "-847.9\n", + "-848.9\n", + "-849.9\n", + "-850.9\n", + "-851.9\n", + "-852.9\n", + "-853.9\n", + "-854.9\n", + "-855.9\n", + "-856.9\n", + "-857.9\n", + "-858.9\n", + "-859.9\n", + "-860.9\n", + "-861.9\n", + "-862.9\n", + "-863.9\n", + "-864.9\n", + "-865.9\n", + "-866.9\n", + "-867.9\n", + "-868.9\n", + "-869.9\n", + "-870.9\n", + "-871.9\n", + "-872.9\n", + "-873.9\n", + "-874.9\n", + "-875.9\n", + "-876.9\n", + "-877.9\n", + "-878.9\n", + "-879.9\n", + "-880.9\n", + "-881.9\n", + "-882.9\n", + "-883.9\n", + "-884.9\n", + "-885.9\n", + "-886.9\n", + "-887.9\n", + "-888.9\n", + "-889.9\n", + "-890.9\n", + "-891.9\n", + "-892.9\n", + "-893.9\n", + "-894.9\n", + "-895.9\n", + "-896.9\n", + "-897.9\n", + "-898.9\n", + "-899.9\n", + "-900.9\n", + "-901.9\n", + "-902.9\n", + "-903.9\n", + "-904.9\n", + "-905.9\n", + "-906.9\n", + "-907.9\n", + "-908.9\n", + "-909.9\n", + "-910.9\n", + "-911.9\n", + "-912.9\n", + "-913.9\n", + "-914.9\n", + "-915.9\n", + "-916.9\n", + "-917.9\n", + "-918.9\n", + "-919.9\n", + "-920.9\n", + "-921.9\n", + "-922.9\n", + "-923.9\n", + "-924.9\n", + "-925.9\n", + "-926.9\n", + "-927.9\n", + "-928.9\n", + "-929.9\n", + "-930.9\n", + "-931.9\n", + "-932.9\n", + "-933.9\n", + "-934.9\n", + "-935.9\n", + "-936.9\n", + "-937.9\n", + "-938.9\n", + "-939.9\n", + "-940.9\n", + "-941.9\n", + "-942.9\n", + "-943.9\n", + "-944.9\n", + "-945.9\n", + "-946.9\n", + "-947.9\n", + "-948.9\n", + "-949.9\n", + "-950.9\n", + "-951.9\n", + "-952.9\n", + "-953.9\n", + "-954.9\n", + "-955.9\n", + "-956.9\n", + "-957.9\n", + "-958.9\n", + "-959.9\n", + "-960.9\n", + "-961.9\n", + "-962.9\n", + "-963.9\n", + "-964.9\n", + "-965.9\n", + "-966.9\n", + "-967.9\n", + "-968.9\n", + "-969.9\n", + "-970.9\n", + "-971.9\n", + "-972.9\n", + "-973.9\n", + "-974.9\n", + "-975.9\n", + "-976.9\n", + "-977.9\n", + "-978.9\n", + "-979.9\n", + "-980.9\n", + "-981.9\n", + "-982.9\n", + "-983.9\n", + "-984.9\n", + "-985.9\n", + "-986.9\n", + "-987.9\n", + "-988.9\n", + "-989.9\n", + "-990.9\n", + "-991.9\n", + "-992.9\n", + "-993.9\n", + "-994.9\n", + "-995.9\n", + "-996.9\n", + "-997.9\n", + "-998.9\n", + "-999.9\n", + "-1000.9\n", + "-1001.9\n", + "-1002.9\n", + "-1003.9\n", + "-1004.9\n", + "-1005.9\n", + "-1006.9\n", + "-1007.9\n", + "-1008.9\n", + "-1009.9\n", + "-1010.9\n", + "-1011.9\n", + "-1012.9\n", + "-1013.9\n", + "-1014.9\n", + "-1015.9\n", + "-1016.9\n", + "-1017.9\n", + "-1018.9\n", + "-1019.9\n", + "-1020.9\n", + "-1021.9\n", + "-1022.9\n", + "-1023.9\n", + "-1024.9\n", + "-1025.9\n", + "-1026.9\n", + "-1027.9\n", + "-1028.9\n", + "-1029.9\n", + "-1030.9\n", + "-1031.9\n", + "-1032.9\n", + "-1033.9\n", + "-1034.9\n", + "-1035.9\n", + "-1036.9\n", + "-1037.9\n", + "-1038.9\n", + "-1039.9\n", + "-1040.9\n", + "-1041.9\n", + "-1042.9\n", + "-1043.9\n", + "-1044.9\n", + "-1045.9\n", + "-1046.9\n", + "-1047.9\n", + "-1048.9\n", + "-1049.9\n", + "-1050.9\n", + "-1051.9\n", + "-1052.9\n", + "-1053.9\n", + "-1054.9\n", + "-1055.9\n", + "-1056.9\n", + "-1057.9\n", + "-1058.9\n", + "-1059.9\n", + "-1060.9\n", + "-1061.9\n", + "-1062.9\n", + "-1063.9\n", + "-1064.9\n", + "-1065.9\n", + "-1066.9\n", + "-1067.9\n", + "-1068.9\n", + "-1069.9\n", + "-1070.9\n", + "-1071.9\n", + "-1072.9\n", + "-1073.9\n", + "-1074.9\n", + "-1075.9\n", + "-1076.9\n", + "-1077.9\n", + "-1078.9\n", + "-1079.9\n", + "-1080.9\n", + "-1081.9\n", + "-1082.9\n", + "-1083.9\n", + "-1084.9\n", + "-1085.9\n", + "-1086.9\n", + "-1087.9\n", + "-1088.9\n", + "-1089.9\n", + "-1090.9\n", + "-1091.9\n", + "-1092.9\n", + "-1093.9\n", + "-1094.9\n", + "-1095.9\n", + "-1096.9\n", + "-1097.9\n", + "-1098.9\n", + "-1099.9\n", + "-1100.9\n", + "-1101.9\n", + "-1102.9\n", + "-1103.9\n", + "-1104.9\n", + "-1105.9\n", + "-1106.9\n", + "-1107.9\n", + "-1108.9\n", + "-1109.9\n", + "-1110.9\n", + "-1111.9\n", + "-1112.9\n", + "-1113.9\n", + "-1114.9\n", + "-1115.9\n", + "-1116.9\n", + "-1117.9\n", + "-1118.9\n", + "-1119.9\n", + "-1120.9\n", + "-1121.9\n", + "-1122.9\n", + "-1123.9\n", + "-1124.9\n", + "-1125.9\n", + "-1126.9\n", + "-1127.9\n", + "-1128.9\n", + "-1129.9\n", + "-1130.9\n", + "-1131.9\n", + "-1132.9\n", + "-1133.9\n", + "-1134.9\n", + "-1135.9\n", + "-1136.9\n", + "-1137.9\n", + "-1138.9\n", + "-1139.9\n", + "-1140.9\n", + "-1141.9\n", + "-1142.9\n", + "-1143.9\n", + "-1144.9\n", + "-1145.9\n", + "-1146.9\n", + "-1147.9\n", + "-1148.9\n", + "-1149.9\n", + "-1150.9\n", + "-1151.9\n", + "-1152.9\n", + "-1153.9\n", + "-1154.9\n", + "-1155.9\n", + "-1156.9\n", + "-1157.9\n", + "-1158.9\n", + "-1159.9\n", + "-1160.9\n", + "-1161.9\n", + "-1162.9\n", + "-1163.9\n", + "-1164.9\n", + "-1165.9\n", + "-1166.9\n", + "-1167.9\n", + "-1168.9\n", + "-1169.9\n", + "-1170.9\n", + "-1171.9\n", + "-1172.9\n", + "-1173.9\n", + "-1174.9\n", + "-1175.9\n", + "-1176.9\n", + "-1177.9\n", + "-1178.9\n", + "-1179.9\n", + "-1180.9\n", + "-1181.9\n", + "-1182.9\n", + "-1183.9\n", + "-1184.9\n", + "-1185.9\n", + "-1186.9\n", + "-1187.9\n", + "-1188.9\n", + "-1189.9\n", + "-1190.9\n", + "-1191.9\n", + "-1192.9\n", + "-1193.9\n", + "-1194.9\n", + "-1195.9\n", + "-1196.9\n", + "-1197.9\n", + "-1198.9\n", + "-1199.9\n", + "-1200.9\n", + "-1201.9\n", + "-1202.9\n", + "-1203.9\n", + "-1204.9\n", + "-1205.9\n", + "-1206.9\n", + "-1207.9\n", + "-1208.9\n", + "-1209.9\n", + "-1210.9\n", + "-1211.9\n", + "-1212.9\n", + "-1213.9\n", + "-1214.9\n", + "-1215.9\n", + "-1216.9\n", + "-1217.9\n", + "-1218.9\n", + "-1219.9\n", + "-1220.9\n", + "-1221.9\n", + "-1222.9\n", + "-1223.9\n", + "-1224.9\n", + "-1225.9\n", + "-1226.9\n", + "-1227.9\n", + "-1228.9\n", + "-1229.9\n", + "-1230.9\n", + "-1231.9\n", + "-1232.9\n", + "-1233.9\n", + "-1234.9\n", + "-1235.9\n", + "-1236.9\n", + "-1237.9\n", + "-1238.9\n", + "-1239.9\n", + "-1240.9\n", + "-1241.9\n", + "-1242.9\n", + "-1243.9\n", + "-1244.9\n", + "-1245.9\n", + "-1246.9\n", + "-1247.9\n", + "-1248.9\n", + "-1249.9\n", + "-1250.9\n", + "-1251.9\n", + "-1252.9\n", + "-1253.9\n", + "-1254.9\n", + "-1255.9\n", + "-1256.9\n", + "-1257.9\n", + "-1258.9\n", + "-1259.9\n", + "-1260.9\n", + "-1261.9\n", + "-1262.9\n", + "-1263.9\n", + "-1264.9\n", + "-1265.9\n", + "-1266.9\n", + "-1267.9\n", + "-1268.9\n", + "-1269.9\n", + "-1270.9\n", + "-1271.9\n", + "-1272.9\n", + "-1273.9\n", + "-1274.9\n", + "-1275.9\n", + "-1276.9\n", + "-1277.9\n", + "-1278.9\n", + "-1279.9\n", + "-1280.9\n", + "-1281.9\n", + "-1282.9\n", + "-1283.9\n", + "-1284.9\n", + "-1285.9\n", + "-1286.9\n", + "-1287.9\n", + "-1288.9\n", + "-1289.9\n", + "-1290.9\n", + "-1291.9\n", + "-1292.9\n", + "-1293.9\n", + "-1294.9\n", + "-1295.9\n", + "-1296.9\n", + "-1297.9\n", + "-1298.9\n", + "-1299.9\n", + "-1300.9\n", + "-1301.9\n", + "-1302.9\n", + "-1303.9\n", + "-1304.9\n", + "-1305.9\n", + "-1306.9\n", + "-1307.9\n", + "-1308.9\n", + "-1309.9\n", + "-1310.9\n", + "-1311.9\n", + "-1312.9\n", + "-1313.9\n", + "-1314.9\n", + "-1315.9\n", + "-1316.9\n", + "-1317.9\n", + "-1318.9\n", + "-1319.9\n", + "-1320.9\n", + "-1321.9\n", + "-1322.9\n", + "-1323.9\n", + "-1324.9\n", + "-1325.9\n", + "-1326.9\n", + "-1327.9\n", + "-1328.9\n", + "-1329.9\n", + "-1330.9\n", + "-1331.9\n", + "-1332.9\n", + "-1333.9\n", + "-1334.9\n", + "-1335.9\n", + "-1336.9\n", + "-1337.9\n", + "-1338.9\n", + "-1339.9\n", + "-1340.9\n", + "-1341.9\n", + "-1342.9\n", + "-1343.9\n", + "-1344.9\n", + "-1345.9\n", + "-1346.9\n", + "-1347.9\n", + "-1348.9\n", + "-1349.9\n", + "-1350.9\n", + "-1351.9\n", + "-1352.9\n", + "-1353.9\n", + "-1354.9\n", + "-1355.9\n", + "-1356.9\n", + "-1357.9\n", + "-1358.9\n", + "-1359.9\n", + "-1360.9\n", + "-1361.9\n", + "-1362.9\n", + "-1363.9\n", + "-1364.9\n", + "-1365.9\n", + "-1366.9\n", + "-1367.9\n", + "-1368.9\n", + "-1369.9\n", + "-1370.9\n", + "-1371.9\n", + "-1372.9\n", + "-1373.9\n", + "-1374.9\n", + "-1375.9\n", + "-1376.9\n", + "-1377.9\n", + "-1378.9\n", + "-1379.9\n", + "-1380.9\n", + "-1381.9\n", + "-1382.9\n", + "-1383.9\n", + "-1384.9\n", + "-1385.9\n", + "-1386.9\n", + "-1387.9\n", + "-1388.9\n", + "-1389.9\n", + "-1390.9\n", + "-1391.9\n", + "-1392.9\n", + "-1393.9\n", + "-1394.9\n", + "-1395.9\n", + "-1396.9\n", + "-1397.9\n", + "-1398.9\n", + "-1399.9\n", + "-1400.9\n", + "-1401.9\n", + "-1402.9\n", + "-1403.9\n", + "-1404.9\n", + "-1405.9\n", + "-1406.9\n", + "-1407.9\n", + "-1408.9\n", + "-1409.9\n", + "-1410.9\n", + "-1411.9\n", + "-1412.9\n", + "-1413.9\n", + "-1414.9\n", + "-1415.9\n", + "-1416.9\n", + "-1417.9\n", + "-1418.9\n", + "-1419.9\n", + "-1420.9\n", + "-1421.9\n", + "-1422.9\n", + "-1423.9\n", + "-1424.9\n", + "-1425.9\n", + "-1426.9\n", + "-1427.9\n", + "-1428.9\n", + "-1429.9\n", + "-1430.9\n", + "-1431.9\n", + "-1432.9\n", + "-1433.9\n", + "-1434.9\n", + "-1435.9\n", + "-1436.9\n", + "-1437.9\n", + "-1438.9\n", + "-1439.9\n", + "-1440.9\n", + "-1441.9\n", + "-1442.9\n", + "-1443.9\n", + "-1444.9\n", + "-1445.9\n", + "-1446.9\n", + "-1447.9\n", + "-1448.9\n", + "-1449.9\n", + "-1450.9\n", + "-1451.9\n", + "-1452.9\n", + "-1453.9\n", + "-1454.9\n", + "-1455.9\n", + "-1456.9\n", + "-1457.9\n", + "-1458.9\n", + "-1459.9\n", + "-1460.9\n", + "-1461.9\n", + "-1462.9\n", + "-1463.9\n", + "-1464.9\n", + "-1465.9\n", + "-1466.9\n", + "-1467.9\n", + "-1468.9\n", + "-1469.9\n", + "-1470.9\n", + "-1471.9\n", + "-1472.9\n", + "-1473.9\n", + "-1474.9\n", + "-1475.9\n", + "-1476.9\n", + "-1477.9\n", + "-1478.9\n", + "-1479.9\n", + "-1480.9\n", + "-1481.9\n", + "-1482.9\n", + "-1483.9\n", + "-1484.9\n", + "-1485.9\n", + "-1486.9\n", + "-1487.9\n", + "-1488.9\n", + "-1489.9\n", + "-1490.9\n", + "-1491.9\n", + "-1492.9\n", + "-1493.9\n", + "-1494.9\n", + "-1495.9\n", + "-1496.9\n", + "-1497.9\n", + "-1498.9\n", + "-1499.9\n", + "-1500.9\n", + "-1501.9\n", + "-1502.9\n", + "-1503.9\n", + "-1504.9\n", + "-1505.9\n", + "-1506.9\n", + "-1507.9\n", + "-1508.9\n", + "-1509.9\n", + "-1510.9\n", + "-1511.9\n", + "-1512.9\n", + "-1513.9\n", + "-1514.9\n", + "-1515.9\n", + "-1516.9\n", + "-1517.9\n", + "-1518.9\n", + "-1519.9\n", + "-1520.9\n", + "-1521.9\n", + "-1522.9\n", + "-1523.9\n", + "-1524.9\n", + "-1525.9\n", + "-1526.9\n", + "-1527.9\n", + "-1528.9\n", + "-1529.9\n", + "-1530.9\n", + "-1531.9\n", + "-1532.9\n", + "-1533.9\n", + "-1534.9\n", + "-1535.9\n", + "-1536.9\n", + "-1537.9\n", + "-1538.9\n", + "-1539.9\n", + "-1540.9\n", + "-1541.9\n", + "-1542.9\n", + "-1543.9\n", + "-1544.9\n", + "-1545.9\n", + "-1546.9\n", + "-1547.9\n", + "-1548.9\n", + "-1549.9\n", + "-1550.9\n", + "-1551.9\n", + "-1552.9\n", + "-1553.9\n", + "-1554.9\n", + "-1555.9\n", + "-1556.9\n", + "-1557.9\n", + "-1558.9\n", + "-1559.9\n", + "-1560.9\n", + "-1561.9\n", + "-1562.9\n", + "-1563.9\n", + "-1564.9\n", + "-1565.9\n", + "-1566.9\n", + "-1567.9\n", + "-1568.9\n", + "-1569.9\n", + "-1570.9\n", + "-1571.9\n", + "-1572.9\n", + "-1573.9\n", + "-1574.9\n", + "-1575.9\n", + "-1576.9\n", + "-1577.9\n", + "-1578.9\n", + "-1579.9\n", + "-1580.9\n", + "-1581.9\n", + "-1582.9\n", + "-1583.9\n", + "-1584.9\n", + "-1585.9\n", + "-1586.9\n", + "-1587.9\n", + "-1588.9\n", + "-1589.9\n", + "-1590.9\n", + "-1591.9\n", + "-1592.9\n", + "-1593.9\n", + "-1594.9\n", + "-1595.9\n", + "-1596.9\n", + "-1597.9\n", + "-1598.9\n", + "-1599.9\n", + "-1600.9\n", + "-1601.9\n", + "-1602.9\n", + "-1603.9\n", + "-1604.9\n", + "-1605.9\n", + "-1606.9\n", + "-1607.9\n", + "-1608.9\n", + "-1609.9\n", + "-1610.9\n", + "-1611.9\n", + "-1612.9\n", + "-1613.9\n", + "-1614.9\n", + "-1615.9\n", + "-1616.9\n", + "-1617.9\n", + "-1618.9\n", + "-1619.9\n", + "-1620.9\n", + "-1621.9\n", + "-1622.9\n", + "-1623.9\n", + "-1624.9\n", + "-1625.9\n", + "-1626.9\n", + "-1627.9\n", + "-1628.9\n", + "-1629.9\n", + "-1630.9\n", + "-1631.9\n", + "-1632.9\n", + "-1633.9\n", + "-1634.9\n", + "-1635.9\n", + "-1636.9\n", + "-1637.9\n", + "-1638.9\n", + "-1639.9\n", + "-1640.9\n", + "-1641.9\n", + "-1642.9\n", + "-1643.9\n", + "-1644.9\n", + "-1645.9\n", + "-1646.9\n", + "-1647.9\n", + "-1648.9\n", + "-1649.9\n", + "-1650.9\n", + "-1651.9\n", + "-1652.9\n", + "-1653.9\n", + "-1654.9\n", + "-1655.9\n", + "-1656.9\n", + "-1657.9\n", + "-1658.9\n", + "-1659.9\n", + "-1660.9\n", + "-1661.9\n", + "-1662.9\n", + "-1663.9\n", + "-1664.9\n", + "-1665.9\n", + "-1666.9\n", + "-1667.9\n", + "-1668.9\n", + "-1669.9\n", + "-1670.9\n", + "-1671.9\n", + "-1672.9\n", + "-1673.9\n", + "-1674.9\n", + "-1675.9\n", + "-1676.9\n", + "-1677.9\n", + "-1678.9\n", + "-1679.9\n", + "-1680.9\n", + "-1681.9\n", + "-1682.9\n", + "-1683.9\n", + "-1684.9\n", + "-1685.9\n", + "-1686.9\n", + "-1687.9\n", + "-1688.9\n", + "-1689.9\n", + "-1690.9\n", + "-1691.9\n", + "-1692.9\n", + "-1693.9\n", + "-1694.9\n", + "-1695.9\n", + "-1696.9\n", + "-1697.9\n", + "-1698.9\n", + "-1699.9\n", + "-1700.9\n", + "-1701.9\n", + "-1702.9\n", + "-1703.9\n", + "-1704.9\n", + "-1705.9\n", + "-1706.9\n", + "-1707.9\n", + "-1708.9\n", + "-1709.9\n", + "-1710.9\n", + "-1711.9\n", + "-1712.9\n", + "-1713.9\n", + "-1714.9\n", + "-1715.9\n", + "-1716.9\n", + "-1717.9\n", + "-1718.9\n", + "-1719.9\n", + "-1720.9\n", + "-1721.9\n", + "-1722.9\n", + "-1723.9\n", + "-1724.9\n", + "-1725.9\n", + "-1726.9\n", + "-1727.9\n", + "-1728.9\n", + "-1729.9\n", + "-1730.9\n", + "-1731.9\n", + "-1732.9\n", + "-1733.9\n", + "-1734.9\n", + "-1735.9\n", + "-1736.9\n", + "-1737.9\n", + "-1738.9\n", + "-1739.9\n", + "-1740.9\n", + "-1741.9\n", + "-1742.9\n", + "-1743.9\n", + "-1744.9\n", + "-1745.9\n", + "-1746.9\n", + "-1747.9\n", + "-1748.9\n", + "-1749.9\n", + "-1750.9\n", + "-1751.9\n", + "-1752.9\n", + "-1753.9\n", + "-1754.9\n", + "-1755.9\n", + "-1756.9\n", + "-1757.9\n", + "-1758.9\n", + "-1759.9\n", + "-1760.9\n", + "-1761.9\n", + "-1762.9\n", + "-1763.9\n", + "-1764.9\n", + "-1765.9\n", + "-1766.9\n", + "-1767.9\n", + "-1768.9\n", + "-1769.9\n", + "-1770.9\n", + "-1771.9\n", + "-1772.9\n", + "-1773.9\n", + "-1774.9\n", + "-1775.9\n", + "-1776.9\n", + "-1777.9\n", + "-1778.9\n", + "-1779.9\n", + "-1780.9\n", + "-1781.9\n", + "-1782.9\n", + "-1783.9\n", + "-1784.9\n", + "-1785.9\n", + "-1786.9\n", + "-1787.9\n", + "-1788.9\n", + "-1789.9\n", + "-1790.9\n", + "-1791.9\n", + "-1792.9\n", + "-1793.9\n", + "-1794.9\n", + "-1795.9\n", + "-1796.9\n", + "-1797.9\n", + "-1798.9\n", + "-1799.9\n", + "-1800.9\n", + "-1801.9\n", + "-1802.9\n", + "-1803.9\n", + "-1804.9\n", + "-1805.9\n", + "-1806.9\n", + "-1807.9\n", + "-1808.9\n", + "-1809.9\n", + "-1810.9\n", + "-1811.9\n", + "-1812.9\n", + "-1813.9\n", + "-1814.9\n", + "-1815.9\n", + "-1816.9\n", + "-1817.9\n", + "-1818.9\n", + "-1819.9\n", + "-1820.9\n", + "-1821.9\n", + "-1822.9\n", + "-1823.9\n", + "-1824.9\n", + "-1825.9\n", + "-1826.9\n", + "-1827.9\n", + "-1828.9\n", + "-1829.9\n", + "-1830.9\n", + "-1831.9\n", + "-1832.9\n", + "-1833.9\n", + "-1834.9\n", + "-1835.9\n", + "-1836.9\n", + "-1837.9\n", + "-1838.9\n", + "-1839.9\n", + "-1840.9\n", + "-1841.9\n", + "-1842.9\n", + "-1843.9\n", + "-1844.9\n", + "-1845.9\n", + "-1846.9\n", + "-1847.9\n", + "-1848.9\n", + "-1849.9\n", + "-1850.9\n", + "-1851.9\n", + "-1852.9\n", + "-1853.9\n", + "-1854.9\n", + "-1855.9\n", + "-1856.9\n", + "-1857.9\n", + "-1858.9\n", + "-1859.9\n", + "-1860.9\n", + "-1861.9\n", + "-1862.9\n", + "-1863.9\n", + "-1864.9\n", + "-1865.9\n", + "-1866.9\n", + "-1867.9\n", + "-1868.9\n", + "-1869.9\n", + "-1870.9\n", + "-1871.9\n", + "-1872.9\n", + "-1873.9\n", + "-1874.9\n", + "-1875.9\n", + "-1876.9\n", + "-1877.9\n", + "-1878.9\n", + "-1879.9\n", + "-1880.9\n", + "-1881.9\n", + "-1882.9\n", + "-1883.9\n", + "-1884.9\n", + "-1885.9\n", + "-1886.9\n", + "-1887.9\n", + "-1888.9\n", + "-1889.9\n", + "-1890.9\n", + "-1891.9\n", + "-1892.9\n", + "-1893.9\n", + "-1894.9\n", + "-1895.9\n", + "-1896.9\n", + "-1897.9\n", + "-1898.9\n", + "-1899.9\n", + "-1900.9\n", + "-1901.9\n", + "-1902.9\n", + "-1903.9\n", + "-1904.9\n", + "-1905.9\n", + "-1906.9\n", + "-1907.9\n", + "-1908.9\n", + "-1909.9\n", + "-1910.9\n", + "-1911.9\n", + "-1912.9\n", + "-1913.9\n", + "-1914.9\n", + "-1915.9\n", + "-1916.9\n", + "-1917.9\n", + "-1918.9\n", + "-1919.9\n", + "-1920.9\n", + "-1921.9\n", + "-1922.9\n", + "-1923.9\n", + "-1924.9\n", + "-1925.9\n", + "-1926.9\n", + "-1927.9\n", + "-1928.9\n", + "-1929.9\n", + "-1930.9\n", + "-1931.9\n", + "-1932.9\n", + "-1933.9\n", + "-1934.9\n", + "-1935.9\n", + "-1936.9\n", + "-1937.9\n", + "-1938.9\n", + "-1939.9\n", + "-1940.9\n", + "-1941.9\n", + "-1942.9\n", + "-1943.9\n", + "-1944.9\n", + "-1945.9\n", + "-1946.9\n", + "-1947.9\n", + "-1948.9\n", + "-1949.9\n", + "-1950.9\n", + "-1951.9\n", + "-1952.9\n", + "-1953.9\n", + "-1954.9\n", + "-1955.9\n", + "-1956.9\n", + "-1957.9\n", + "-1958.9\n", + "-1959.9\n", + "-1960.9\n", + "-1961.9\n", + "-1962.9\n", + "-1963.9\n", + "-1964.9\n", + "-1965.9\n", + "-1966.9\n", + "-1967.9\n", + "-1968.9\n", + "-1969.9\n", + "-1970.9\n", + "-1971.9\n", + "-1972.9\n", + "-1973.9\n", + "-1974.9\n", + "-1975.9\n", + "-1976.9\n", + "-1977.9\n", + "-1978.9\n", + "-1979.9\n", + "-1980.9\n", + "-1981.9\n", + "-1982.9\n", + "-1983.9\n", + "-1984.9\n", + "-1985.9\n", + "-1986.9\n", + "-1987.9\n", + "-1988.9\n", + "-1989.9\n", + "-1990.9\n", + "-1991.9\n", + "-1992.9\n", + "-1993.9\n", + "-1994.9\n", + "-1995.9\n", + "-1996.9\n", + "-1997.9\n", + "-1998.9\n", + "-1999.9\n", + "-2000.9\n", + "-2001.9\n", + "-2002.9\n", + "-2003.9\n", + "-2004.9\n", + "-2005.9\n", + "-2006.9\n", + "-2007.9\n", + "-2008.9\n", + "-2009.9\n", + "-2010.9\n", + "-2011.9\n", + "-2012.9\n", + "-2013.9\n", + "-2014.9\n", + "-2015.9\n", + "-2016.9\n", + "-2017.9\n", + "-2018.9\n", + "-2019.9\n", + "-2020.9\n", + "-2021.9\n", + "-2022.9\n", + "-2023.9\n", + "-2024.9\n", + "-2025.9\n", + "-2026.9\n", + "-2027.9\n", + "-2028.9\n", + "-2029.9\n", + "-2030.9\n", + "-2031.9\n", + "-2032.9\n", + "-2033.9\n", + "-2034.9\n", + "-2035.9\n", + "-2036.9\n", + "-2037.9\n", + "-2038.9\n", + "-2039.9\n", + "-2040.9\n", + "-2041.9\n", + "-2042.9\n", + "-2043.9\n", + "-2044.9\n", + "-2045.9\n", + "-2046.9\n", + "-2047.9\n", + "-2048.9\n", + "-2049.9\n", + "-2050.9\n", + "-2051.9\n", + "-2052.9\n", + "-2053.9\n", + "-2054.9\n", + "-2055.9\n", + "-2056.9\n", + "-2057.9\n", + "-2058.9\n", + "-2059.9\n", + "-2060.9\n", + "-2061.9\n", + "-2062.9\n", + "-2063.9\n", + "-2064.9\n", + "-2065.9\n", + "-2066.9\n", + "-2067.9\n", + "-2068.9\n", + "-2069.9\n", + "-2070.9\n", + "-2071.9\n", + "-2072.9\n", + "-2073.9\n", + "-2074.9\n", + "-2075.9\n", + "-2076.9\n", + "-2077.9\n", + "-2078.9\n", + "-2079.9\n", + "-2080.9\n", + "-2081.9\n", + "-2082.9\n", + "-2083.9\n", + "-2084.9\n", + "-2085.9\n", + "-2086.9\n", + "-2087.9\n", + "-2088.9\n", + "-2089.9\n", + "-2090.9\n", + "-2091.9\n", + "-2092.9\n", + "-2093.9\n", + "-2094.9\n", + "-2095.9\n", + "-2096.9\n", + "-2097.9\n", + "-2098.9\n", + "-2099.9\n", + "-2100.9\n", + "-2101.9\n", + "-2102.9\n", + "-2103.9\n", + "-2104.9\n", + "-2105.9\n", + "-2106.9\n", + "-2107.9\n", + "-2108.9\n", + "-2109.9\n", + "-2110.9\n", + "-2111.9\n", + "-2112.9\n", + "-2113.9\n", + "-2114.9\n", + "-2115.9\n", + "-2116.9\n", + "-2117.9\n", + "-2118.9\n", + "-2119.9\n", + "-2120.9\n", + "-2121.9\n", + "-2122.9\n", + "-2123.9\n", + "-2124.9\n", + "-2125.9\n", + "-2126.9\n", + "-2127.9\n", + "-2128.9\n", + "-2129.9\n", + "-2130.9\n", + "-2131.9\n", + "-2132.9\n", + "-2133.9\n", + "-2134.9\n", + "-2135.9\n", + "-2136.9\n", + "-2137.9\n", + "-2138.9\n", + "-2139.9\n", + "-2140.9\n", + "-2141.9\n", + "-2142.9\n", + "-2143.9\n", + "-2144.9\n", + "-2145.9\n", + "-2146.9\n", + "-2147.9\n", + "-2148.9\n", + "-2149.9\n", + "-2150.9\n", + "-2151.9\n", + "-2152.9\n", + "-2153.9\n", + "-2154.9\n", + "-2155.9\n", + "-2156.9\n", + "-2157.9\n", + "-2158.9\n", + "-2159.9\n", + "-2160.9\n", + "-2161.9\n", + "-2162.9\n", + "-2163.9\n", + "-2164.9\n", + "-2165.9\n", + "-2166.9\n", + "-2167.9\n", + "-2168.9\n", + "-2169.9\n", + "-2170.9\n", + "-2171.9\n", + "-2172.9\n", + "-2173.9\n", + "-2174.9\n", + "-2175.9\n", + "-2176.9\n", + "-2177.9\n", + "-2178.9\n", + "-2179.9\n", + "-2180.9\n", + "-2181.9\n", + "-2182.9\n", + "-2183.9\n", + "-2184.9\n", + "-2185.9\n", + "-2186.9\n", + "-2187.9\n", + "-2188.9\n", + "-2189.9\n", + "-2190.9\n", + "-2191.9\n", + "-2192.9\n", + "-2193.9\n", + "-2194.9\n", + "-2195.9\n", + "-2196.9\n", + "-2197.9\n", + "-2198.9\n", + "-2199.9\n", + "-2200.9\n", + "-2201.9\n", + "-2202.9\n", + "-2203.9\n", + "-2204.9\n", + "-2205.9\n", + "-2206.9\n", + "-2207.9\n", + "-2208.9\n", + "-2209.9\n", + "-2210.9\n", + "-2211.9\n", + "-2212.9\n", + "-2213.9\n", + "-2214.9\n", + "-2215.9\n", + "-2216.9\n", + "-2217.9\n", + "-2218.9\n", + "-2219.9\n", + "-2220.9\n", + "-2221.9\n", + "-2222.9\n", + "-2223.9\n", + "-2224.9\n", + "-2225.9\n", + "-2226.9\n", + "-2227.9\n", + "-2228.9\n", + "-2229.9\n", + "-2230.9\n", + "-2231.9\n", + "-2232.9\n", + "-2233.9\n", + "-2234.9\n", + "-2235.9\n", + "-2236.9\n", + "-2237.9\n", + "-2238.9\n", + "-2239.9\n", + "-2240.9\n", + "-2241.9\n", + "-2242.9\n", + "-2243.9\n", + "-2244.9\n", + "-2245.9\n", + "-2246.9\n", + "-2247.9\n", + "-2248.9\n", + "-2249.9\n", + "-2250.9\n", + "-2251.9\n", + "-2252.9\n", + "-2253.9\n", + "-2254.9\n", + "-2255.9\n", + "-2256.9\n", + "-2257.9\n", + "-2258.9\n", + "-2259.9\n", + "-2260.9\n", + "-2261.9\n", + "-2262.9\n", + "-2263.9\n", + "-2264.9\n", + "-2265.9\n", + "-2266.9\n", + "-2267.9\n", + "-2268.9\n", + "-2269.9\n", + "-2270.9\n", + "-2271.9\n", + "-2272.9\n", + "-2273.9\n", + "-2274.9\n", + "-2275.9\n", + "-2276.9\n", + "-2277.9\n", + "-2278.9\n", + "-2279.9\n", + "-2280.9\n", + "-2281.9\n", + "-2282.9\n", + "-2283.9\n", + "-2284.9\n", + "-2285.9\n", + "-2286.9\n", + "-2287.9\n", + "-2288.9\n", + "-2289.9\n", + "-2290.9\n", + "-2291.9\n", + "-2292.9\n", + "-2293.9\n", + "-2294.9\n", + "-2295.9\n", + "-2296.9\n", + "-2297.9\n", + "-2298.9\n", + "-2299.9\n", + "-2300.9\n", + "-2301.9\n", + "-2302.9\n", + "-2303.9\n", + "-2304.9\n", + "-2305.9\n", + "-2306.9\n", + "-2307.9\n", + "-2308.9\n", + "-2309.9\n", + "-2310.9\n", + "-2311.9\n", + "-2312.9\n", + "-2313.9\n", + "-2314.9\n", + "-2315.9\n", + "-2316.9\n", + "-2317.9\n", + "-2318.9\n", + "-2319.9\n", + "-2320.9\n", + "-2321.9\n", + "-2322.9\n", + "-2323.9\n", + "-2324.9\n", + "-2325.9\n", + "-2326.9\n", + "-2327.9\n", + "-2328.9\n", + "-2329.9\n", + "-2330.9\n", + "-2331.9\n", + "-2332.9\n", + "-2333.9\n", + "-2334.9\n", + "-2335.9\n", + "-2336.9\n", + "-2337.9\n", + "-2338.9\n", + "-2339.9\n", + "-2340.9\n", + "-2341.9\n", + "-2342.9\n", + "-2343.9\n", + "-2344.9\n", + "-2345.9\n", + "-2346.9\n", + "-2347.9\n", + "-2348.9\n", + "-2349.9\n", + "-2350.9\n", + "-2351.9\n", + "-2352.9\n", + "-2353.9\n", + "-2354.9\n", + "-2355.9\n", + "-2356.9\n", + "-2357.9\n", + "-2358.9\n", + "-2359.9\n", + "-2360.9\n", + "-2361.9\n", + "-2362.9\n", + "-2363.9\n", + "-2364.9\n", + "-2365.9\n", + "-2366.9\n", + "-2367.9\n", + "-2368.9\n", + "-2369.9\n", + "-2370.9\n", + "-2371.9\n", + "-2372.9\n", + "-2373.9\n", + "-2374.9\n", + "-2375.9\n", + "-2376.9\n", + "-2377.9\n", + "-2378.9\n", + "-2379.9\n", + "-2380.9\n", + "-2381.9\n", + "-2382.9\n", + "-2383.9\n", + "-2384.9\n", + "-2385.9\n", + "-2386.9\n", + "-2387.9\n", + "-2388.9\n", + "-2389.9\n", + "-2390.9\n", + "-2391.9\n", + "-2392.9\n", + "-2393.9\n", + "-2394.9\n", + "-2395.9\n", + "-2396.9\n", + "-2397.9\n", + "-2398.9\n", + "-2399.9\n", + "-2400.9\n", + "-2401.9\n", + "-2402.9\n", + "-2403.9\n", + "-2404.9\n", + "-2405.9\n", + "-2406.9\n", + "-2407.9\n", + "-2408.9\n", + "-2409.9\n", + "-2410.9\n", + "-2411.9\n", + "-2412.9\n", + "-2413.9\n", + "-2414.9\n", + "-2415.9\n", + "-2416.9\n", + "-2417.9\n", + "-2418.9\n", + "-2419.9\n", + "-2420.9\n", + "-2421.9\n", + "-2422.9\n", + "-2423.9\n", + "-2424.9\n", + "-2425.9\n", + "-2426.9\n", + "-2427.9\n", + "-2428.9\n", + "-2429.9\n", + "-2430.9\n", + "-2431.9\n", + "-2432.9\n", + "-2433.9\n", + "-2434.9\n", + "-2435.9\n", + "-2436.9\n", + "-2437.9\n", + "-2438.9\n", + "-2439.9\n", + "-2440.9\n", + "-2441.9\n", + "-2442.9\n", + "-2443.9\n", + "-2444.9\n", + "-2445.9\n", + "-2446.9\n", + "-2447.9\n", + "-2448.9\n", + "-2449.9\n", + "-2450.9\n", + "-2451.9\n", + "-2452.9\n", + "-2453.9\n", + "-2454.9\n", + "-2455.9\n", + "-2456.9\n", + "-2457.9\n", + "-2458.9\n", + "-2459.9\n", + "-2460.9\n", + "-2461.9\n", + "-2462.9\n", + "-2463.9\n", + "-2464.9\n", + "-2465.9\n", + "-2466.9\n", + "-2467.9\n", + "-2468.9\n", + "-2469.9\n", + "-2470.9\n", + "-2471.9\n", + "-2472.9\n", + "-2473.9\n", + "-2474.9\n", + "-2475.9\n", + "-2476.9\n", + "-2477.9\n", + "-2478.9\n", + "-2479.9\n", + "-2480.9\n", + "-2481.9\n", + "-2482.9\n", + "-2483.9\n", + "-2484.9\n", + "-2485.9\n", + "-2486.9\n", + "-2487.9\n", + "-2488.9\n", + "-2489.9\n", + "-2490.9\n", + "-2491.9\n", + "-2492.9\n", + "-2493.9\n", + "-2494.9\n", + "-2495.9\n", + "-2496.9\n", + "-2497.9\n", + "-2498.9\n", + "-2499.9\n", + "-2500.9\n", + "-2501.9\n", + "-2502.9\n", + "-2503.9\n", + "-2504.9\n", + "-2505.9\n", + "-2506.9\n", + "-2507.9\n", + "-2508.9\n", + "-2509.9\n", + "-2510.9\n", + "-2511.9\n", + "-2512.9\n", + "-2513.9\n", + "-2514.9\n", + "-2515.9\n", + "-2516.9\n", + "-2517.9\n", + "-2518.9\n", + "-2519.9\n", + "-2520.9\n", + "-2521.9\n", + "-2522.9\n", + "-2523.9\n", + "-2524.9\n", + "-2525.9\n", + "-2526.9\n", + "-2527.9\n", + "-2528.9\n", + "-2529.9\n", + "-2530.9\n", + "-2531.9\n", + "-2532.9\n", + "-2533.9\n", + "-2534.9\n", + "-2535.9\n", + "-2536.9\n", + "-2537.9\n", + "-2538.9\n", + "-2539.9\n", + "-2540.9\n", + "-2541.9\n", + "-2542.9\n", + "-2543.9\n", + "-2544.9\n", + "-2545.9\n", + "-2546.9\n", + "-2547.9\n", + "-2548.9\n", + "-2549.9\n", + "-2550.9\n", + "-2551.9\n", + "-2552.9\n", + "-2553.9\n", + "-2554.9\n", + "-2555.9\n", + "-2556.9\n", + "-2557.9\n", + "-2558.9\n", + "-2559.9\n", + "-2560.9\n", + "-2561.9\n", + "-2562.9\n", + "-2563.9\n", + "-2564.9\n", + "-2565.9\n", + "-2566.9\n", + "-2567.9\n", + "-2568.9\n", + "-2569.9\n", + "-2570.9\n", + "-2571.9\n", + "-2572.9\n", + "-2573.9\n", + "-2574.9\n", + "-2575.9\n", + "-2576.9\n", + "-2577.9\n", + "-2578.9\n", + "-2579.9\n", + "-2580.9\n", + "-2581.9\n", + "-2582.9\n", + "-2583.9\n", + "-2584.9\n", + "-2585.9\n", + "-2586.9\n", + "-2587.9\n", + "-2588.9\n", + "-2589.9\n", + "-2590.9\n", + "-2591.9\n", + "-2592.9\n", + "-2593.9\n", + "-2594.9\n", + "-2595.9\n", + "-2596.9\n", + "-2597.9\n", + "-2598.9\n", + "-2599.9\n", + "-2600.9\n", + "-2601.9\n", + "-2602.9\n", + "-2603.9\n", + "-2604.9\n", + "-2605.9\n", + "-2606.9\n", + "-2607.9\n", + "-2608.9\n", + "-2609.9\n", + "-2610.9\n", + "-2611.9\n", + "-2612.9\n", + "-2613.9\n", + "-2614.9\n", + "-2615.9\n", + "-2616.9\n", + "-2617.9\n", + "-2618.9\n", + "-2619.9\n", + "-2620.9\n", + "-2621.9\n", + "-2622.9\n", + "-2623.9\n", + "-2624.9\n", + "-2625.9\n", + "-2626.9\n", + "-2627.9\n", + "-2628.9\n", + "-2629.9\n", + "-2630.9\n", + "-2631.9\n", + "-2632.9\n", + "-2633.9\n", + "-2634.9\n", + "-2635.9\n", + "-2636.9\n", + "-2637.9\n", + "-2638.9\n", + "-2639.9\n", + "-2640.9\n", + "-2641.9\n", + "-2642.9\n", + "-2643.9\n", + "-2644.9\n", + "-2645.9\n", + "-2646.9\n", + "-2647.9\n", + "-2648.9\n", + "-2649.9\n", + "-2650.9\n", + "-2651.9\n", + "-2652.9\n", + "-2653.9\n", + "-2654.9\n", + "-2655.9\n", + "-2656.9\n", + "-2657.9\n", + "-2658.9\n", + "-2659.9\n", + "-2660.9\n", + "-2661.9\n", + "-2662.9\n", + "-2663.9\n", + "-2664.9\n", + "-2665.9\n", + "-2666.9\n", + "-2667.9\n", + "-2668.9\n", + "-2669.9\n", + "-2670.9\n", + "-2671.9\n", + "-2672.9\n", + "-2673.9\n", + "-2674.9\n", + "-2675.9\n", + "-2676.9\n", + "-2677.9\n", + "-2678.9\n", + "-2679.9\n", + "-2680.9\n", + "-2681.9\n", + "-2682.9\n", + "-2683.9\n", + "-2684.9\n", + "-2685.9\n", + "-2686.9\n", + "-2687.9\n", + "-2688.9\n", + "-2689.9\n", + "-2690.9\n", + "-2691.9\n", + "-2692.9\n", + "-2693.9\n", + "-2694.9\n", + "-2695.9\n", + "-2696.9\n", + "-2697.9\n", + "-2698.9\n", + "-2699.9\n", + "-2700.9\n", + "-2701.9\n", + "-2702.9\n", + "-2703.9\n", + "-2704.9\n", + "-2705.9\n", + "-2706.9\n", + "-2707.9\n", + "-2708.9\n", + "-2709.9\n", + "-2710.9\n", + "-2711.9\n", + "-2712.9\n", + "-2713.9\n", + "-2714.9\n", + "-2715.9\n", + "-2716.9\n", + "-2717.9\n", + "-2718.9\n", + "-2719.9\n", + "-2720.9\n", + "-2721.9\n", + "-2722.9\n", + "-2723.9\n", + "-2724.9\n", + "-2725.9\n", + "-2726.9\n", + "-2727.9\n", + "-2728.9\n", + "-2729.9\n", + "-2730.9\n", + "-2731.9\n", + "-2732.9\n", + "-2733.9\n", + "-2734.9\n", + "-2735.9\n", + "-2736.9\n", + "-2737.9\n", + "-2738.9\n", + "-2739.9\n", + "-2740.9\n", + "-2741.9\n", + "-2742.9\n", + "-2743.9\n", + "-2744.9\n", + "-2745.9\n", + "-2746.9\n", + "-2747.9\n", + "-2748.9\n", + "-2749.9\n", + "-2750.9\n", + "-2751.9\n", + "-2752.9\n", + "-2753.9\n", + "-2754.9\n", + "-2755.9\n", + "-2756.9\n", + "-2757.9\n", + "-2758.9\n", + "-2759.9\n", + "-2760.9\n", + "-2761.9\n", + "-2762.9\n", + "-2763.9\n", + "-2764.9\n", + "-2765.9\n", + "-2766.9\n", + "-2767.9\n", + "-2768.9\n", + "-2769.9\n", + "-2770.9\n", + "-2771.9\n", + "-2772.9\n", + "-2773.9\n", + "-2774.9\n", + "-2775.9\n", + "-2776.9\n", + "-2777.9\n", + "-2778.9\n", + "-2779.9\n", + "-2780.9\n", + "-2781.9\n", + "-2782.9\n", + "-2783.9\n", + "-2784.9\n", + "-2785.9\n", + "-2786.9\n", + "-2787.9\n", + "-2788.9\n", + "-2789.9\n", + "-2790.9\n", + "-2791.9\n", + "-2792.9\n", + "-2793.9\n", + "-2794.9\n", + "-2795.9\n", + "-2796.9\n", + "-2797.9\n", + "-2798.9\n", + "-2799.9\n", + "-2800.9\n", + "-2801.9\n", + "-2802.9\n", + "-2803.9\n", + "-2804.9\n", + "-2805.9\n", + "-2806.9\n", + "-2807.9\n", + "-2808.9\n", + "-2809.9\n", + "-2810.9\n", + "-2811.9\n", + "-2812.9\n", + "-2813.9\n", + "-2814.9\n", + "-2815.9\n", + "-2816.9\n", + "-2817.9\n", + "-2818.9\n", + "-2819.9\n", + "-2820.9\n", + "-2821.9\n", + "-2822.9\n", + "-2823.9\n", + "-2824.9\n", + "-2825.9\n", + "-2826.9\n", + "-2827.9\n", + "-2828.9\n", + "-2829.9\n", + "-2830.9\n", + "-2831.9\n", + "-2832.9\n", + "-2833.9\n", + "-2834.9\n", + "-2835.9\n", + "-2836.9\n", + "-2837.9\n", + "-2838.9\n", + "-2839.9\n", + "-2840.9\n", + "-2841.9\n", + "-2842.9\n", + "-2843.9\n", + "-2844.9\n", + "-2845.9\n", + "-2846.9\n", + "-2847.9\n", + "-2848.9\n", + "-2849.9\n", + "-2850.9\n", + "-2851.9\n", + "-2852.9\n", + "-2853.9\n", + "-2854.9\n", + "-2855.9\n", + "-2856.9\n", + "-2857.9\n", + "-2858.9\n", + "-2859.9\n", + "-2860.9\n", + "-2861.9\n", + "-2862.9\n", + "-2863.9\n", + "-2864.9\n", + "-2865.9\n", + "-2866.9\n", + "-2867.9\n", + "-2868.9\n", + "-2869.9\n", + "-2870.9\n", + "-2871.9\n", + "-2872.9\n", + "-2873.9\n", + "-2874.9\n", + "-2875.9\n", + "-2876.9\n", + "-2877.9\n", + "-2878.9\n", + "-2879.9\n", + "-2880.9\n", + "-2881.9\n", + "-2882.9\n", + "-2883.9\n", + "-2884.9\n", + "-2885.9\n", + "-2886.9\n", + "-2887.9\n", + "-2888.9\n", + "-2889.9\n", + "-2890.9\n", + "-2891.9\n", + "-2892.9\n", + "-2893.9\n", + "-2894.9\n", + "-2895.9\n", + "-2896.9\n", + "-2897.9\n", + "-2898.9\n", + "-2899.9\n", + "-2900.9\n", + "-2901.9\n", + "-2902.9\n", + "-2903.9\n", + "-2904.9\n", + "-2905.9\n", + "-2906.9\n", + "-2907.9\n", + "-2908.9\n", + "-2909.9\n", + "-2910.9\n", + "-2911.9\n", + "-2912.9\n", + "-2913.9\n", + "-2914.9\n", + "-2915.9\n", + "-2916.9\n", + "-2917.9\n", + "-2918.9\n", + "-2919.9\n", + "-2920.9\n", + "-2921.9\n", + "-2922.9\n", + "-2923.9\n", + "-2924.9\n", + "-2925.9\n", + "-2926.9\n", + "-2927.9\n", + "-2928.9\n", + "-2929.9\n", + "-2930.9\n", + "-2931.9\n", + "-2932.9\n", + "-2933.9\n", + "-2934.9\n", + "-2935.9\n", + "-2936.9\n", + "-2937.9\n", + "-2938.9\n", + "-2939.9\n", + "-2940.9\n", + "-2941.9\n", + "-2942.9\n", + "-2943.9\n", + "-2944.9\n", + "-2945.9\n", + "-2946.9\n", + "-2947.9\n", + "-2948.9\n", + "-2949.9\n" + ] + }, + { + "ename": "RecursionError", + "evalue": "maximum recursion depth exceeded while calling a Python object", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "... last 1 frames repeated, from the frame below ...\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded while calling a Python object" + ] + } + ], + "source": [ + "countdown(3.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the same way, a `RecursionError` occurs if we call `factorial()` with `3.1` instead of `3`." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "... last 1 frames repeated, from the frame below ...\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded in comparison" + ] + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The infinite recursions could easily be avoided by replacing `n == 0` with `n <= 0` in both functions and thereby **generalizing** them. But even then, calling either `countdown()` or `factorial()` with a non-integer number is *semantically* wrong and therefore we better leave the base cases unchanged.\n", + "\n", + "Errors as above are a symptom of missing **type checking**: By design, Python allows us to pass in not only integers but objects of any type as arguments to the `countdown()` and `factorial()` functions. As long as the arguments \"behave\" like integers, we will not encounter any *runtime* errors. This is the case here as the two example functions only use the `-` and `*` operators internally and in this context a `float` object behaves exactly like an `int` object. So, the functions keep calling themselves until Python decides with a built-in heuristic that the recursion is likely not going to end and aborts the computations with a `RecursionError`. Stricly speaking, this is of course a *runtime* error as well. The missing type checking is 100% intentional and considered a feature of rather than a bug in Python.\n", + "\n", + "Pythonistas often use the term **[duck typing](https://en.wikipedia.org/wiki/Duck_typing)** when refering to the same idea and the colloquial saying goes \"If it walks like a duck and it quacks like a duck, it must be a duck\". For example, we could call `factorial()` with the `float` object `3.0` and the recursion works out fine. So, as long as the `3.0` \"walks\" like a `3` and \"quacks\" like a `3`, it \"must be\" a `3`.\n", + "\n", + "We see a similar behavior when we mix objects of types `int` and `float` with arithmetic operators. For example, `1 + 2.0` works because Python implicitly views the `1` as a `1.0` at runtime and then knows how to do floating-point arithmetic: Here, the `int` \"walks\" and \"quacks\" like a `float`. Strictly speaking, this is yet another example of operator overloading whereas duck typing refers to the same behavior when passing arguments to function calls.\n", + "\n", + "The important lesson is that we must expect our functions to be called with objects of *any* type at runtime, as opposed to the one type we had in mind when we defined the function.\n", + "\n", + "Duck typing is possible because Python is a dynamically typed language. On the contrary, in statically typed languages like C we have to declare (i.e., \"specify\") the data type of every parameter in a function definition. Then, a `RecursionError` as for `countdown(3.1)` or `factorial(3.1)` above could not occur. For example, if we declared the `countdown()` and `factorial()` functions to only accept `int` objects, calling the functions with a `float` argument would immediately fail *syntactically*. As a downside, we would then lose the ability to call `factorial()` with `3.0`, which is *semantically* correct nevertheless.\n", + "\n", + "So, there is no black or white answer as to which of the two language designs is better. Yet, most professional programmers have very strong opinions with respect to duck typing reaching from \"love\" to \"hate\". This is another example as to how programming is a subjective art and not an \"objective\" science. Probably, Python is regarded more beginner friendly as `3` and `3.0` should intuitively be interchangeable." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Type Checking & Input Validation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can use the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function to make sure `factorial()` is called with an `int` object passed in. We further **validate the input** by verifying that the integer is non-negative.\n", + "\n", + "Meanwhile, we also see how we can manually raise exceptions with the `raise` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement), another way of controlling the flow of execution.\n", + "\n", + "The first two branches in the revised `factorial()` function act as **guardians** ensuring that the code does not produce *unexpected* runtime errors: Errors can certainly be expected when mentioned in the docstring.\n", + "\n", + "Forcing `n` to be an `int` is a very puritan way of handling the issues discussed above. A more relaxed approach could be to also accept a `float` and use its [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method to check if `n` could be casted as an `int`. After all, by being too puritan we can not take advantage of duck typing.\n", + "\n", + "So in essence, we are doing *two* things here. Besides checking for the correct type, we are also enforcing **domain-specific** (i.e., mathematical) rules with respect to the non-negativity of `n`." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for; must be positive\n", + "\n", + " Returns:\n", + " factorial (int)\n", + "\n", + " Raises:\n", + " TypeError: if n is not an integer\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"Factorial is only defined for integers\")\n", + " elif n < 0:\n", + " raise ValueError(\"Factorial is not defined for negative integers\")\n", + " elif n == 0:\n", + " return 1\n", + " return n * factorial(n - 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The revised `factorial()` function works like the old one." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead of running into an infinite recursion, we now receive very specifc error messages." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Factorial is only defined for integers", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[1;32m 14\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" + ] + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Factorial is not defined for negative integers", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: Factorial is not defined for negative integers" + ] + } + ], + "source": [ + "factorial(-42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Theory of Computation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With everything *officially* introduced so far (i.e., without the introductary example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) that only served as an overview), Python would be what is called **[Turing complete](https://en.wikipedia.org/wiki/Turing_completeness)**. That means that anything that could be formulated as an algorithm could be expressed with all the language features we have seen. Note that, in particular, we have *not* yet formally *introduced* the `for` and `while` statements!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Looping" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `while` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas functions combined with `if` statements suffice to model any type of iteration with a recursion, Python comes with a `while` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) that often makes it easier to implement iterative ideas.\n", + "\n", + "It consists of a header line with a boolean expression followed by an indented code block. Before the first and after every execution of the code block, the boolean expression is evaluated and if it is (still) equal to `True`, the code block runs (again). Eventually, some variable referenced in the boolean expression is changed in the code block such that the condition becomes `False`.\n", + "\n", + "If the condition is `False` before the first iteration, the entire code block is *never* executed. As the flow of control keeps **looping** (or **iterating**) back to the beginning of the code block, this concept is also called a `while`-loop and each pass through the loop an **iteration**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Trivial Example: Countdown (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's rewrite the `countdown()` example in an iterative style. We also build in **input validation** by allowing the function to only be called with strictly positive integers. As any positive integer will hit $0$ at some point in time when iteratively decremented by $1$, `countdown()` is guaranteed to **terminate**. Note also that the base case is now handled at the end of the function, which commonly happens with iterative solutions to problems." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def countdown(n):\n", + " \"\"\"Print a countdown until the party starts.\n", + "\n", + " Args:\n", + " n (int): seconds until the party begins; must be positive\n", + "\n", + " Raises:\n", + " TypeError: if n is not of an integer\n", + " ValueError: if n is not positive\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"Can only count down with whole numbers\")\n", + " elif n <= 0:\n", + " raise ValueError(\"n must be stricly positive\")\n", + "\n", + " while n != 0:\n", + " print(n)\n", + " n -= 1\n", + "\n", + " print(\"Happy new Year!\") # = base case" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "2\n", + "1\n", + "Happy new Year!\n" + ] + } + ], + "source": [ + "countdown(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20not%20isinstance%28n,%20int%29%3A%0A%20%20%20%20%20%20%20%20raise%20TypeError%28%22...%22%29%0A%20%20%20%20elif%20n%20%3C%3D%200%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22...%22%29%0A%0A%20%20%20%20while%20n%20!%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20n%20-%3D%201%0A%0A%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, there is a subtle but important difference in the way a `while` statement is treated in memory: In short, `while` statements can *not* run into a `RecursionError` as only *one* frame is needed to manage the names. After all, there is only *one* function call to be made. For common day-to-day applications this difference is, however, not important." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Still involved\" Example: [Euclid's Algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Finding the greatest common divisor of two numbers is still not so obvious when using a `while`-loop instead of a recursion.\n", + "\n", + "The iterative implementation of `gcd()` below accepts any two strictly positive integers. As in any iteration through the loop the smaller number is subtracted from the larger one, the two decremented values of `a` and `b` will eventually be equal. Thus, this algorithm is also guaranteed to terminate. If one of the two numbers were negative or $0$ to begin with, `gcd()` would run forever and not even Python could detect this. Try this out by removing the input validation and running the function with negative arguments!" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def gcd(a, b):\n", + " \"\"\"Calculate the greatest common divisor of two numbers.\n", + "\n", + " Args:\n", + " a (int): first number; must be positive\n", + " b (int): second number; must be positive\n", + "\n", + " Returns:\n", + " gcd (int)\n", + "\n", + " Raises:\n", + " TypeError: if a or b are not of an integer type\n", + " ValueError: if a or b are not positive\n", + " \"\"\"\n", + " if not isinstance(a, int) or not isinstance(b, int):\n", + " raise TypeError(\"Greatest common divisor is only defined for two integers\")\n", + " elif a <= 0 or b <= 0:\n", + " raise ValueError(\"a and b must be strictly positive\")\n", + "\n", + " while a != b:\n", + " if a > b:\n", + " a -= b\n", + " else:\n", + " b -= a\n", + "\n", + " return a" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(12, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(2, 7919)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Efficiency of Algorithms (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can also see that this implementation is a lot *less* efficient than its recursive counterpart which solves `gcd()` for the same two numbers $112233445566778899$ and $987654321$ within microseconds." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4.76 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "gcd(112233445566778899, 987654321)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Infinite Loops" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with recursion, we must ensure that the iteration ends. For the above `countdown()` and `gcd()` examples we could \"prove\" (i.e., at least argue in favor) that some pre-defined **termination criterion** will be reached eventually. However, this cannot be done in all cases as the following example shows." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Mystery\" Example: [Collatz Conjecture](https://en.wikipedia.org/wiki/Collatz_conjecture)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Let's play the following game:\n", + "- think of any positive integer $n$\n", + "- if $n$ is even, the next $n$ is half the old $n$\n", + "- if $n$ is odd, multiply the old $n$ by $3$ and add $1$ to obtain the next $n$\n", + "- repeat these steps until you reach $1$\n", + "\n", + "**Do we always reach the final $1$?**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The function below implements this game. Does it always reach $1$? No one has proven it so far! We include some input validation as before because `collatz()` would definitely not terminate if we called it with a negative number. Further, the Collatz sequence also works for real numbers but then we would have to study fractals (cf., [this](https://en.wikipedia.org/wiki/Collatz_conjecture#Iterating_on_real_or_complex_numbers)). So we restrict our example to integers only." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def collatz(n):\n", + " \"\"\"Print a Collatz sequence in descending order.\n", + "\n", + " Given a positive integer n, modify it according to these rules:\n", + " - if n is even, the next n is half the previous one\n", + " - if n is odd, the next n is 3 times the previous one plus 1\n", + " - if n is 1, stop the iteration\n", + "\n", + " Args:\n", + " n (int): a positive number to start the Collatz sequence at\n", + "\n", + " Raises:\n", + " TypeError: if n is not of an integer\n", + " ValueError: if n is not positive\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"non-integers require some advanced math\")\n", + " elif n <= 0:\n", + " raise ValueError(\"n must be stricly positive\")\n", + "\n", + " while n != 1:\n", + " print(n, end=\" \")\n", + " if n % 2 == 0:\n", + " n //= 2 # //= so that n remains an int\n", + " else:\n", + " n = 3 * n + 1\n", + "\n", + " print(n) # = base case" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Collatz sequences do not necessarily become longer with a larger initial `n`." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100 50 25 76 38 19 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(100)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1000 500 250 125 376 188 94 47 142 71 214 107 322 161 484 242 121 364 182 91 274 137 412 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593 1780 890 445 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276 638 319 958 479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822 911 2734 1367 4102 2051 6154 3077 9232 4616 2308 1154 577 1732 866 433 1300 650 325 976 488 244 122 61 184 92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10000 5000 2500 1250 625 1876 938 469 1408 704 352 176 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(10000)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100000 50000 25000 12500 6250 3125 9376 4688 2344 1172 586 293 880 440 220 110 55 166 83 250 125 376 188 94 47 142 71 214 107 322 161 484 242 121 364 182 91 274 137 412 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593 1780 890 445 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276 638 319 958 479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822 911 2734 1367 4102 2051 6154 3077 9232 4616 2308 1154 577 1732 866 433 1300 650 325 976 488 244 122 61 184 92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(100000)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `for` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can re-formulate a recursive implementation in an iterative way and vice verca despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", + "\n", + "So how does the `for` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) we saw in the very first example in this book fit into this picture? It is really a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. Sugar makes a cup of tea taste better but we can drink tea without sugar too.\n", + "\n", + "Consider the following `numbers` list. Without the `for` statement, we would have to keep track of a temporary **index variable** `i` to loop over all its elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [5, 6, 7, 8, 9]" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 6 7 8 9 " + ] + } + ], + "source": [ + "i = 0\n", + "while i < len(numbers):\n", + " number = numbers[i]\n", + " print(number, end=\" \")\n", + " i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `for` statement, on the contrary, makes the actual business logic more apparent by stripping all the boilerplate code away." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 6 7 8 9 " + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For sequences of integers the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly\" and we will look closely at the underlying data types in Chapter 7." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "for number in [0, 1, 2, 3, 4]:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "for number in range(5):\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's quickly verify that `range(5)` creates an object of type `range`." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "range" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(range(5))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[range()](https://docs.python.org/3/library/functions.html#func-range) takes optional `start` and `step` arguments that we can use to customize the sequence of integers even more." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 3 5 7 9 " + ] + } + ], + "source": [ + "for number in [1, 3, 5, 7, 9]:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 3 5 7 9 " + ] + } + ], + "source": [ + "for number in range(1, 10, 2):\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Containers vs. Iterables" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The important difference between the `list` objects (i.e., `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`) and the `range` objects (i.e., `range(5)` and `range(1, 10, 2)`) is that in the former case *six* objects are created in memory, one `list` holding pointers to *five* `int` objects, whereas in the latter case only *one* `range` object exists in memory that **generates** `int` objects as we ask for it.\n", + "\n", + "However, we can iterate over both of them. So a natural question to ask is why Python treats objects of *different* types in the *same* way when used with a `for` statement.\n", + "\n", + "So far, the overarching storyline in this book goes like this: In Python, *everything* is an object. Besides its *identity* and *value*, every object is characterized by belonging to *one* data type that determines how the object behaves and what we can do with it.\n", + "\n", + "Now, just as we classify objects by their types, we also classify these **concrete data types** (e.g., `int`, `float`, or `str`) into **abstract concepts**.\n", + "\n", + "We have actually done this in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) already when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an inherent order associated with its elements. There exist, however, many other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", + "\n", + "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is by definition an iterable. So, `range` objects, for example, are iterables taht do *not* contain any other objects. Moreover, looping does *not* have to occur in any particular order although this is the case for both `list` and `range` objects.\n", + "\n", + "Typically, containers are iterable and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 10 will finally give an explanation as to how abstract concepts are implemented and play together.\n", + "\n", + "`list` objects like `first_names` below are iterable containers. They actually implement even more abstract concepts as we will see in Chapter 7." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "first_names = [\"Achim\", \"Berthold\", \"Carl\", \"Diedrich\", \"Eckardt\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The characteristic operator associated with a container type is the `in` operator which checks if a given object evaluates equal to any of the objects in the container. Colloquially, it checks if an object is \"contained\" in the container. This operation is also called **membership testing**." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Achim\" in first_names" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Alexander\" in first_names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This shows the exact workings of the `in` operator: Although `7.0` is *not* in `numbers`, `7.0` evaluates equal to the `7` that is in it, which is why the following expression evaluates to `True`. So, while we could colloquially say that `numbers` \"contains\" `7.0`, it actually does not." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "7.0 in numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, the characteristic operation of an iterable type is that it supports iteration, for example, with a `for`-loop." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Achim Berthold Carl Diedrich Eckardt " + ] + } + ], + "source": [ + "for name in first_names:\n", + " print(name, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we need to have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an iterable as its argument and then generates a stream of \"pairs\" consisting of an index variable and an object provided by the iterable. There is *no* need to ever revert back to the `while` statement to loop over an iterable object." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 > Achim 1 > Berthold 2 > Carl 3 > Diedrich 4 > Eckardt " + ] + } + ], + "source": [ + "for i, name in enumerate(first_names):\n", + " print(i, name, sep=\" > \", end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[enumerate()](https://docs.python.org/3/library/functions.html#enumerate) takes an optional `start` argument." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 > Achim 2 > Berthold 3 > Carl 4 > Diedrich 5 > Eckardt " + ] + } + ], + "source": [ + "for i, name in enumerate(first_names, start=1):\n", + " print(i, name, sep=\" > \", end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [zip()](https://docs.python.org/3/library/functions.html#zip) built-in allows us to combine the elements of two or more iterables in a *pairwise* fashion: It conceptually works like a zipper for a jacket." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "last_names = [\"MĂ¼ller\", \"Meyer\", \"Mayer\", \"Schmitt\", \"Schmidt\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Achim MĂ¼ller\n", + "Berthold Meyer\n", + "Carl Mayer\n", + "Diedrich Schmitt\n", + "Eckardt Schmidt\n" + ] + } + ], + "source": [ + "for first_name, last_name in zip(first_names, last_names):\n", + " print(first_name, last_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Hard at first Glance\" Example: [Fibonacci Numbers](https://en.wikipedia.org/wiki/Fibonacci_number) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In contrast to its recursive counterpart, the iterative `fibonacci()` function below is somewhat harder to read. For example, it is not so obvious as to how many iterations through the `for`-loop we need to make when implementing it. There is an increased risk of making an *off-by-one* error. Moreover, we need to track a `temp` variable along, at least until we have worked through Chapter 7. Do you understand what `temp` does?\n", + "\n", + "However, one advantage of calculating Fibonacci numbers in a **forwards** fashion with a `for` statement is that we could list the entire sequence in ascending order as we calculate the desired number. To show this, we added `print()` statements in `fibonacci()` below.\n", + "\n", + "We do *not* need to store the index variable in the `for`-loop's header line: That is what the underscore \"\\_\" indicates; we \"throw it away\". Also, we do not need to explicitly check if `i` is of type `int` as the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in raises a `TypeError` if used with anything other than an `int`." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def fibonacci(i):\n", + " \"\"\"Calculate the ith Fibonacci number.\n", + "\n", + " Args:\n", + " i (int): index of the Fibonacci number to calculate\n", + "\n", + " Returns:\n", + " ith_fibonacci (int)\n", + "\n", + " Raises:\n", + " TypeError: if i is not of an integer type\n", + " ValueError: if i is not positive\n", + " \"\"\"\n", + " # no need to check if i is an integer as range() does that\n", + " if i < 0:\n", + " raise ValueError(\"i must be non-negative\")\n", + "\n", + " a = 0\n", + " b = 1\n", + " print(a, b, sep=\" \", end=\" \") # added for didactical purposes\n", + " for _ in range(i - 1): # the index variable is not needed\n", + " temp = a + b\n", + " a = b\n", + " b = temp\n", + " print(b, end=\" \") # added for didactical purposes\n", + "\n", + " return b" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 1 2 3 5 8 13 21 34 55 89 144 " + ] + }, + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12) # = 13th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Efficiency of Algorithms (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another more important advantage is that now we can calculate even big Fibonacci numbers *efficiently*." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 32951280099 53316291173 86267571272 139583862445 225851433717 365435296162 591286729879 956722026041 1548008755920 2504730781961 4052739537881 6557470319842 10610209857723 17167680177565 27777890035288 44945570212853 72723460248141 117669030460994 190392490709135 308061521170129 498454011879264 806515533049393 1304969544928657 2111485077978050 3416454622906707 5527939700884757 8944394323791464 14472334024676221 23416728348467685 37889062373143906 61305790721611591 99194853094755497 160500643816367088 259695496911122585 420196140727489673 679891637638612258 1100087778366101931 1779979416004714189 2880067194370816120 4660046610375530309 7540113804746346429 12200160415121876738 19740274219868223167 31940434634990099905 51680708854858323072 83621143489848422977 135301852344706746049 218922995834555169026 " + ] + }, + { + "data": { + "text/plain": [ + "218922995834555169026" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(99) # = 100th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Easy Example: [Factorial](https://en.wikipedia.org/wiki/Factorial) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The iterative `factorial()` function is comparable to its recursive counterpart when it comes to readability. One advantage of calculating the factorial in a forwards fashion is that we could track the intermediate `product` as it grows." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for, must be positive\n", + "\n", + " Returns:\n", + " factorial (int)\n", + "\n", + " Raises:\n", + " TypeError: if n is not an integer\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"Factorial is only defined for integers\")\n", + " elif n < 0:\n", + " raise ValueError(\"Factorial is not defined for negative integers\")\n", + "\n", + " product = 1 # because 0! = 1\n", + " for i in range(1, n + 1): # loop starts at 1 as 0! is already covered\n", + " product *= i\n", + " print(product, end=\" \") # added for didactical purposes\n", + "\n", + " return product" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 6 24 120 720 5040 40320 362880 3628800 " + ] + }, + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `break` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The remainder of this chapter introduces more syntactic sugar. None of the language constructs introduced below are actually needed but contribute to making Python the expressive and easy to read language that it is." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Example: Searching an Iterable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's say we have a list of numbers and we want to check if the square of at least one of its numbers is above a `threshold` of `100`." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [3, 7, 2, 9, 11, 4, 7, 9, 4, 5]" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "threshold = 100 # is the square of an element in numbers greater than this?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A first naive implementation could look like this: We loop over *every* element in `numbers` and set an **indicator variable** `is_above` to `True` once we encounter an element satisfying our search condition." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 4 7 9 4 5 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "is_above = False\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " is_above = True\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This implementation is *inefficient* as even if the *first* number in `numbers` has a square greater than `100`, we loop until the last element, which could take a long time for a big list.\n", + "\n", + "Moreover, we must initialize `is_above` before the `for`-loop and write an `if`-`else`-logic seperate from it to check for the final result. The actual business logic is not clear right away.\n", + "\n", + "Luckily, Python provides a `break` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) that let's us stop the `for`-loop in any iteration of the loop. Conceptually, it is yet another means of controlling the flow of execution." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "is_above = False\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " is_above = True\n", + " break\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This is a computational improvement. However, the code can still be split into *three* groups: initialization, the `for`-loop, and some finalizing logic. We would prefer to convey the program's idea in *one* compound statement." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `for`-`else` Clause" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop. The `else`-branch is only executed if the body in the `for`-branch is *not* stopped with a `break` statement before reaching the last iteration in the loop. The word \"else\" implies a rather unintuitive meaning and it had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement within the `for`-loop's body.\n", + "\n", + "Overall, the expressive power of our code increases. Not many programming languages support a `for`-`else`-branching, which turns out to be very useful in practice." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Searching an Iterable (revisited)" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " is_above = True\n", + " break\n", + "else:\n", + " is_above = False\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, we incorporate the `if`-`else` logic at the end into the `for`-loop and avoid the `is_above` variable alltogether." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + " break\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, if we set the `threshold` a number's square has to pass higher, for example to `200`, we have to loop through the entire `numbers` list. There is no way to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)**, at least as long as we model the list of numbers with a `list` object. More advanced data types, however, exist that mitigate that downside." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 4 7 9 4 5 => no number's square is above 200\n" + ] + } + ], + "source": [ + "threshold = 200\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + " break\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `continue` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often times, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) or, more realistically, data from a CSV file with many rows and columns.\n", + "\n", + "Processing numeric data usually comes down to operations that can be grouped into one of the following three categories:\n", + "\n", + "- **mapping**: transform an observation according to some functional relationship $y = f(x)$\n", + "- **filtering**: throw away individual observations (e.g., statistical outliers)\n", + "- **reducing**: collect individual observations into summary statistics\n", + "\n", + "We will study this **map-filter-reduce** paradigm extensively in Chapter 7 after introducing more advanced data types that are needed to work with \"big\" data.\n", + "\n", + "In this section, we focus on *filtering out* some samples within a `for`-loop." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Example: A simple Filter" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Calculate the sum of all even numbers from $1$ through $12$ after squaring them and adding $1$ to the squares:\n", + "\n", + "- **\"all\"** => loop over an iterable\n", + "- **\"even\"** => *filter* out the odd numbers\n", + "- **\"square and add $1$\"** => apply the *map* $y = f(x) = x^2 + 1$\n", + "- **\"sum\"** => *reduce* the remaining and mapped numbers to their sum" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 > 5 4 > 17 6 > 37 8 > 65 10 > 101 12 > 145 " + ] + }, + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for x in numbers:\n", + " if x % 2 == 0: # only keep even numbers\n", + " y = (x ** 2) + 1\n", + " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes\n", + " total += y\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The above code is still rather easy to read as it only involves two levels of indentation.\n", + "\n", + "In general, code gets harder to comprehend the more **horizontal space** it occupies. It is commonly considered good practice to grow a program **vertically** rather than horizontally. Code complient with [PEP 8](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) requires us to use *at most* 79 characters in a line!\n", + "\n", + "Consider the next example, whose implementation in code already starts to look \"unbalanced\"." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Example: Nested Filters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Calculate the sum of every third and even number from $1$ through $12$ after squaring them and adding $1$ to the squares:\n", + "\n", + "- **\"every\"** => loop over an iterable\n", + "- **\"third\"** => *filter* out all numbers except every third\n", + "- **\"even\"** => *filter* out the odd numbers\n", + "- **\"square and add $1$\"** => apply the *map* $y = f(x) = x^2 + 1$\n", + "- **\"sum\"** => *reduce* the remaining and mapped numbers to their sum" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 > 37 12 > 145 " + ] + }, + { + "data": { + "text/plain": [ + "182" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for i, x in enumerate(numbers, start=1):\n", + " if i % 3 == 0: # only keep every third number\n", + " if x % 2 == 0: # only keep even numbers\n", + " y = (x ** 2) + 1\n", + " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes \n", + " total += y\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With already three levels of indentation, less horizontal space is available for the actual code. Of course, one could combine the two `if` statements with the logical `and` operator as shown in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb). However, then we trade off horizontal space against a more \"complex\" `if` logic and this is not a real improvement.\n", + "\n", + "A Pythonista would instead make use of the `continue` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement) that causes a loop to jump right into the next iteration skipping the rest of the code block.\n", + "\n", + "The revised code fragement below occupies more vertical space and less horizontal space. A good trade-off." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Nested Filters (revisited)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 > 37 12 > 145 " + ] + }, + { + "data": { + "text/plain": [ + "182" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for i, x in enumerate(numbers, start=1):\n", + " if i % 3 != 0: # only keep every third number\n", + " continue\n", + " elif x % 2 != 0: # only keep even numbers\n", + " continue\n", + "\n", + " y = (x ** 2) + 1\n", + " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes \n", + " total += y\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This is yet another illustration of why programming is an art. The two preceding code fragments do *exactly* the *same* with *identical* time complexity. However, arguably the latter it a lot easier to read for a human, at least when the business logic grows beyond more than just two nested filters.\n", + "\n", + "The idea behind the `continue` statement is conceptually similar to the early exit pattern we saw in the context of function definitions in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb).\n", + "\n", + "The two examples can be modeled in an even better way as we will see in Chapter 7.\n", + "\n", + "Both the `break` and `continue` statements as well as the optional `else`-clause are not only supported within `for`-loops but also `while`-loops." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Indefinite Loops" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes we find ourselves in situations where we can *not* know ahead of time how often or until which point in time a code block is to be executed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Example: Guessing a Coin Toss" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's consider a game where we randomly choose a variable to be either \"Heads\" or \"Tails\" and the user of our program has to guess it.\n", + "\n", + "Python provides the built-in [input()](https://docs.python.org/3/library/functions.html#input) function that prints a message to the user, called the **prompt**, and reads in what the user typed in response as a `str` object. We use it to process the user's \"unpredictable\" input to our program (i.e., a user might type in some invalid response). Further, we use the [random()](https://docs.python.org/3/library/random.html#random.random) function in the [random](https://docs.python.org/3/library/random.html) module to model the coin toss.\n", + "\n", + "A popular pattern to approach such **indefinite loops** is to go with a `while True` statement which on its own would cause Python to enter into an infinite loop. Then, once a certain event occurs, we `break` out of the loop.\n", + "\n", + "Let's look at a first naive implementation." + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: Tails\n", + "Ooops, it was tails\n", + "Guess if the coin comes up as heads or tails: heads\n", + "Yes, it was heads\n" + ] + } + ], + "source": [ + "while True:\n", + " guess = input(\"Guess if the coin comes up as heads or tails: \")\n", + "\n", + " if random.random() < 0.5:\n", + " if guess == \"heads\":\n", + " print(\"Yes, it was heads\")\n", + " break\n", + " else:\n", + " print(\"Ooops, it was heads\")\n", + " else:\n", + " if guess == \"tails\":\n", + " print(\"Yes, it was tails\")\n", + " break\n", + " else:\n", + " print(\"Ooops, it was tails\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This version has two *severe* aspects where we should improve on:\n", + "\n", + "1. If a user enters something other than \"heads\" or \"tails\", for example, \"Heads\" or \"Tails\", the program keeps running without the user knowing about the mistake!\n", + "2. It intermingles the coin tossing with the comparison against the user's input: Mixing unrelated business logic in the same code fragment makes a program harder to read and maintain in the long run." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Example: Guessing a Coin Toss (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's refactor the code and make it *modular* and *comprehendable*.\n", + "\n", + "First, we divide the logic into two functions `get_guess()` and `toss_coin()` that are controlled from within a `while`-loop.\n", + "\n", + "`get_guess()` not only reads in the user's input but also implements a simple input validation pattern in that the [strip()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.strip) and [lower()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.lower) methods remove preceeding and trailing whitespace and lower case the input ensuring that the user can spell the input in any possible way (e.g. all upper or lower case). Also, `get_guess()` checks if the user entered one of the two valid options. If so, it returns either `\"heads\"` or `\"tails\"`; if not, it returns `None`." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def get_guess():\n", + " \"\"\"Process the user's input.\n", + " \n", + " Returns:\n", + " guess (str / NoneType): either \"heads\" or \"tails\"\n", + " if the input can be parsed and None otherwise\n", + " \"\"\"\n", + " guess = input(\"Guess if the coin comes up as heads or tails: \")\n", + " # handle frequent cases of \"misspelled\" user input\n", + " guess = guess.strip().lower()\n", + "\n", + " if guess in [\"heads\", \"tails\"]:\n", + " return guess\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`toss_coin()` is a simple function that models a fair coin toss when called with default arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def toss_coin(p_heads=0.5):\n", + " \"\"\"Simulate the tossing of a coin.\n", + "\n", + " Args:\n", + " p_heads (optional, float): probability that the coin comes up \"heads\";\n", + " defaults to 0.5 resembling a fair coin\n", + "\n", + " Returns:\n", + " side_on_top (str): \"heads\" or \"tails\"\n", + " \"\"\"\n", + " if random.random() < p_heads:\n", + " return \"heads\"\n", + " return \"tails\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Second, we rewrite the `if`-`else`-logic to explictly handle the case where `get_guess()` returns `None`: Whenever the user enters something invalid, a warning is shown, and another try is granted. Observe how we use the `is` operator and not the `==` operator as `None` is a singleton object.\n", + "\n", + "The `while` statement itself takes on the role of **glue code** that only manages how other parts of the program interact with each other." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: invalid\n", + "Make sure to enter your guess correctly!\n", + "Guess if the coin comes up as heads or tails: Heads\n", + "Yes, it was heads\n" + ] + } + ], + "source": [ + "while True:\n", + " guess = get_guess()\n", + " result = toss_coin()\n", + "\n", + " if guess is None:\n", + " print(\"Make sure to enter your guess correctly!\")\n", + " elif guess == result:\n", + " print(\"Yes, it was\", result)\n", + " break\n", + " else:\n", + " print(\"Ooops, it was\", result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, our little program's business logic is a lot easier to comprehend. More importantly, we can now easily make changes to the program. For example, we could make the `toss_coin()` function base the tossing on a probability distribution other than the uniform (i.e., replace the [random.random()](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, a modular architecture leads to improved software maintenance." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Iteration** is about **running blocks of code repeatedly**.\n", + "\n", + "There are two redundant approaches of achieving that.\n", + "\n", + "First, we can combine functions that call themselves with conditional statements. This concept is known as **recursion** and suffices to control the flow of execution in *every* way we desire. For a beginner, this approach of **backwards** reasoning might not be intuitive but it turns out to be a very useful tool to have in one's toolbox.\n", + "\n", + "Second, the `while` and `for` statements are alternative and potentially more intuitive ways to express iteration as they correspond to a **forwards** reasoning. The `for` statement is **syntactic sugar** that allows to rewrite common occurences of the `while` statement in a concise way. Python provides the `break` and `continue` statements as well as an optional `else`-clause that make working with the `for` and `while` statements even more convenient.\n", + "\n", + "**Iterables** are any **concrete data types** that support being looped over, for example, with the `for` statement. The idea behind iterables is an **abstract concept** that may or may not be implemented by any given concrete data type. For example, both `list` and `range` objects can be looped over. The `list` type is also a **container** as any given `list` objects \"contains\" pointers to other objects in memory. On the contrary, the `range` type does not point to any other objects but rather creates new `int` objects \"on the fly\"." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/04_iteration_review_and_exercises.ipynb b/04_iteration_review_and_exercises.ipynb new file mode 100644 index 0000000..0e937b7 --- /dev/null +++ b/04_iteration_review_and_exercises.ipynb @@ -0,0 +1,941 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 4: Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) of the book. Then work through the fourteen review questions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions briefly with *at most* 300 characters per question!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: What is so \"special\" about the number **7919**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Solving a problem with a **recursion** is not only popular in computer science and math. Name some examples from the fields of business or economics where problems are also solved in a **backwards** fashion!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Explain what **duck typing** means! Why can it cause problems? Why it is [not a bug but a feature](https://www.urbandictionary.com/define.php?term=It%27s%20not%20a%20bug%2C%20it%27s%20a%20feature)?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What is **syntactic sugar**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Describe in your own words why the **recursive** version of `fibonacci()`, the \"Easy at first Glance\" example in the chapter, is computationally **inefficient**! Why does the **iterative** version of `fibonacci()`, the \"Hard at first Glance\" example, run so much faster?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What is the conceptual difference between a **container** and a **list**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: What is a good use case for the `for`-loop's optional `else`-clause?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: When a **recursion** does **not** reach the base case, this is an example of the **early exit** strategy." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Any programming language **without** looping constructs like the `for` or `while` statements is **not** Turing complete." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: A **recursive** formulation is the same as a **circular** one: The terms are **synonyms**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Formulating a computational problem as a **recursion** results in an **efficient** implementation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Whereas a **recursion** may accidently result in a **never ending** program, `while`-loops and `for`-loops are guaranteed to **terminate**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: Before writing **any** kind of **loop**, we **always** need to think about a **stopping criterion** ahead of time." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: **Container** types such as `list` objects are characterized by their **support** for **being looped over**, for example as in:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```python\n", + "for element in container:\n", + " # do something for every element\n", + " ...\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Towers of Hanoi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A popular example for a problem that is solved with a recursion art the **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)**.\n", + "\n", + "In its basic version, a tower consisting of, for example, four disks with increasing radii, is placed on the left-most of **three** adjacent spots. In the following, we refer to the number of disks as $n$, so here $n = 4$.\n", + "\n", + "The task is to move the entire tower to the right-most spot whereby **two rules** must be obeyed:\n", + "\n", + "1. Disks can only be moved individually, and\n", + "2. a disk with a larger radius must *never* be placed on a disk with a smaller one.\n", + "\n", + "Although the **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** are a **classic** example, introduced by the mathematician [Édouard Lucas](https://en.wikipedia.org/wiki/%C3%89douard_Lucas) already in 1883, it is still **actively** researched as this scholarly [article](https://www.worldscientific.com/doi/abs/10.1142/S1793830919300017?journalCode=dmaa&) published in January 2019 shows.\n", + "\n", + "Despite being so easy to formulate, the game is quite hard to solve.\n", + "\n", + "Below is an interactive illustration of the solution with the minimal number of moves for $n = 4$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Watch the following video by [MIT](https://www.mit.edu/)'s professor [Richard Larson](https://idss.mit.edu/staff/richard-larson/) for a comprehensive introduction.\n", + "\n", + "The [MIT Blossoms Initative](https://blossoms.mit.edu/) is primarily aimed at high school students and does not have any prerequisites.\n", + "\n", + "The video consists of three segments the last of which is *not* necessary to have watched in order to solve the tasks below. So, watch the video until 37:55." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"UuIneNBbscc\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Video Review Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.1**: Explain for the $n = 3$ case why it can be solved as a **recursion**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.2**: How does the number of minimal moves needed to solve a problem with three spots and $n$ disks grow as a function of $n$? How does this relate to the answer to **Q15.1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.3**: The **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem is of **exponential growth**. What does that mean? What does that imply for large $n$?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.4**: The video introduces the recursive relationship $Sol(4, 1, 3) = Sol(3, 1, 2) ~ \\bigoplus ~ Sol(1, 1, 3) ~ \\bigoplus ~ Sol(3, 2, 3)$. The $\\bigoplus$ is to be interpreted as some sort of \"plus\" operation. How does this \"plus\" operation work? How does this way of expressing the problem relate to the answer to **Q15.1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Naive Translation to Python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As most likely the first couple of tries will result in *semantic* errors, it is advisable to have some sort of **visualization tool** for the progam's output: For example, an online version of the game can be found **[here](https://www.mathsisfun.com/games/towerofhanoi.html)**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's first **generalize** the mathematical relationship from above.\n", + "\n", + "While the first number of $Sol(\\cdot)$ is the number of `disks` $n$, the second and third \"numbers\" are actually the **labels** for the three spots. Instead of spots `1`, `2`, and `3` we could also call them `\"left\"`, `\"center\"`, and `\"right\"` in our Python implementation. When \"passed\" to the $Sol(\\cdot)$ \"function\" they take on the role of an `origin` (= $o$) and `destination` (= $d$) pair.\n", + "\n", + "So, the expression $Sol(4, 1, 3)$ is the same as $Sol(4, \\text{\"left\"}, \\text{\"right\"})$ and describes the problem of moving a tower consisting of $n = 4$ disks from `origin` `1` / `\"left\"` to `destination` `3` / `right`. As we have seen in the video, we need some `intermediate` (= $i$) spot." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In summary, the generalized functional relationship can be expressed as:\n", + "\n", + "$Sol(n, o, d) = Sol(n-1, o, i) ~ \\bigoplus ~ Sol(1, o, d) ~ \\bigoplus ~ Sol(n-1, i, d)$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In words, this means that in order to move a tower consisting of $n$ disks from an `origin` $o$ to a `destination` $d$, we three steps must be executed:\n", + "\n", + "1. Move the top most $n - 1$ disks of the tower temporarily from $o$ to $i$ (= sub-problem 1)\n", + "2. Move the remaining and largest disk from $o$ to $d$\n", + "3. Move the the $n - 1$ disks from the temporary spot $i$ to $d$ (= sub-problem 2)\n", + "\n", + "The two sub-problems can be solved via the same recursive logic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$Sol(\\cdot)$ can be written in Python as a function `sol()` that takes three arguments `disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$.\n", + "\n", + "Assume that all arguments to `sol()` will be `int` objects!\n", + "\n", + "Once completed, `sol()` should print out all the moves in the correct order. With **printing a move**, we simply mean a line like \"1 -> 3\", short for \"Move the top-most disk from spot 1 to spot 3\".\n", + "\n", + "Write your answers to **Q15.5** to **Q15.7** into the subsequent code cell and finalize `sol()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def sol(disks, origin, destination):\n", + "\n", + " # answer to Q15.5\n", + " # ...\n", + "\n", + " # answer to Q15.6\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + "\n", + " # answer to Q15.7\n", + " # ...\n", + " # ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.5**: What is the `disks` argument when the function reaches its **base case**? Check for the base case with a simple `if` statement and return from the function using the **early exit** pattern!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.6**: If not in the base case, `sol()` needs to determine the `intermediate` spot given concrete `origin` and `destination` arguments. For example, if called with `origin=1` and `destination=2`, `intermediate` must be `3`.\n", + "\n", + "Add **one** compound `if` statement to `sol()` that has a branch for **every** possible `origin`-`destination` pair that sets a variable `intermediate` to the correct temporary spot. **How many** branches will there be?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.7**: `sol()` needs to call itself **two more times** with the correct 2-pairs chosen from the three available spots `origin`, `intermediate`, and `destination`.\n", + "\n", + "In between the two recursive function calls, write a `print()` statement that prints out from where to where the \"remaining and largest\" disk has to be moved!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.8**: Execute the code cells below and confirm that the printed moves are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(1, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(2, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(3, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(4, 1, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Pythonic Re-Factoring" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The previous `sol()` implementation does the job but the conditional statement needed in unnecessarily tedious. \n", + "\n", + "Let's create a more concise `hanoi()` function that in addition to a positional `disks` argument takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n", + "\n", + "Write your answers to **Q15.9** and **Q15.10** into the subsequent code cell and finalize `hanoi()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", + "\n", + " # answer to Q15.9\n", + " # ...\n", + "\n", + " # answer to Q15.10\n", + " # ...\n", + " # ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.9**: Copy the base case from `sol()`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.10**: Instead of conditional logic, `hanoi()` calls itself **two times** with the **three** arguments `origin`, `intermediate`, and `destination` passed on in a **different** order.\n", + "\n", + "Figure out how the arguments are passed on in the two recursive `hanoi()` calls!\n", + "\n", + "Also, write a `print()` statement analogous to the one in `sol()` in between the two recursive function calls. Is it ok to just copy and paste it?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.11**: Execute the code cells below and confirm that the printed moves are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We could of course also use **numeric labels** for the three steps like so." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(3, origin=1, intermediate=2, destination=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Passing a Value \"up\" the Recursion Tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's say, we did not know about the **analytical formula** for the number of **minimal moves** given $n$.\n", + "\n", + "In such cases, we could modify a recursive function to return a count value to be passed up the recursion tree.\n", + "\n", + "In fact, this is similar to what we do in the recursive versions of `factorial()` and `fibonacci()` in [Chapter 4](https://github.com/webartifex/intro-to-python/blob/master/04_iteration.ipynb) where we pass up an intermediate result.\n", + "\n", + "Let's create a `hanoi_moves()` function that follows the same internal logic as `hanoi()` but instead of printing out the moves returns the number of steps done so far in the recursion.\n", + "\n", + "Write your answers to **Q15.12** to **Q15.14** into the subsequent code cell and finalize `hanoi_moves()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi_moves(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", + "\n", + " # answer to Q15.12\n", + " # ...\n", + "\n", + " moves = ... # <- answer to Q15.13\n", + " moves += hanoi_moves(...) # <- answer to Q15.14 between the ()\n", + " moves += hanoi_moves(...) # <- answer to Q15.14 between the ()\n", + "\n", + " return moves" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.12**: Copy the base case from `hanoi()`! What count should be returned when it is reached?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.13**: Initialize the variable `moves` with an appropriate count! This is the number of moves that corresponds to **one** recursive function call." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.14**: `moves` is updated with the counts passed up from the two recursive calls.\n", + "\n", + "Complete the two recursive function calls with the same arguments as in `hanoi()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.15**: Write a `for`-loop that prints out the **minimum number** of moves needed to solve Towers of Hanoi for any number of `disks` from `1` through `20` to confirm your answer to **Q15.2**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Time Complexity" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Observe how quickly the `hanoi_moves()` function slows down for increasing `disks` arguments.\n", + "\n", + "With `disks` in the range from `24` through `26` the computation time roughly doubles for each increase of `disks` by 1.\n", + "\n", + "**Q15.16**: Execute the code cells below and see for yourself!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1 -r 1\n", + "print(\"Number of moves:\", hanoi_moves(24))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1 -r 1\n", + "print(\"Number of moves:\", hanoi_moves(25))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1 -r 1\n", + "print(\"Number of moves:\", hanoi_moves(26))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Passing a Value \"down\" the Recursion Tree (Advanced)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above `hanoi()` prints the optimal solution's moves in the correct order but fails to label each move with an order number. This can be build in by passing on one more argument `offset` down the recursion tree. As the logic gets a bit \"involved\", `hanoi_ordered()` below is almost finished.\n", + "\n", + "Write your answers to **Q15.17** and **Q15.18** into the subsequent code cell and finalize `hanoi_ordered()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi_ordered(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\", offset=None):\n", + "\n", + " # answer to Q15.17\n", + " # ...\n", + "\n", + " total = (2 ** disks - 1)\n", + " half = (2 ** (disks - 1) - 1)\n", + " count = total - half\n", + "\n", + " if offset is not None:\n", + " count += offset\n", + "\n", + " hanoi_ordered(..., offset=offset) # <- answer to Q15.18 between the ()\n", + " # answer to Q15.18\n", + " hanoi_ordered(..., offset=count) # <- answer to Q15.18 between the ()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.17**: Copy the base case from the original `hanoi()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.18**: Complete the two recursive function calls with the same arguments as in `hanoi()` or `hanoi_moves()`! Do not change the already filled in `offset` arguments!\n", + "\n", + "Then, copy the `print()` statement from `hanoi()` and adjust it to print out `count` as well!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.19**: Execute the code cells below and confirm that the order numbers are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, it is to be mentioned that for problem instances with a small `disks` argument it is easier to collect all the moves first in a list and then add the order number with the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Open Question" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.20**: Conducting your own research on the internet (max. 15 minutes), what can you say about generalizing the **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem to a setting with **more than three** landing spots?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.7.3" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/README.md b/README.md index 951bf75..4561afb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ As such they can be viewed in a plain web browser: - [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) - [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) - [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) +- [04 - Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. diff --git a/static/towers_of_hanoi.gif b/static/towers_of_hanoi.gif new file mode 100644 index 0000000..7ab13d4 Binary files /dev/null and b/static/towers_of_hanoi.gif differ