{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "### CS 200: Deep Recursion\n", "\n", "\n", "

\n", "\n", "In regular recursion, we would recurse over the top level elements of a list, string, or tuple.\n", "\n", "There are other recursive data structures that have multiple levels, for example, trees.\n", "\n", "A tree, like the UNIX file system, has a root node. Each node can contain terminals nodes, called \"leaves\", or other nodes or branches. These nodes are called children. The main node is the parent. For example, in a UNIX directory, .. refers to the parent directory and . refers to the child directory. If a UNIX program spawns a process, the new process is the child and the originating process is the parent. (In C, you use the fork command to create a child process.)\n", "\n", "The recursive function must process each node and all the children nodes.\n", "\n", "Our first example procedure is maptree(proc, tree) which creates a copy of the given tree, with each leaf node replaced by proc(leafnode)\n", "\n", "The base case is when a node is empty, an integer, or some other leaf node.\n", "\n", "The recursive case is a list, in which the function is called recursively on each element of the list, and returns a copy of the list with appropriate modifications.\n", "\n", "The functions in this notebook are from recursion.py\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1', '2', '3', '4']" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(str,[1,2,3,4]))" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, 9, 16]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda x: x*x , [1,2,3,4]))" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1', '2', '[3, 4]']" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(str, [1,2,[3,4]]))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "list" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type([])" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def maptree(proc,tree):\n", " if tree == []:\n", " return tree\n", " if not type(tree) == type([]):\n", " return proc(tree)\n", " result = []\n", " for c in tree:\n", " result.append(maptree(proc, c))\n", " return result" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "l = [1,2,[3,4,[5,6],'a', 'b', []]]" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, [3, 4, [5, 6], 'a', 'b', []]]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1', '2', ['3', '4', ['5', '6'], 'a', 'b', []]]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "maptree(str, l) ## convert every leaf into a string" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def square(x):\n", " if type(x) == int:\n", " return x*x\n", " else:\n", " return x" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "maptree(square, l)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, [3, 4, [5, 6], 'a', 'b', []]]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(1, 1),\n", " (2, 2),\n", " [(3, 3), (4, 4), [(5, 5), (6, 6)], ('a', 'a'), ('b', 'b'), []]]" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "maptree(lambda x: (x,x), l)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(1)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "maptree(lambda x: x*x if type(x) == type(1) else x, l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will redefine maptree using a list comprehension" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "def lcmaptree(proc,tree):\n", " if tree == []:\n", " return tree\n", " if not type(tree) == type([]):\n", " return proc(tree)\n", " return [lcmaptree(proc,x) for x in tree]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1', '2', ['3', '4', ['5', '6'], 'a', 'b', []]]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lcmaptree(str, l)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lcmaptree(square, l)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(1, 1),\n", " (2, 2),\n", " [(3, 3), (4, 4), [(5, 5), (6, 6)], ('a', 'a'), ('b', 'b'), []]]" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lcmaptree(lambda x: (x,x), l)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lcmaptree(lambda x: x*x if type(x) == type(1) else x, l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a nested list comprehension version of maptree." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "def lclcmaptree(proc,tree):\n", " return [[lclcmaptree(proc,x) for x in t] if type(t) == list\n", " else t if t == [] else proc(t) for t in tree]" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "'int' object is not iterable", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m/tmp/ipykernel_653364/3129645351.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlclcmaptree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ml\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/tmp/ipykernel_653364/3375282614.py\u001b[0m in \u001b[0;36mlclcmaptree\u001b[0;34m(proc, tree)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mlclcmaptree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtree\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----> 2\u001b[0;31m return [[lclcmaptree(proc,x) for x in t] if type(t) == list\n\u001b[0m\u001b[1;32m 3\u001b[0m else t if t == [] else proc(t) for t in tree]\n", "\u001b[0;32m/tmp/ipykernel_653364/3375282614.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mlclcmaptree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtree\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----> 2\u001b[0;31m return [[lclcmaptree(proc,x) for x in t] if type(t) == list\n\u001b[0m\u001b[1;32m 3\u001b[0m else t if t == [] else proc(t) for t in tree]\n", "\u001b[0;32m/tmp/ipykernel_653364/3375282614.py\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mlclcmaptree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtree\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----> 2\u001b[0;31m return [[lclcmaptree(proc,x) for x in t] if type(t) == list\n\u001b[0m\u001b[1;32m 3\u001b[0m else t if t == [] else proc(t) for t in tree]\n", "\u001b[0;32m/tmp/ipykernel_653364/3375282614.py\u001b[0m in \u001b[0;36mlclcmaptree\u001b[0;34m(proc, tree)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mlclcmaptree\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mproc\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtree\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----> 2\u001b[0;31m return [[lclcmaptree(proc,x) for x in t] if type(t) == list\n\u001b[0m\u001b[1;32m 3\u001b[0m else t if t == [] else proc(t) for t in tree]\n", "\u001b[0;31mTypeError\u001b[0m: 'int' object is not iterable" ] } ], "source": [ "lclcmaptree(str, l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oops. This does not quite work. I invite you to fix it\n", "\n", "We can also use the map function to traverse trees." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "def mapmaptree(proc,tree):\n", " if tree == []:\n", " return tree\n", " if not type(tree) == type([]):\n", " return proc(tree)\n", " return list(map(lambda x: mapmaptree(proc,x),tree))" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1', '2', ['3', '4', ['5', '6'], 'a', 'b', []]]" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mapmaptree(str, l)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mapmaptree(square, l)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(1, 1),\n", " (2, 2),\n", " [(3, 3), (4, 4), [(5, 5), (6, 6)], ('a', 'a'), ('b', 'b'), []]]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mapmaptree(lambda x: (x,x), l)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mapmaptree(lambda x: x*x if type(x) == type(1) else x, l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yet another attempt. This time I combine map with recursion." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "def rmaptree(proc,tree):\n", " if tree == []:\n", " return tree\n", " if not type(tree) == type([]):\n", " return proc(tree)\n", " result = []\n", " result.append(rmaptree(proc, tree[0]))\n", " result.extend(rmaptree(proc, tree[1:]))\n", " return result" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1', '2', ['3', '4', ['5', '6'], 'a', 'b', []]]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rmaptree(str, l)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rmaptree(square , l)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(1, 1),\n", " (2, 2),\n", " [(3, 3), (4, 4), [(5, 5), (6, 6)], ('a', 'a'), ('b', 'b'), []]]" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rmaptree(lambda x: (x,x), l)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 4, [9, 16, [25, 36], 'a', 'b', []]]" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rmaptree(lambda x: x*x if type(x) == type(1) else x, l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will now look at a deep recursive function that does not return a copy of the tree. It simply returns the number of leaf nodes in the tree." ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "def countleaves(tree):\n", " if tree == []:\n", " return 0\n", " if not type(tree) == list:\n", " return 1\n", " else: \n", " return sum(map(countleaves,tree))" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "countleaves([1,2,3,4])" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "countleaves([[[]]])" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "countleaves([[[1,2,3]]])" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "countleaves(['a',['b',['c','d']]])" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "def rcountleaves(tree):\n", " if tree == []:\n", " return 0\n", " if not type(tree) == type([]):\n", " return 1\n", " else:\n", " return rcountleaves(tree[0]) + rcountleaves(tree[1:])" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rcountleaves([1,2,3,4])" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rcountleaves([[[[[]]]]])" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rcountleaves([[[1,2,3],[4,5,6]]])" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "def lccountleaves(tree):\n", " if tree == []:\n", " return 0\n", " if type(tree) != list:\n", " return 1\n", " else:\n", " return sum([lccountleaves(x) for x in tree])" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lccountleaves([1,2,3])" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lccountleaves([[[[1]]]])" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lccountleaves([[1,2,3],[4,5,6],[[[]]]])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "End of Deep Recursion notebook." ] } ], "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 }