{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Recursion\n", "\n", "We are no longer in the Google Python Course.\n", "\n", "\n", "\n", "\n", "#### Recursion allows you to define infinite objects, finitely\n", "\n", "We may define the positive integers as follows:\n", "\n", "> The number 1 is a positive integer.\n", "\n", "> Any positive integer plus 1 is a positive integer.\n", "\n", "Below we define a generator for integers. (We will discuss generators later.)\n", "\n", "These examples are found in the python file recursion.py" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "def integers(n):\n", " yield n\n", " while True:\n", " n +=1\n", " yield n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "intg = integers(1)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "generator" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(intg)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(intg)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(intg)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(intg)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "newintg = integers(1000)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1000" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(newintg)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1001" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(newintg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We may extend the definition to include all integers.\n", "\n", "\n", "> Any positive integer is an integer.\n", "\n", "> Any integer minus 1 is an integer.\n", "\n", "### Recursive Data Structures\n", "\n", "Lists and strings are recursive data structures.\n", "\n", "> The empty list [] is a list.\n", "\n", "> Appending an item to a list results in a list.\n", "\n", "Similarly for strings.\n", "\n", "> The empty string \"\" is a string.\n", "\n", "> Concatenating a character to a string results in a string.\n", "\n", "We will see similar definitions for other data structures, including stacks, queues, \n", "trees, and graphs. FYI, the Facebook friends network is a graph.\n", "\n", "Given a recursive data structure, it is convenient to use recursive functions to process the recursive structures. The two hallmarks of a recursive function are \n", "\n", "> A base case, such as the empty list or the empty string.\n", "\n", "> A traversal function for moving through the data structure.\n", "\n", "\n", "Below we compare recursion to iteration and introduce tail recursion as a more efficient version.\n", "\n", "\n", "### Iterative versions of total [sum], length (len), and max." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def itotal(lst):\n", " sum = 0\n", " for n in lst:\n", " sum += n\n", " return sum" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def ilength(lst):\n", " length = 0\n", " for i in lst:\n", " length += 1\n", " return length" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def imax(lst):\n", " m = lst[0]\n", " for n in lst[1:]:\n", " if n > m:\n", " m = n\n", " return m" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "l = list(range(1,6))" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 5]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "itotal(l)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ilength(l)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "imax(l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Recursive versions of total [sum], length [len], and max\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def rtotal(lst):\n", " if not lst:\n", " return 0\n", " else:\n", " return lst[0] + rtotal(lst[1:])" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "def rlength(lst):\n", " if not lst:\n", " return 0\n", " else:\n", " return 1 + rlength(lst[1:])" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "def rmax(lst):\n", " if not lst:\n", " return float('-inf')\n", " else:\n", " return max(lst[0], rmax(lst[1:]))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "l = list(range(1,6))" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 5]" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sum(l)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rtotal(l)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(l)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rlength(l)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "max(l)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rmax(l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below we define trace() which allows us watch the execution of recursive functions. Later we will see that trace() can be used as a decorator." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "def trace(f):\n", " f.indent = 0\n", " def g(x):\n", " print('| ' * f.indent + '|--', f.__name__, x)\n", " f.indent += 1\n", " value = f(x)\n", " print('| ' * f.indent + '|--', 'return', repr(value))\n", " f.indent -= 1\n", " return value\n", " return g" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "rtotal = trace(rtotal)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "|-- rtotal [1, 2, 3, 4, 5]\n", "| |-- rtotal [2, 3, 4, 5]\n", "| | |-- rtotal [3, 4, 5]\n", "| | | |-- rtotal [4, 5]\n", "| | | | |-- rtotal [5]\n", "| | | | | |-- rtotal []\n", "| | | | | | |-- return 0\n", "| | | | | |-- return 5\n", "| | | | |-- return 9\n", "| | | |-- return 12\n", "| | |-- return 14\n", "| |-- return 15\n" ] }, { "data": { "text/plain": [ "15" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rtotal(l)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "rlength = trace(rlength)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "|-- rlength [1, 2, 3, 4, 5]\n", "| |-- rlength [2, 3, 4, 5]\n", "| | |-- rlength [3, 4, 5]\n", "| | | |-- rlength [4, 5]\n", "| | | | |-- rlength [5]\n", "| | | | | |-- rlength []\n", "| | | | | | |-- return 0\n", "| | | | | |-- return 1\n", "| | | | |-- return 2\n", "| | | |-- return 3\n", "| | |-- return 4\n", "| |-- return 5\n" ] }, { "data": { "text/plain": [ "5" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rlength(l)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "rmax = trace(rmax)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "|-- rmax [1, 2, 3, 4, 5]\n", "| |-- rmax [2, 3, 4, 5]\n", "| | |-- rmax [3, 4, 5]\n", "| | | |-- rmax [4, 5]\n", "| | | | |-- rmax [5]\n", "| | | | | |-- rmax []\n", "| | | | | | |-- return -inf\n", "| | | | | |-- return 5\n", "| | | | |-- return 5\n", "| | | |-- return 5\n", "| | |-- return 5\n", "| |-- return 5\n" ] }, { "data": { "text/plain": [ "5" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rmax(l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "(define grammar-mcd\n", " (cfg\n", " '(a the mouse cat dog it slept swam chased evaded dreamed believed that)\n", " '(s np vp det n pn vi vt v3)\n", " 's\n", " (list\n", " (rule 's '(np vp))\n", " (rule 'np '(det n))\n", " (rule 'np '(pn))\n", " (rule 'det '(a))\n", " (rule 'det '(the))\n", " (rule 'n '(mouse))\n", " (rule 'n '(cat))\n", " (rule 'n '(dog))\n", " (rule 'pn '(it))\n", " (rule 'vp '(vi))\n", " (rule 'vp '(vt np))\n", " (rule 'vp '(v3 that s))\n", " (rule 'vi '(slept))\n", " (rule 'vi '(swam))\n", " (rule 'vt '(chased))\n", " (rule 'vt '(evaded))\n", " (rule 'v3 '(dreamed))\n", " (rule 'v3 '(believed)))))\n", "\n", "\n", "We interpret the grammar as follows:\n", "\n", "Terminal values: a the mouse cat dog it slept swam chased evaded dreamed believed that\n", "\n", "Non-terminal values: s np vp det n pn vi vt v3\n", "\n", "Rewrite rules: (the left side can be rewritten as the right side)\n", "\n", "
\n", "s : np vp # a sentence is a noun phrase followed by a verb phrase\n", "np : det n # a noun phrase can be a determiner followed by a noun\n", "np : pn # a noun can be a pronoun\n", "det : a # a determiner can be a\n", "det : the # a determiner can be the\n", "n : mouse # a noun can be mouse\n", "n : cat # a noun can be cat\n", "n : dog # a noun can be dog\n", "pn : it # a pronoun can be it\n", "vp : vi # a verb phrase can be an intransitive verb\n", "vp : vt np # a verb phrase can be a transitive verb followed by a noun phrase\n", "vp : v3 that s # a verb phrase can be a v3 verb followed by \"that\" followed by a sentence\n", "vi : slept # an intransitive verb can be slept\n", "vi : swam # an intransitive verb can be swam\n", "vt : chased # a transitive verb can be chased\n", "vt : evaded # a transitive verb can be evaded\n", "v3: dreamed # a v3 verb can be dreamed\n", "v3 : believed # a v3 verb can be believed\n", "\n", "\n", "We can derive a sentence by expanding each non-terminal node of a tree until no non-terminal nodes remain. This is a recursive process.\n", "\n", "
\n", "S -> NP VP\n", "NP -> DET N\n", "DET -> the\n", "N -> cat\n", "VP -> VT NP\n", "VT -> chased\n", "NP -> PN\n", "PN -> it\n", "\n", "the cat chased it\n", "\n", "\n", "Note that the v3 rule results in a recursive call to the sentence node." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This type of grammar, with a single non-terminal on the left hand side, is \n", "known as a context free grammar. There is a more restrictive type of grammar known as regular expressions, which we will examine later.\n", "\n", "There are also less restrictive grammars including context sensitive grammars and recursively enumerable languages. We will not be discussing those.\n", "\n", "They all fall under the general category of formal languages.\n", "\n", "Context free grammars are especially interesting for computer science as they provide a grammatical structure for most computer programming languages. The common name for these grammatical descriptions of programming languages is Backus Naur Form or BNF. John Backus designed FORTRAN and Peter Naur designed ALGOL.\n", "\n", "See http://matt.might.net/articles/grammars-bnf-ebnf/ for a discussion of the language of languages." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "End of Recursion notebook, for now. Later we will discuss deep recursion." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" } }, "nbformat": 4, "nbformat_minor": 4 }