{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lecture 13 - More on Functions\n", "\n", "## Overview, Objectives, and Key Terms\n", " \n", "In [Lecture 12](A08_Basics_of_Functions.ipynb), the basics of defining functions were presented. In this lecture, more advanced features of function definitions are presented, including the use of the special `*arg` and `**kwarg` structures. In addition, anonymous `lambda` functions are presented for quick, in-line applications. Finally, some interesting (and possibly suprising) applications of functions are considered, which include use of functions as arguments to other functions and the use of functions that call themselves (i.e., *recursion*). \n", "\n", "Note that much of the content of this lecture is focused primarily on features of functions specific to Python that provide a lot of flexibility. For many students, these features are likely not needed for routine programming, but an awareness of these features is useful when understanding the code of others.\n", "\n", "### Objectives\n", "\n", "By the end of this lesson, you should be able to\n", "\n", "- Define a function using `*args` and `**kwargs`.\n", "- Define a `lambda` function.\n", "- Define a recursive function.\n", "\n", "\n", "### Key Terms\n", "\n", "- `*args`\n", "- `**kwargs`\n", "- anonymous function\n", "- `lambda`\n", "- `filter`\n", "- recursion\n", "- recursive function\n", "- recursive sequence" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## `*args` and `**kwargs`\n", "\n", "What on earth are `*args` and `**kwargs`? Before moving along, let's look at a function introduced back in [Lecture 3](L03_BasicDataProcessingWithNumPyAndMatplotlib.ipynb): `plt.plot`. If you forgot how to use it, just use `help`:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```python\n", "import matplotlib.pyplot as plt\n", "help(plt.plot)\n", "```" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "The output is a bit long, but here are the first several lines:\n", "\n", "```\n", "Help on function plot in module matplotlib.pyplot:\n", "\n", "plot(*args, **kwargs)\n", " Plot lines and/or markers to the\n", " :class:`~matplotlib.axes.Axes`. *args* is a variable length\n", " argument, allowing for multiple *x*, *y* pairs with an\n", " optional format string. For example, each of the following is\n", " legal::\n", " \n", " plot(x, y) # plot x and y using default line style and color\n", " plot(x, y, 'bo') # plot x and y using blue circle markers\n", " plot(y) # plot y using x as index array 0..N-1\n", " plot(y, 'r+') # ditto, but with red plusses\n", " \n", " If *x* and/or *y* is 2-dimensional, then the corresponding columns\n", " will be plotted.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wait, consider the line `plot(*args, **kwargs)`. Those arguments do not look like `(x, y)` or `(x, y, x, z)` or `(x, y, 'k-', x, z, 'r--')`, similar to what we've previously used. Indeed, `*args` and `**kwargs` represent two different ways to pass arguments. \n", "\n", "In [Lecture 12](A08_Basics_of_Functions.ipynb), functions were defined with a pretty specific structure, e.g., \n", "```python\n", " def some_function(arg1, arg2 = default2, arg3 = default3):\n", " # do stuff and maybe return a value \n", " # (but don't have only comments like these!)\n", "```\n", "Such a function has arguments explicitly listed, and sometimes, they are given default values. \n", "\n", "For a function like `plt.plot`, something else is happening, and the arguments that we pass (like `x` and `y`) are somehow being passed in `*args*` or `**kwargs`. Perhaps, before explaining the details, it is worth considering an example. First, define the following three functions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def foo(*args) :\n", " print(args)\n", "\n", "def goo(**kwargs) :\n", " print(kwargs)\n", " \n", "def hoo(a, b, c) :\n", " print(a, b, c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's call `foo` with the three arguments 1, 2, and 3:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo(1, 2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That output looks like like a `tuple` containing the values provided as individual arguments. Moreover, the order of the values in the `tuple` is the same as the order in which they were passed.\n", "\n", "> **Exercise**: Modify `foo` with a second `print` statement to verify that the type of `args` is, in fact, a `tuple`.\n", "\n", "Out of curiosity, what if we pass just one value to `foo`? Or two?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo(1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "foo(1, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The observation is the same: a `tuple` whose values are the arguments given.\n", "\n", "Now, let's try the same with `goo`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "goo() takes 0 positional arguments but 3 were given", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mgoo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: goo() takes 0 positional arguments but 3 were given" ] } ], "source": [ "goo(1, 2, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Interesting. The error indicates that `**kwargs` is different from `*args`. What does the error mean, though? \n", "\n", "When we pass an argument to a function without naming it, the argument is called a **positional argument**. In other words, Python assigns the value to the appropriate name based on the position of the value relative to the other arguments. For example, Python knows that when one calls `hoo(1, 2, 3)` the value `1` should be assigned to `a` because `1` is the first values passed, and `a` is the first argument named in the `def` statement.\n", "\n", "If an argument is not **positional**, then it is a **keyword argument**. As seen in [Lecture 12](A08_Basics_of_Functions.ipynb), keyword arguments are named arguments. The error suggests that `goo` requires *only* keyword arguments, so let's try the following:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "goo(a=1, b=2, c=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That worked, and the output suggests that `kwargs` is a `dict` whose keys and values are the names and values of the arguments given as input.\n", "\n", "> **Exercise**: Modify `goo` with a second `print` statement to verify that the type of `kwargs` is, in fact, a `dict`.\n", "\n", "Take a moment to consider what `*args` and `**kwargs` let one do. A function whose argument is `*args` can be passed *any number of positional arguments*. Maybe these arguments look like `(x, x**2, 'r--', x, x**3, x, x**4, 'bx')`, like those used for `plt.plot`.\n", "\n", "Likewise, a function whose argument is `**kwargs` can be passed *any number of keywork (i.e., named) arguments*. Maybe these arguments look like `(lw=2, color='black', label = 'line 1')`, which are common options used for `plt.plot`.\n", "\n", "To make these examples concrete, consider the following:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "image/png": "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\nPj297W1iyTN3AlVVMGeO+zbGmETKyIApU+DccxMdiQeSQF2dewGjsND101FY6Obr6hIdmTHGJF5K\nJ4Hly9MpKoLp06GmxrXNralx80VF4OsFOqrWr1/P5MmTueyyy3j00UejvwNjjImilEwCGze6HqIn\nTMhm82bXT4e/fftcB01jx8Lw4VBeHr19n3TSSTz22GPMnz+flStXRu+HjTEmBlIuCZSWuifub74J\n+/e33s30/v1QVuYezpSWRjOGUi6++GIuuuii6P2oMaZDee45eOop935AMku5JLB2rav2qa9ve11w\n6x044LZrr2AjiwGMHj2aV199lWeffbb9P2qM6fCqq92LYVdfDWec4d4UTlYp0UR05kyItBPPmTPd\npz2DXgYbWaysrIwXX3yRQ4cO2Z2AMR51112wc6ebXrMGvvUtGD8enn46/r2EtiXJwulYgo0sVlJS\nQklJSaJDM8YkyCefwP/+b2C5SPIlAEjB6qB4spHFjDHN3XRTYBP07Gy4777ExNMWSwJhspHFjDHN\nLVkCL78cWH7LLXDccfGPJxSWBMJgI4sZY5o7fBh++tPA8t69XRJIVimRBBof6Kq66Uh+IxQ2spgx\nprnHHoOPPgosv+8+yMmJfzyhSokk4K+42NW/hdopU3q6W7+4OLZxGWNS15dfwh13BJaffTZccUX8\n42mPlEsCo0e7HvpKSiAnp/VL+5wcGDoU1q1z2xljTDhmznSJoLnZs12roGSWckkAXL9Ay5bBH/94\ngD59IDe36fLcXOjTx408uXQp9OuXmDiNMR3fRx/BI48Elk+cCKefHv942islk0Cj88+vp7wc7rkH\n8vJctU9enpsvL3f9ehtjTLhU3cPg5j0U5Oa680xHkISvLoRGVZEQ7rMyMtwgzhMmwIIF8N3vQn5+\nHAJsgbbnlWRjTFJ75RXXLLS5228PPpRkMuqQdwJZWVlUVVW164Sanw+TJiU+AVRVVZGVlZW4IIwx\nUVFbCz/7WWB5YWHw8mQV0Z2AiPwU+C9AgfeBHwI5wPNAIVABXK6qX/nWnwZcA9QDU1X19XD227t3\nbyorK9nZ2DlHCw4ePJh0J9ysrCx69+6d6DCMMRF66CHXbX1z998PSXbaaVXYSUBEegFTgQGqekBE\n5gPjgQHAclW9V0RuA24DbhWRAb7lJwNfB5aJyP9R1RD7+zwiMzOTvn37trleWVkZxdb20xgTZTt3\n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\nAsYY42H/H9nNtmICemU9AAAAAElFTkSuQmCC\n" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "x = np.linspace(0, 10, 10)\n", "# \n", "plt.plot(x, x**2, '-*', color='red', linewidth=2, label='$x^2$', markersize=10)\n", "plt.plot(x, x**3, '--h', color='blue', linewidth=5, label='$x^3$', markersize=15)\n", "plt.legend() # automatically uses the labels\n", "plt.grid(True)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In summary, `*args` and `**kwargs` are extremely powerful constructs, and when used together, let one write functions that can handle an arbitrary number of arguments. You'll probably encounter these constructs most often when inspecting functions defined in modules like `matplotlib.pyplot`, so knowing what they are and how to use them can be very useful when learning to use such functions.\n", "\n", "> **Exercise**: Define a function `ideal_gas(**kwargs)` that implements the ideal gas law $PV = nRT$. Specifically, a user should enters just three of the four physical quantities $P$, $V$, $n$, and $T$, and the fourth should be computed. Assume that $R = 8.314$ J/K mol. In addition to applying the `**kwargs` construct, this exercise flexes what you remember about `if` statements and the `dict` type.\n", "\n", "> **Exercise**: Define a function `are_they_bool(*args)` that returns a `list` whose length is equal to the number of arguments passed. The `i`th element of this `list` is `True` if the `i`th argument is (logically equal to) `True` and is otherwise `False`.\n", "\n", ">> *Solution*:\n", ">>\n", "```python\n", "def are_they_bool(*args):\n", " # We need to return a list, so start of by \n", " # initializing an empty list.\n", " rvalue = []\n", " # Go through each of the arguments\n", " for i in range(len(args)):\n", " # Is this the ith argument a bool?\n", " is_it_bool = bool(args[i])\n", " # Add it to the list we'll be returning.\n", " rvalue.append(is_it_bool)\n", " return rvalue \n", "# Test it with\n", "are_they_bool(1, 0, 5, '')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Anonymous Functions\n", "\n", "So far, functions have been defined by a `def` statement. However, that's not the only way to define functions in Python, and for some applications, a shorter (if not simpler) approach is useful.\n", "\n", "### A simple `lambda`\n", "\n", "To get the syntax down, consider first a function to square a number. We could define that using `def` as" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def square_it(x):\n", " return x*x\n", "square_it(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same functionality can be defined using" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "square_it_lambda = lambda x: x**2\n", "square_it_lambda(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The syntax for anonymous functions starts with the keyword `lambda`, followed by one or more argument names, a colon `:`, and the expression to be returned. Why are such functions called anonymous functions? *Because they have no real name*:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(square_it)\n", "print(square_it_lambda)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In practice, `lambda` functions are the only way to define a function as part of a single expression. For example, one can do the following:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(lambda x: x**2)(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is strange syntax, for sure, but once `(lambda x: x**2)` is recognized as a *callable* function, the syntax is a bit clearer. The important point, though, is that `lambda` functions can be part of expressions, and expression include function calls." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### When `lambda` is more natural\n", "\n", "A richer example, of course, helps motivate the utility of `lambda`. Consider the built-in `filter` function:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "help(filter)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you wade through all that output, notice this line:\n", "\n", "```\n", "Return an iterator yielding those items of iterable for which function(item)\n", "is true. If function is None, return the items that are true.\n", "```\n", "\n", "Don't worry about what an *iterator* is; like `range()` and `dict.keys()`, it can be converted into a `list`. The important point is that `filter` applies a filtering `function` to all the items of a sequence and returns only those elements that pass the filter. Such filtering has *lots* of applications, which is why the functionality is built into Python. The problem is that each application requires different filtering functions.\n", "\n", "Consider a class with lots of students and lots of assignments (and assignment categories). A common task (or it should be anyway) for an instructor is to check how performance in one category impacts performance in another category. For example, can a student do very well on homework but do very badly on the exam? Lots of conclusions can be made if only this question could be answered, and it can be answered using, of course, `filter`. For concreteness, consider the following category scores (and some notes) for several students:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "records = [\n", " {'name': 'John von Neumann', 'hw': 68.0, 'labs': 94.3, 'quiz': 100.0, 'exam': 98.5, 'notes': 'Invented merge sort.'},\n", " {'name': 'Alan Turing', 'hw': 90.5, 'labs': 33.3, 'quiz': 91.0, 'exam': 69.5, 'notes': 'Developed the \"Turing Test\" for AI.'},\n", " {'name': 'Ada Lovelace', 'hw': 94.0, 'labs': 88.0, 'quiz': 94.0, 'exam': 100.0, 'notes': 'First computer programmer.'},\n", " {'name': 'Donald Knuth', 'hw': 72.0, 'labs': 98.0, 'quiz': 74.0, 'exam': 79.0, 'notes': 'Invented TeX.'},\n", " {'name': 'Grace Hopper', 'hw': 96.0, 'labs': 56.0, 'quiz': 82.0, 'exam': 83.0, 'notes': 'Popularized machine-independent languages (of which Python is an example)'}\n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This sort of information is exactly the kind an instructor might have from Canvas (or other course-management systems). Now, the question, who does very well on the homeworks (say, over 90% or an \"A\") but does *not* do well on exams? Each item in `records` is itself a `dict`, and we could write the following function to return such an item only if it meets the criteria:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def good_hw_bad_exam(item):\n", " if item['hw'] >= 90.0 and item['exam'] < 70.0:\n", " return True\n", " else:\n", " pass # i.e., return None (equivalent to False)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list(filter(good_hw_bad_exam, records)) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah, our friend Alan Turing meets the criteria. He has done quite well on the homework (an \"A\"), but his exam score is less than satisfying. Also worth noting: *we passed a function name as an argument*. \n", "\n", "> **Note**: A function can be passed as an argument to other functions.\n", "\n", "Alright, alright, but what about **anonymous functions**? Let's filter that data for high exam scores and low homework scores (i.e., the lazy but smart student; yes, they exist). Although a new `def` statement could be written, a much faster approach is `lambda`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list(filter(lambda item: item['exam'] >= 90 and item['hw'] < 70, records))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hence, the full `lambda` function accepts the argument `item` and returns `True` if it passes the filter. The reason `lambda` is useful here is because the filtering criteria can be changed on the fly. For example, one could check for several different patterns:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# shows up for labs, but not as much for exams\n", "list(filter(lambda item: item['labs'] >= 90 and item['exam'] < 80, records))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# wows on the quizzes, but not on exams\n", "list(filter(lambda item: item['quiz'] >= 90 and item['exam'] < 80, records))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hence, rather than defining new functions using `def`, one can simply define a `lambda` when needed as part of one expression." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Exercise**: Define a `lambda` function that doubles a values.\n", "\n", "> **Exercise**: Define a `lambda` function that adds two values.\n", "\n", ">> *Solution*: `lambda x, y: x + y`\n", "\n", "> **Exercise**: The function `map` is called as `map(func, seq)`, where `func` is a function to apply to each element of a sequence `seq`. By using `list(map(func, seq))`, one gets a list of the values `func(seq[i])`. Use `map` and a `lambda` function to produce a list of `True` or `False` indicating whether the elements of a list of integers is even or odd.\n", "\n", ">> *Solution*: `list(map(lambda x: x % 2 == 0, [1, 2, 3, 4]))`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Recursive Functions\n", "\n", "The topic of **recursion** is included here almost entirely for the sake of completion. Before we dive into what recursion is, note that in much of scientific computing, recursion can be replaced by appropriate loops.\n", "\n", "> **Note**: Recursion can often be replaced by loops.\n", "\n", "Now, **recursion** is a process in which output (say, a particular value) is computed at any particular step using the output from one or more previous steps using a fixed relationship. If this output is a single value, then the process yields an $m$-term, **recursive sequence** of the form $a_n = f(a_{n-1}, a_{n-2}, \\ldots, a_{n-m}$. Here, $f$ is an arbitrary function of the past $m$ values in the sequence.\n", "\n", "The basic structure of a **recursive function** can be illustrated by the following function:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def a_basic_recursive_function(n):\n", " \"\"\"This function does something simple, but it\n", " might not be immediately obvious.\"\"\"\n", " if n == 1:\n", " return 1\n", " else:\n", " return n + a_basic_recursive_function(n-1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for n in range(1, 6):\n", " print(\"For n={}, the function returns {}\".format(n, a_basic_recursive_function(n)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do you see the pattern?\n", "\n", "> **Exercise**: Before moving on, try to determine what happens in this function by adding a `print` statement after the `else` clause to display the value of `n`.\n", "\n", "What function gives 1 when given 1, 3 when given 2, and 6 when given 3? A function that adds the integers from 1 to $n$, just like we've done before using loops. The sequence is simple: $a_n = f(a_{n-1}) = a_{n-1} + n$ with $a_0 = 1$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Exercise**: Implement the factorial function using recursion.\n", "\n", "> **Exercise**: Use recursion to produce the list `[a, 2, 3, 4, ..., b]` for positive integers `a` and `b`. You may assume that `b >= a`.\n", "\n", ">> *Solution*:\n", ">>\n", "```python \n", "def recursive_range(a, b):\n", " if a == b:\n", " return [a]\n", " else:\n", " return recursive_range(a, b-1) + [b]\n", "```\n", "> Remember, `[1] + [2]` leads to `[1, 2]`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A recursive function must always have an effective termination criterion that prevents the function from being called again. Sometimes this is called a *base case* or a *guard*. For our summation example, that condition is `n == 1`. When we add the integers from 1 through 5, the recursive function does so like this: $5 + (4 + (3 + (2 + (1))))$, where the parentheses indicate a new call to the recursive function. Here, these calls terminate at $(1)$. If we forgot to terminate when `n == 1`, the function would keep calling itself over and over. It turns out that Python limits the number of times this can happen:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "ename": "RecursionError", "evalue": "maximum recursion depth exceeded", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minfinite_recursion\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[1;32m 2\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0minfinite_recursion\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[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minfinite_recursion\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36minfinite_recursion\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minfinite_recursion\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----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0minfinite_recursion\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[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minfinite_recursion\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "... last 1 frames repeated, from the frame below ...\n", "\u001b[0;32m\u001b[0m in \u001b[0;36minfinite_recursion\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minfinite_recursion\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----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0minfinite_recursion\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[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minfinite_recursion\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" ] } ], "source": [ "def infinite_recursion(n):\n", " return n + infinite_recursion(n-1)\n", "print(infinite_recursion(5))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's not obvious what this maximum depth (i.e., number of calls) is, but it's probably pretty big.\n", "\n", "> **Warning**: Like loops, recursive functions need a termination criterion. For recursive sequences, thats usually when the recursion gets to the initial values of the sequence, e.g., $a_0 = 1$ for the sum of the integers from 1 to $n$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A Famous Example\n", "\n", "Remember the Fibonacci sequence: $1, 1, 2, 3, 5, 8, \\ldots$. It starts with two 1's, but all of the subsequent terms have a special pattern: they are the sum of the two previous terms. In other words, one can define the Fibbonaci sequence as $a_{n} = a_{n-1} + a_{n-2}$ given $a_0 = a_1 = 1$. This is a two-term, recursive sequence. Of course, defining the $n$th term is easy using a simple loop. Here, that approach is provided as a function:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def fibo_loop(n):\n", " \"\"\"Compute the nth term in Fibonacci's sequence using loops.\"\"\"\n", " # initialize all three terms a_n, a_{n-1}, and a_{n-2}\n", " a_n, a_1, a_2 = 1, 1, 1\n", " # initialize the coutner\n", " i = 2\n", " while i <= n:\n", " # apply the sequence\n", " a_n = a_1 + a_2\n", " # and then store the old values\n", " a_2 = a_1\n", " a_1 = a_n\n", " # always increment the counter\n", " i += 1\n", " return a_n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fibo_loop(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Fibonacci sequence is recursive. Each term $a_n$ depends on the two previous terms $a_{n-1}$ and $a_{n-2}$. Hence, the process to compute $a_n$ looks just like the process to compute $a_{n-1}$, all the way down to the first to values $a_0$ and $a_1$. Consider the case of $n = 5$, for which $a_5$ is 8. Let's break it down:\n", "\n", "$$\n", "\\begin{split}\n", "8 &= 3 + 5 \\\\\n", " &= (1_1 + 2) + (2 + 3) \\\\\n", " &= (1_1 + (1_0 + 1_1)) + ((1_0 + 1_1) + (1_1 + 2))\\\\\n", " &= (1_1 + (1_0 + 1_1)) + ((1_0 + 1_1) + (1_1 + (1_0 + 1_1)))\\\\\n", "\\end{split}\n", "$$\n", "\n", "Here, the subscripts on the 1's indicate whether they are $a_0$ or $a_1$. The conclusion is that every single term in the sequence is just a sum of multiple 1's. If we were to write this using functions, \n", "\n", "```\n", " 8 = fibo(3) + fibo(4)\n", " = (fibo(1) + fibo(2) + (fibo(2) + fibo(3))\n", " = (fibo(1) + (fibo(0)+fibo(1))) + ((fibo(0)+fibo(1)) + (fibo(1)+fibo(2)))\n", " = (fibo(1) + (fibo(0)+fibo(1))) + ((fibo(0)+fibo(1)) + (fibo(1)+(fibo(0)+fibo(1))))\n", "```\n", "\n", "What this means is that calling `fibo(3)` ought to return the sum of `fibo(1)` and `fibo(2)`, and that the call `fibo(2)` ought to return the sum of `fibo(0)` and `fibo(1)`. The only time calling `fibo(n)` doesn't require calling `fibo(n-1)` and `fibo(n-2)` is when `n` is 0 or 1, i.e., when the value to be returned is 1.\n", "\n", "Here is this idea packaged into the **recursive function** `fibo_recursive`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def fibo_recursive(n):\n", " if n < 2: # again, we need a termination criterion.\n", " return 1\n", " else:\n", " return fibo_recursive(n-2) + fibo_recursive(n-1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fibo_recursive(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Take a moment and process this function. Test it out (in Spyder or by hand) and verify that it works.\n", "\n", "> **Exercise**: Implement `fibo_recursive` in Spyder. For $n = 5$, use the graphical debugger to trace the output.\n", "\n", "> **Exercise**: Consider two integers $a > 0$ and $b > 0$. Their [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) (call it $gcd(a, b)$) is the largest integer that divides both of them. One way to compute $gcd(a, b)$ is the [Euclidean algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm). Using modular arithmetic, this algorithm can be summarized by the equations $gcd(a, b) = gcd(b, a\\,\\text{mod}\\, b)$ and $gcd(a, 0) = a$. Remember $a\\,\\text{mod}\\, b$ is equivalent to the Python `a % b`. Use these equations to implement a recursive Python function `gcd(a, b)`. You can check your answer using `math.gcd`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Further Reading\n", "\n", "A pretty neat overview of recursion can be found [here](https://medium.freecodecamp.org/how-recursion-works-explained-with-flowcharts-and-a-video-de61f40cb7f9)." ] } ], "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.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }