intro-to-python/04_iteration_00_lecture.ipynb

6581 lines
164 KiB
Text

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 4: Recursion & Looping"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"While controlling the flow of execution with an `if` statement is 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 precisely that to write useful software.\n",
"\n",
"The `for` statement shown in some examples before might be the missing piece in the puzzle. However, we can 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 order is opposite to many other introductory books 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 popular joke among programmers by an unknown author goes like this (cf., [discussion](https://www.quora.com/What-does-the-phrase-in-order-to-understand-recursion-you-must-first-understand-recursion-mean-to-you)):"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"> \"In order to understand **recursion**, you must first understand **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 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, 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 apparent 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 a vital 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 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 may indeed 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: # = base case\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 a **[leap of faith](http://greenteapress.com/thinkpython2/html/thinkpython2007.html#sec75)**. We practice this already 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 exemplary `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 more concise way using the so-called **early exit** pattern: No `else`-clause is needed as reaching a `return` statement ends a function call *immediately*. Furthermore, we do not 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: # = base case\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](https://docs.python.org/3/library/index.html) 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 codebase."
]
},
{
"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: # = base case\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 operator `%`. However, this is *not* true for recursion in general, which may result in slow programs if not applied correctly."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"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": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(7, 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](https://docs.python.org/3/library/index.html) 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(7, 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$ is the $0$th number. Looking at the numbers in a **backward** 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: # = first base case\n",
" return 0\n",
" elif i == 1: # = second base case\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 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 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., \"reuse\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We do so in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_lecture.ipynb#Memoization), after introducing the `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": [
"35.2 µs ± 971 ns 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": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"11.2 ms ± 81.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"%%timeit -n 100\n",
"fibonacci(24)"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3.67 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n"
]
}
],
"source": [
"%%timeit -n 1 -r 1\n",
"fibonacci(36)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5.94 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 theoretically runs 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": 27,
"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": 28,
"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<ipython-input-28-62f686a9f8a1>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-27-80575699c62c>\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<ipython-input-27-80575699c62c>\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 infinite recursion. Let's call it with `3.1` instead of `3`. What goes wrong here?"
]
},
{
"cell_type": "code",
"execution_count": 29,
"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<ipython-input-29-43e3ad331761>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-1-d7441c038fc8>\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<ipython-input-1-d7441c038fc8>\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": 30,
"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<ipython-input-30-188b816be8b6>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-6-d41f5be5adaa>\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;31m# = base case\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<ipython-input-6-d41f5be5adaa>\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;31m# = base case\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 do not encounter any *runtime* errors. This is the case here as the two example functions only use the `-` and `*` operators internally, and, in the context of arithmetic, a `float` object behaves 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`. Strictly speaking, a `RecursionError` is, of course, a *runtime* error as well.\n",
"\n",
"The missing type checking is *100% intentional* and considered a **[feature of rather than a bug](https://www.urbandictionary.com/define.php?term=It%27s%20not%20a%20bug%2C%20it%27s%20a%20feature)** in Python!\n",
"\n",
"Pythonistas often use the colloquial term **[duck typing](https://en.wikipedia.org/wiki/Duck_typing)** when referring to the same idea, and the 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 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 *must* 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 strong opinions concerning duck typing, reaching from \"love\" to \"hate.\" This is another example of how programming is a subjective art rather than \"objective\" science. Python's design is probably more appealing to beginners who intuitively regard `3` and `3.0` as interchangeable."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Type Checking & Input Validation"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We 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 as the argument. We further **validate the input** by verifying that the integer is non-negative.\n",
"\n",
"Meanwhile, we also see how we manually raise exceptions with the `raise` statement (cf., [reference](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 may 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 cast as an `int`. After all, being too puritan, we cannot take advantage of duck typing.\n",
"\n",
"So, in essence, we are doing *two* things here: Besides checking the type, we also enforce **domain-specific** (i.e., mathematical here) rules concerning the non-negativity of `n`."
]
},
{
"cell_type": "code",
"execution_count": 31,
"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: # = base case\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": 32,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"factorial(0)"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"6"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"factorial(3)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Instead of running into a situation of infinite recursion, we now receive specific error messages."
]
},
{
"cell_type": "code",
"execution_count": 34,
"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<ipython-input-34-188b816be8b6>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-31-ff0b507f76b8>\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": 35,
"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<ipython-input-35-b06b2cada645>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-31-ff0b507f76b8>\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;31m# = base case\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, 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 repetitive logic, Python comes with a compound `while` statement (cf., [reference](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**\" (i.e., more formally, **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 only to be called with strictly positive integers. As any positive integer hits $0$ at some point when iteratively decremented by $1$, `countdown()` is guaranteed to **terminate**. Also, 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": 36,
"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 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!\")"
]
},
{
"cell_type": "code",
"execution_count": 37,
"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 essential 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 typical day-to-day applications, this difference is, however, not so important *unless* a problem instance becomes so big that a large (i.e., $> 3.000$) number of recursive calls must be made."
]
},
{
"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 recursive formulation.\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` eventually become equal. Thus, this algorithm is also guaranteed to terminate. If one of the two numbers were negative or $0$ in the first place, `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": 38,
"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 integers\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": 39,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(12, 4)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"gcd(7, 7919)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"##### Efficiency of Algorithms (continued)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We 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": 41,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5.22 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** is 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 for sure 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": 42,
"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 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 # //= to preserve the int type\n",
" else:\n",
" n = 3 * n + 1\n",
"\n",
" print(1)"
]
},
{
"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": 43,
"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": 44,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"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": 45,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"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": 46,
"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 formulate any recursive implementation in an iterative way and vice versa despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n",
"\n",
"So how does the compound `for` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement)) in this book's very first example fit into this picture? It is 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**. A cup of tea tastes better with sugar, but we may drink tea without sugar too.\n",
"\n",
"Consider `elements` below. Without the `for` statement, we must manage a temporary **index variable**, `index`, to loop over all the elements and also obtain the individual elements with the `[]` operator in each iteration of the loop."
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"elements = [0, 1, 2, 3, 4]"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 1 2 3 4 "
]
}
],
"source": [
"index = 0\n",
"\n",
"while index < len(elements):\n",
" element = elements[index]\n",
" print(element, end=\" \")\n",
" index += 1\n",
"\n",
"del index"
]
},
{
"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](https://en.wikipedia.org/wiki/Boilerplate_code)** away. The variable that is automatically set by Python in each iteration of the loop (i.e., `element` in the example) is called the **target variable**."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 1 2 3 4 "
]
}
],
"source": [
"for element in elements:\n",
" print(element, 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 look closely at the underlying effects in memory in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_lecture.ipynb#Mapping)."
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 1 2 3 4 "
]
}
],
"source": [
"for element in range(5):\n",
" print(element, end=\" \")"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"range"
]
},
"execution_count": 51,
"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 use to customize the sequence of integers even more."
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 3 5 7 9 "
]
}
],
"source": [
"for element in [1, 3, 5, 7, 9]:\n",
" print(element, end=\" \")"
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 3 5 7 9 "
]
}
],
"source": [
"for element in range(1, 10, 2):\n",
" print(element, end=\" \")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"#### Containers vs. Iterables"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The essential difference between the above `list` objects, `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`, and the `range` objects, `range(5)` and `range(1, 10, 2)`, is that in the former case *six* objects are created in memory *before* the `for` statement starts running, *one* `list` holding references to *five* `int` objects, whereas in the latter case only *one* `range` object is created that **generates** `int` objects one at a time *while* the `for`-loop runs.\n",
"\n",
"However, we can loop 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 may do with it.\n",
"\n",
"Now, just as we classify objects by data type, we also classify these data types (e.g., `int`, `float`, `str`, or `list`) into **abstract concepts**.\n",
"\n",
"We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Who-am-I?-And-how-many?) when we described a `list` object as \"some sort of container that holds [...] references 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 order associated with their elements. There exist, however, 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, even though they hold no references to other objects. Moreover, looping does *not* have to occur in a *predictable* order, although this is the case for both `list` and `range` objects.\n",
"\n",
"Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_lecture.ipynb#Collections-vs.-Sequences), we formalize these two concepts and introduce many more. Finally, Chapter 10 gives an explanation how abstract concepts are implemented and play together.\n",
"\n",
"Let's continue with `first_names` below as an example an illustrate what iterable containers are."
]
},
{
"cell_type": "code",
"execution_count": 54,
"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 container types is the `in` operator: It checks if a given object evaluates equal to at least one of the objects in the container. Colloquially, it checks if an object is \"contained\" in the container. Formally, this operation is called **membership testing**."
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"Achim\" in first_names"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 56,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"Alexander\" in first_names"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The cell below shows the *exact* workings of the `in` operator: Although `3.0` is *not* contained in `elements`, it evaluates equal to the `3` that is, which is why the following expression evaluates to `True`. So, while we could colloquially say that `elements` \"contains\" `3.0`, it actually does not."
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[0, 1, 2, 3, 4]"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"elements"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"3.0 in elements"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Similarly, the characteristic operation of an iterable type is that it supports being looped over, for example, with the `for` statement."
]
},
{
"cell_type": "code",
"execution_count": 59,
"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 must 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\" of an index variable, `i` below, and an object provided by the iterable, `name`, separated by a `,`. There is *no* need to ever revert to the `while` statement with an explicitly managed index variable to loop over an iterable object."
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"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, 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": 61,
"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, 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": 62,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"last_names = [\"Müller\", \"Meyer\", \"Mayer\", \"Schmitt\", \"Schmidt\"]"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Achim Müller Berthold Meyer Carl Mayer Diedrich Schmitt Eckardt Schmidt "
]
}
],
"source": [
"for first_name, last_name in zip(first_names, last_names):\n",
" print(first_name, last_name, end=\" \")"
]
},
{
"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.\n",
"\n",
"However, one advantage of calculating Fibonacci numbers in a **forward** 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 variable `_` indicates; we \"throw it away.\""
]
},
{
"cell_type": "code",
"execution_count": 64,
"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 an integer\n",
" ValueError: if i is not positive\n",
" \"\"\"\n",
" if not isinstance(i, int):\n",
" raise TypeError(\"i must be an integer\")\n",
" elif 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):\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": 65,
"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": 65,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fibonacci(12) # = 13th number"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"##### Efficiency of Algorithms (continued)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Another more important advantage is that now we may calculate even big Fibonacci numbers *efficiently*."
]
},
{
"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 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": 66,
"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()` implementation is comparable to its recursive counterpart when it comes to readability. One advantage of calculating the factorial in a forward fashion is that we could track the intermediate `product` as it grows."
]
},
{
"cell_type": "code",
"execution_count": 67,
"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):\n",
" product *= i\n",
" print(product, end=\" \") # added for didactical purposes\n",
"\n",
" return product"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 2 6 "
]
},
{
"data": {
"text/plain": [
"6"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"factorial(3)"
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"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": [
"### Customizing `for`- and `while`-loops"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This section introduces additional syntax to customize `for` and `while` statements in our code even further. They are mostly syntactic sugar in that they do not change how a program runs but make its code more readable. We illustrate them for the `for` statement only. However, everything presented in this section also works for the `while` statement."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"#### Example: Is the square of a number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` greater than `100`?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Let's say we have a list of `numbers` and want to check if the square of at least one of its elements is greater than `100`. So, conceptually, we are asking the question if a list of numbers as a whole satisfies a certain condition."
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]"
]
},
{
"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`, initialized as `False`, to `True` once we encounter an element satisfying the condition.\n",
"\n",
"This implementation is *inefficient* as even if the *first* element in `numbers` has a square greater than `100`, we loop until the last element: This 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 *after* it to check for the result. The actual business logic is *not* conveyed in a clear way."
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 8 5 3 12 2 6 9 10 1 4 => at least one number satisfies the condition\n"
]
}
],
"source": [
"is_above = False\n",
"\n",
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" is_above = True\n",
"\n",
"if is_above:\n",
" print(\"=> at least one number satisfies the condition\")\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"#### The `break` Statement"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Python provides the `break` statement (cf., [reference](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement)) that lets us stop a loop prematurely at any iteration. It is yet another means of controlling the flow of execution, and we say that we \"break out of a loop.\""
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 => at least one number satisfies the condition\n"
]
}
],
"source": [
"is_above = False\n",
"\n",
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" is_above = True\n",
" break\n",
"\n",
"if is_above:\n",
" print(\"=> at least one number satisfies the condition\")\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This is a computational improvement. However, the code still consists of *three* sections: Some initialization *before* the `for`-loop, the loop itself, and some finalizing logic. We prefer to convey the program's idea in *one* compound statement instead."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"#### The `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 (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement)). The `else`-clause is executed *only if* the `for`-loop is *not* stopped with a `break` statement *prematurely* (i.e., *before* reaching the *last* iteration in the loop). The word \"else\" implies a somewhat unintuitive meaning and may have better been named a `then`-clause. In most use cases, however, the `else`-clause logically goes together with some `if` statement in the loop's body.\n",
"\n",
"Overall, the code's expressive power increases. Not many programming languages support an optional `else`-branching for the `for` and `while` statements, which turns out to be very useful in practice."
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 => at least one number satisfies the condition\n"
]
}
],
"source": [
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" is_above = True\n",
" break\n",
"else:\n",
" is_above = False\n",
"\n",
"if is_above:\n",
" print(\"=> at least one number satisfies the condition\")\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Lastly, we incorporate the finalizing `if`-`else` logic into the `for`-loop, avoiding the `is_above` variable altogether."
]
},
{
"cell_type": "code",
"execution_count": 74,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 => at least one number satisfies the condition\n"
]
}
],
"source": [
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" print(\"=> at least one number satisfies the condition\")\n",
" break\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Of course, if we choose the number an element's square has to pass to be larger, for example, to `200`, we have to loop over all `numbers`. There is *no way* to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)** further."
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 8 5 3 12 2 6 9 10 1 4 => no number satisfies the condition\n"
]
}
],
"source": [
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 200:\n",
" print(\"=> at least one number satisfies the condition\")\n",
" break\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"#### A first Glance at the Map-Filter-Reduce Paradigm"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Often, we process some iterable with numeric data, for example, a list of `numbers` as in this book's introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Example:-Averaging-Even-Numbers) or, more realistically, data from a CSV file with many rows and columns.\n",
"\n",
"Processing numeric data usually comes down to operations that may be grouped into one of the following three categories:\n",
"\n",
"- **mapping**: transform a number according to some functional relationship $y = f(x)$\n",
"- **filtering**: throw away individual numbers (e.g., statistical outliers in a sample)\n",
"- **reducing**: collect individual numbers into summary statistics\n",
"\n",
"We study this **map-filter-reduce** paradigm extensively in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_lecture.ipynb#The-Map-Filter-Reduce-Paradigm) after introducing more advanced data types that are needed to work with \"big\" data.\n",
"\n",
"Here, we focus on *filtering out* some numbers in 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 in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` 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": 76,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]"
]
},
{
"cell_type": "code",
"execution_count": 77,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8 -> 65 12 -> 145 2 -> 5 6 -> 37 10 -> 101 4 -> 17 "
]
},
{
"data": {
"text/plain": [
"370"
]
},
"execution_count": 77,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"total = 0\n",
"\n",
"for number in numbers:\n",
" if number % 2 == 0: # only keep even numbers\n",
" square = (number ** 2) + 1\n",
" print(number, \"->\", square, end=\" \") # added for didactical purposes\n",
" total += square\n",
"\n",
"total"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The above code is easy to read as it involves only 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 compliant 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: Several Filters"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Calculate the sum of every third and even number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` 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": 78,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]"
]
},
{
"cell_type": "code",
"execution_count": 79,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8 -> 65 12 -> 145 4 -> 17 "
]
},
{
"data": {
"text/plain": [
"227"
]
},
"execution_count": 79,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"total = 0\n",
"\n",
"for i, number in enumerate(numbers, start=1):\n",
" if i % 3 == 0: # only keep every third number\n",
" if number % 2 == 0: # only keep even numbers\n",
" square = (number ** 2) + 1\n",
" print(number, \"->\", square, end=\" \") # added for didactical purposes \n",
" total += square\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 block. Of course, one could flatten 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_00_lecture.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic, and this is *not* a real improvement."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"#### The `continue` Statement"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A Pythonista would instead make use of the `continue` statement (cf., [reference](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement)) that causes a loop to jump into the next iteration skipping the rest of the code block.\n",
"\n",
"The revised code fragment below occupies more vertical space and less horizontal space: A *good* trade-off.\n",
"\n",
"One caveat is that we need to negate the conditions in the `if` statements. Conceptually, we are now filtering \"out\" and not \"in.\""
]
},
{
"cell_type": "code",
"execution_count": 80,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8 -> 65 12 -> 145 4 -> 17 "
]
},
{
"data": {
"text/plain": [
"227"
]
},
"execution_count": 80,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"total = 0\n",
"\n",
"for i, number in enumerate(numbers, start=1):\n",
" if i % 3 != 0: # only keep every third number\n",
" continue\n",
" elif number % 2 != 0: # only keep even numbers\n",
" continue\n",
"\n",
" square = (number ** 2) + 1\n",
" print(number, \"->\", square, end=\" \") # added for didactical purposes \n",
" total += square\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 cells do the *same* with *identical* time complexity. However, the latter is arguably easier to read for a human, even more so when the business logic grows beyond two filters."
]
},
{
"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 *cannot* 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 was typed in response as a `str` object. We use it to process a user's \"unreliable\" 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 particular event occurs, we `break` out of the loop.\n",
"\n",
"Let's look at a first and naive implementation."
]
},
{
"cell_type": "code",
"execution_count": 81,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"import random"
]
},
{
"cell_type": "code",
"execution_count": 82,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"random.seed(42)"
]
},
{
"cell_type": "code",
"execution_count": 83,
"metadata": {
"code_folding": [],
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: Heads\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ooops, it was tails\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: Heads\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ooops, it was heads\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: heads\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"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 exhibits two *severe* issues where we should improve on:\n",
"\n",
"1. If a user enters *anything* other than `\"heads\"` or `\"tails\"`, for example, `\"Heads\"` or `\"Tails\"`, the program keeps running *without* the user knowing about the mistake!\n",
"2. The code *intermingles* the coin tossing with the processing of the user's input: Mixing *unrelated* business logic in the *same* code block makes a program harder to read and, more importantly, 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*.\n",
"\n",
"First, we divide the business 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 preceding and trailing whitespace and lower case the input ensuring that the user may 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": 84,
"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()` models a fair coin toss when called with default arguments."
]
},
{
"cell_type": "code",
"execution_count": 85,
"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 handle the case where `get_guess()` returns `None` explicitly: Whenever the user enters something invalid, a warning is shown, and another try is granted. We use the `is` operator and not the `==` operator as `None` is a singleton object.\n",
"\n",
"The `while`-loop takes on the role of **glue code** that manages how other parts of the program interact with each other."
]
},
{
"cell_type": "code",
"execution_count": 86,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(42)"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: invalid\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Make sure to enter your guess correctly!\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: Heads\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"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, the program's business logic is expressed in a clearer way. More importantly, we can now change it more easily. 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, 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 to achieving that.\n",
"\n",
"First, we 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 **backward** reasoning might not be intuitive, but it turns out to be a handy 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 **forward** reasoning. The `for` statement is **syntactic sugar** that allows rewriting common occurrences of the `while` statement concisely. 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\" references to other objects in memory. On the contrary, the `range` type does not reference any other object but instead creates *new* `int` objects \"on the fly\" (i.e., when being looped over)."
]
}
],
"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.4"
},
"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": 4
}