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

\n", "" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "(require racket)\n", "(require racket/base)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Recursion, Glorious, Recursion\n", "See recursion.rkt and try out the trace facility.\n", "\n", "modulo vs remainder See modulo.rkt (Also, quotient vs /)\n", "\n", "Recursive procedures that take lists as arguments and return lists as values.\n", "\n", "See racket4.rkt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall from above that we have list predicates: empty?, null?, list?, list selectors: first, rest, car, cdr, and a list constructor: cons.\n", "\n", "There is a built-in procedure (length lst) that takes a list lst as its argument and returns the number of top-level elements in lst. For example we have the following." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(length '())" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(length '(a b c))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(length '((1 2) (3 4 5)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll write our own recursive version of length, (our-length lst). As we saw, we need one or more base cases. A natural one for an input list is the empty list, '(). If the input list is empty, we can immediately return the value 0 for our-length. We also need one or more recursive cases that \"move\" the input towards a base case. In the case of an input list, removing one element from the list (by using rest or cdr) is a natural candidate, since it moves towards the empty list as a base case. As we think about how to construct the procedure, it may be helpful to consider an example. " ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "(define lst '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(24 6)" ], "text/plain": [ "'(24 6)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(rest lst) " ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(length (rest lst))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "17" ], "text/plain": [ "17" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first lst)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(length lst)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, if the recursive call (our-length (rest lst)) returns a correct value, we just need to add 1 to it to get a correct answer for (our-length lst). We don't actually seem to need the value of (first lst) at all. We are led to define the following procedure." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "(define (our-length lst)\n", " (if (empty? lst)\n", " 0\n", " (+ 1 (our-length (rest lst)))))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can draw a tree of the recursive calls of (our-length '(17 24 6)) to help us understand how this works.\n", "

\n",
    "    (our-length '(17 24 6))\n",
    "     /  |     \\\n",
    "    +   1   (our-length '(24 6))\n",
    "             /  |     \\\n",
    "            +   1    (our-length '(6))\n",
    "                      /  |     \\\n",
    "                     +   1    (our-length '())\n",
    "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that each successive recursive call has an argument that is one element shorter than its predecessor, until we reach the base case of the empty list '(). Then there is no further recursive call, and the procedure just returns 0, leading to the following sequence of return values (from the bottom up.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "    (our-length '(17 24 6)) => 3\n",
    "     /  |     \\\n",
    "    +   1   (our-length '(24 6)) => 2\n",
    "             /  |     \\\n",
    "            +   1    (our-length '(6)) => 1\n",
    "                      /  |     \\\n",
    "                     +   1    (our-length '()) => 0\n",
    "\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the if expression tests (empty? lst) -- it would be equivalent to test (null? lst) or (equal? lst '()).\n", "\n", "This procedure is not tail-recursive, because the value of the recursive call, (our-length (rest lst)) is modified, that is, has 1 added to it, before being returned as the value of (our-length lst). We discuss tail-recursion below." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(our-length '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "(require racket/trace)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "(trace our-length)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(our-length '(17 24 6))\n", "> (our-length '(24 6))\n", "> >(our-length '(6))\n", "> > (our-length '())\n", "< < 0\n", "< <1\n", "< 2\n", "<3\n" ] }, { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(our-length '(17 24 6))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testing whether an element is in a list.\n", "There is a built-in procedure (member item lst) to test whether an arbitrary item is equal? to a top-level element of the list lst. If item is not equal? to any of the top-level elements of lst, then this procedure returns #f. If item is equal to some top-level element of lst, then this procedure returns a list with all the remaining elements of lst from the first one equal? to item until the end. As examples, we have the following." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(2 3)" ], "text/plain": [ "'(2 3)" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(member 2 '(1 2 3))" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(2 3 2 1)" ], "text/plain": [ "'(2 3 2 1)" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(member 2 '(1 2 3 2 1)) " ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(member 4 '(1 2 3)) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that this is not a predicate, because it does not always return #t or #f. We'll write (as a recursive procedure) a predicate version of member, namely (member? item lst). If item is equal? to a top-level element of lst, then (member? item lst) evaluates to #t; otherwise, it evaluates to #f We can define a trivial version using member." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "(define (member? item lst)\n", " (if (member item lst) #t #f))" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(member? 2 '(1 2 3)) " ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(member? 2 '(1 2 3 2 1))" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(member? 4 '(1 2 3)) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because the input lst is a list, a reasonable base case is when lst is an empty list. In this case, lst contains no top-level elements, so item cannot be equal? to one of them, and the procedure should return #f. Thinking about the first example above, we have item => 2 and lst => '(1 2 3)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "    (first lst) => 1\n",
    "    (rest lst) => '(2 3)\n",
    "    (member? item (rest lst)) => #t   (if it returns the correct answer)\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So there should be a recursive case in which we evaluate (member? item (rest lst)) and return the value that it returns. However, this isn't the only case; we have the case in which item is actually equal? to (first lst). This is another base case, which should return the value #t. We are led to the following procedure definition." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "(define (member? item lst)\n", " (cond\n", " [(empty? lst) #f]\n", " [(equal? item (first lst)) #t]\n", " [else (member? item (rest lst))]))" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "(define (ifmember? item lst)\n", " (if (empty? lst) #f\n", " (if (equal? item (first lst))\n", " #t\n", " (ifmember? item (rest lst)))))" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(ifmember? 2 '(1 2 3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "We used a cond special form to avoid a situation of nested if expressions. There are two base cases: lst is empty, and the answer is #f, and the list is non-empty and item is equal? to the first element of lst, when the answer is #t. If neither of these cases applies, then we need to continue to search for item in the rest of the list. The answer (#t or #f) that the recursive call returns will be the answer to whether (member? item lst) should be #t or #f. We can draw the tree of recursive calls for (member? 24 '(17 24 6)) as follows.\n", "
\n",
    "    (member? 24 '(17 24 6))\n",
    "        |\n",
    "    (member? 24 '(24 6))\n",
    "
\n", "There are no further recursive calls because 24 is equal? to (first lst) in this case, and #t is returned. The return value just propagates upward as follows.\n", "
\n",
    "    (member? 24 '(17 24 6)) => #t\n",
    "        |\n",
    "    (member? 24 '(24 6)) => #t\n",
    "    
\n", "Note that our member? procedure is tail-recursive: the value of the recursive call is returned (unmodified) as the value of the whole procedure." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not 1)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not '())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

A procedure that takes a list as input and returns a list.

\n", "\n", "Now we write a procedure (double-each lst) that takes a list lst of numbers and returns the list consisting of twice each number in the original list. As an example we have the following.\n", "
\n",
    "    (double-each '(17 24 6)) => '(34 48 12)\n",
    "
\n", "Once again, the empty list is a reasonable base case. We have to decide what we want (double-each '()) to be. We choose the result to be '(), which consists of twice each number in '(). Thinking about the example, if lst => '(17 24 6), we have the following.\n", "
\n",
    "    (first lst) => 17\n",
    "    (rest lst) => '(24 6)\n",
    "    (double-each (rest lst)) => '(48 12)  (assuming it works correctly)\n",
    "
\n", "For the recursive case, we can call (double-each (rest lst)), and get a list containing twice each of the numbers in the rest of the list. To make this into the desired result, all we have to do is to include twice (first lst) at the front of the list. The list constructor (cons item lst) returns the list equal? to lst with item included as the first element. We may now write the complete procedure as follows." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "(define (double-each lst)\n", " (if (null? lst)\n", " '()\n", " (cons (* 2 (first lst))\n", " (double-each (rest lst)))))" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "*: contract violation\n", " expected: number?\n", " given: 'a\n", " argument position: 2nd\n", " other arguments...:\n", " 2\n" ] } ], "source": [ "(double-each '(a b 3))" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(2 4 6)" ], "text/plain": [ "'(2 4 6)" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(double-each '(1 2 3))" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "(trace double-each)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(double-each '(17 24 6))\n", "> (double-each '(24 6))\n", "> >(double-each '(6))\n", "> > (double-each '())\n", "< < '()\n", "< <'(12)\n", "< '(48 12)\n", "<'(34 48 12)\n" ] }, { "data": { "text/html": [ "'(34 48 12)" ], "text/plain": [ "'(34 48 12)" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(double-each '(17 24 6))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we could get by with an if expression because there were only two cases: the base case and the recursive case. We can draw the tree of recursive calls for the application (double-each '(17 24 6)) as follows.\n", "
\n",
    "    (double-each '(17 24 6))\n",
    "     /   |    \\\n",
    "   cons  34  (double-each '(24 6))\n",
    "              /   |    \\\n",
    "            cons  48  (double-each '(6))\n",
    "                       /   |    \\\n",
    "                     cons  12  (double-each '())\n",
    "
\n", "There are no further recursive calls because we reached the base case (lst => '()), which returns '(). Then the returns propagate up the tree as follows.
\n",
    "    (double-each '(17 24 6)) => '(34 48 12)\n",
    "     /   |    \\\n",
    "   cons  34  (double-each '(24 6)) => '(48 12)\n",
    "              /   |    \\\n",
    "            cons  48  (double-each '(6)) => '(12)\n",
    "                       /   |    \\\n",
    "                     cons  12  (double-each '()) => '()\n",
    "
\n", "Note that this procedure is not tail-recursive, because the value returned by the recursive call is modified (by having twice (first lst) cons'ed on the front) before being returned as the value of the whole procedure." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Deep recursion.

\n", "See add-tree in racket5.rkt\n", "\n", "So far we have seen recursion on numbers, from n to n-1, or from n to (quotient n 10), and \"flat\" recursion on lists, which processes (first lst) in some way and does a recursive call on (rest lst). Now we'll consider a procedure that does \"deep recursion\", in which the procedure is called recursively on sublists, and sublists of sublists, and so on. There is a built-in procedure (flatten value). Here are some examples of it." ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "(require racket)\n", "(require racket/base)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17)" ], "text/plain": [ "'(17)" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(flatten 17)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17 24 6)" ], "text/plain": [ "'(17 24 6)" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(flatten '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(1 2 4 1 2 3)" ], "text/plain": [ "'(1 2 4 1 2 3)" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(flatten '((1 2) (4 (1 2)) 3))" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'()" ], "text/plain": [ "'()" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(flatten '(() ()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the argument value is not a list, the return value is a list containing it as the only element. If the argument value is the empty list, the return value is the empty list. However, if the argument value is a non-empty list, the result is obtained by appending the result of flattening (first value) to the result of flattening (rest lst). We'll write our own version of flatten, (o-f value)." ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "(define (o-f value)\n", " (cond\n", " [(not (list? value)) (list value)]\n", " [(empty? value) '()]\n", " [else\n", " (append (o-f (first value))\n", " (o-f (rest value)))]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that here we've used the built-in list constructor list. This takes an arbitrary finite number of arguments, evaluates all of them, and makes a list of the values. Thus, (list value) is equivalent to (cons value '()). Note the contrast with quote ('), in the following." ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/html": [ "''a" ], "text/plain": [ "''a" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(car '('a 1))" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/html": [ "''a" ], "text/plain": [ "''a" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(car (quote ((quote a) 1)))" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(a 1)" ], "text/plain": [ "'(a 1)" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list 'a 1)" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(3 6)" ], "text/plain": [ "'(3 6)" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list (+ 1 2) (* 2 3))" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((+ 1 2) (* 3 6))" ], "text/plain": [ "'((+ 1 2) (* 3 6))" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'((+ 1 2) (* 3 6))" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((+ 1 2) (* 2 3))" ], "text/plain": [ "'((+ 1 2) (* 2 3))" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(quote ((+ 1 2) (* 2 3)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the case of list, the expressions (+ 1 2) and (* 2 3) are evaluated, and the list of their values returned. In the case of quote ('), evaluation of the sub-expressions of the expression is inhibited, and the value of '((+ 1 2) (* 3 6)) is a list containing two lists, the first with the identifier '+, the number 1 and the number 2, and the second with the identifier '*, the number 3 and the number 6.\n", "\n", "We may draw the tree of recursive calls for an application like (o-f '((1 2) 3)). " ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [], "source": [ "(require racket/trace)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [], "source": [ "(trace o-f)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(o-f '((1 2) 3))\n", "> (o-f '(1 2))\n", "> >(o-f 1)\n", "< <'(1)\n", "> >(o-f '(2))\n", "> > (o-f 2)\n", "< < '(2)\n", "> > (o-f '())\n", "< < '()\n", "< <'(2)\n", "< '(1 2)\n", "> (o-f '(3))\n", "> >(o-f 3)\n", "< <'(3)\n", "> >(o-f '())\n", "< <'()\n", "< '(3)\n", "<'(1 2 3)\n" ] }, { "data": { "text/html": [ "'(1 2 3)" ], "text/plain": [ "'(1 2 3)" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(o-f '((1 2) 3))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "          (o-f '((1 2) 3))\n",
    "       _______________________\n",
    "       /    |                 \\\n",
    "      /     |                  \\\n",
    "     /      |                   \\\n",
    "    /       |                    \\ \n",
    "append  (o-f '(1 2))              (o-f '(3))\n",
    "       /    |     \\               /    |    \\\n",
    "  append  (o-f 1) (o-f '(2))  append (o-f 3) (o-f '())\n",
    "                 /    |    \\\n",
    "            append (o-f 2) (o-f '()) \n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All the leaves are base cases (either the empty list or a non-list value), and they are appended and returned up the tree of recursive calls to yield (o-f '((1 2) 3)) => '(1 2 3).\n", "\n", "Going forward, deep recursion will appear in many guises, to traverse or process more complex recursive data structures. It is your friend. Like it on Facebook. (BTW, Facebook is basically a huge graph which is a mammoth recursive data structure.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### add-tree deep recursion\n", "\n", "We first define a variable with a tree structure." ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [], "source": [ "(define s '((2 3 (4) ((5))) ((4)) (3 4 5)))" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(2 3 4 5 4 3 4 5)" ], "text/plain": [ "'(2 3 4 5 4 3 4 5)" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(flatten s)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/html": [ "30" ], "text/plain": [ "30" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(apply + (flatten s))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we define add-tree which adds up the elements of the tree using deep recursion which returns a single value." ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [], "source": [ "(define (add-tree lst)\n", " (cond ((null? lst) 0)\n", " ((number? lst) lst)\n", " ((list? lst)\n", " (+ (add-tree (car lst))\n", " (add-tree (cdr lst))))\n", " ))" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/html": [ "30" ], "text/plain": [ "30" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(add-tree s)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [], "source": [ "(trace add-tree)" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(add-tree '((2 3 (4) ((5))) ((4)) (3 4 5)))\n", "> (add-tree '(2 3 (4) ((5))))\n", "> >(add-tree 2)\n", "< <2\n", "> >(add-tree '(3 (4) ((5))))\n", "> > (add-tree 3)\n", "< < 3\n", "> > (add-tree '((4) ((5))))\n", "> > >(add-tree '(4))\n", "> > > (add-tree 4)\n", "< < < 4\n", "> > > (add-tree '())\n", "< < < 0\n", "< < <4\n", "> > >(add-tree '(((5))))\n", "> > > (add-tree '((5)))\n", "> > > >(add-tree '(5))\n", "> > > > (add-tree 5)\n", "< < < < 5\n", "> > > > (add-tree '())\n", "< < < < 0\n", "< < < <5\n", "> > > >(add-tree '())\n", "< < < <0\n", "< < < 5\n", "> > > (add-tree '())\n", "< < < 0\n", "< < <5\n", "< < 9\n", "< <12\n", "< 14\n", "> (add-tree '(((4)) (3 4 5)))\n", "> >(add-tree '((4)))\n", "> > (add-tree '(4))\n", "> > >(add-tree 4)\n", "< < <4\n", "> > >(add-tree '())\n", "< < <0\n", "< < 4\n", "> > (add-tree '())\n", "< < 0\n", "< <4\n", "> >(add-tree '((3 4 5)))\n", "> > (add-tree '(3 4 5))\n", "> > >(add-tree 3)\n", "< < <3\n", "> > >(add-tree '(4 5))\n", "> > > (add-tree 4)\n", "< < < 4\n", "> > > (add-tree '(5))\n", "> > > >(add-tree 5)\n", "< < < <5\n", "> > > >(add-tree '())\n", "< < < <0\n", "< < < 5\n", "< < <9\n", "< < 12\n", "> > (add-tree '())\n", "< < 0\n", "< <12\n", "< 16\n", "<30\n" ] }, { "data": { "text/html": [ "30" ], "text/plain": [ "30" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(add-tree s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We next define a deep recursive function which transforms the tree, returning a modified version of the tree. We assume that the tree contains only numeric leaves." ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "(define (treeadd1 tree)\n", " (cond ((null? tree) '())\n", " ((not (pair? tree))\n", " (+ 1 tree))\n", " (else\n", " (cons (treeadd1 (first tree))\n", " (treeadd1 (rest tree))))))" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "+: contract violation\n", " expected: number?\n", " given: 'd\n", " argument position: 2nd\n", " other arguments...:\n", " 1\n" ] } ], "source": [ "(treeadd1 '(1 2 d e))" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((3 4 (5) ((6))) ((5)) (4 5 6))" ], "text/plain": [ "'((3 4 (5) ((6))) ((5)) (4 5 6))" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(treeadd1 s)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "(trace treeadd1 )" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(treeadd1 '((2 3 (4) ((5))) ((4)) (3 4 5)))\n", "> (treeadd1 '(2 3 (4) ((5))))\n", "> >(treeadd1 2)\n", "< <3\n", "> >(treeadd1 '(3 (4) ((5))))\n", "> > (treeadd1 3)\n", "< < 4\n", "> > (treeadd1 '((4) ((5))))\n", "> > >(treeadd1 '(4))\n", "> > > (treeadd1 4)\n", "< < < 5\n", "> > > (treeadd1 '())\n", "< < < '()\n", "< < <'(5)\n", "> > >(treeadd1 '(((5))))\n", "> > > (treeadd1 '((5)))\n", "> > > >(treeadd1 '(5))\n", "> > > > (treeadd1 5)\n", "< < < < 6\n", "> > > > (treeadd1 '())\n", "< < < < '()\n", "< < < <'(6)\n", "> > > >(treeadd1 '())\n", "< < < <'()\n", "< < < '((6))\n", "> > > (treeadd1 '())\n", "< < < '()\n", "< < <'(((6)))\n", "< < '((5) ((6)))\n", "< <'(4 (5) ((6)))\n", "< '(3 4 (5) ((6)))\n", "> (treeadd1 '(((4)) (3 4 5)))\n", "> >(treeadd1 '((4)))\n", "> > (treeadd1 '(4))\n", "> > >(treeadd1 4)\n", "< < <5\n", "> > >(treeadd1 '())\n", "< < <'()\n", "< < '(5)\n", "> > (treeadd1 '())\n", "< < '()\n", "< <'((5))\n", "> >(treeadd1 '((3 4 5)))\n", "> > (treeadd1 '(3 4 5))\n", "> > >(treeadd1 3)\n", "< < <4\n", "> > >(treeadd1 '(4 5))\n", "> > > (treeadd1 4)\n", "< < < 5\n", "> > > (treeadd1 '(5))\n", "> > > >(treeadd1 5)\n", "< < < <6\n", "> > > >(treeadd1 '())\n", "< < < <'()\n", "< < < '(6)\n", "< < <'(5 6)\n", "< < '(4 5 6)\n", "> > (treeadd1 '())\n", "< < '()\n", "< <'((4 5 6))\n", "< '(((5)) (4 5 6))\n", "<'((3 4 (5) ((6))) ((5)) (4 5 6))\n" ] }, { "data": { "text/html": [ "'((3 4 (5) ((6))) ((5)) (4 5 6))" ], "text/plain": [ "'((3 4 (5) ((6))) ((5)) (4 5 6))" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(treeadd1 s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we define double-tree which copies a tree, replacing each leaf with twice its value." ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [], "source": [ ";; assuming only numeric leaves to the tree\n", "(define (double-tree tree)\n", " (cond ((null? tree) '())\n", " ((not (pair? tree))\n", " (+ tree tree))\n", " (else\n", " (cons (double-tree (car tree))\n", " (double-tree (cdr tree))))))" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((4 6 (8) ((10))) ((8)) (6 8 10))" ], "text/plain": [ "'((4 6 (8) ((10))) ((8)) (6 8 10))" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(double-tree s)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [], "source": [ "(trace double-tree)" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(double-tree '((2 3 (4) ((5))) ((4)) (3 4 5)))\n", "> (double-tree '(2 3 (4) ((5))))\n", "> >(double-tree 2)\n", "< <4\n", "> >(double-tree '(3 (4) ((5))))\n", "> > (double-tree 3)\n", "< < 6\n", "> > (double-tree '((4) ((5))))\n", "> > >(double-tree '(4))\n", "> > > (double-tree 4)\n", "< < < 8\n", "> > > (double-tree '())\n", "< < < '()\n", "< < <'(8)\n", "> > >(double-tree '(((5))))\n", "> > > (double-tree '((5)))\n", "> > > >(double-tree '(5))\n", "> > > > (double-tree 5)\n", "< < < < 10\n", "> > > > (double-tree '())\n", "< < < < '()\n", "< < < <'(10)\n", "> > > >(double-tree '())\n", "< < < <'()\n", "< < < '((10))\n", "> > > (double-tree '())\n", "< < < '()\n", "< < <'(((10)))\n", "< < '((8) ((10)))\n", "< <'(6 (8) ((10)))\n", "< '(4 6 (8) ((10)))\n", "> (double-tree '(((4)) (3 4 5)))\n", "> >(double-tree '((4)))\n", "> > (double-tree '(4))\n", "> > >(double-tree 4)\n", "< < <8\n", "> > >(double-tree '())\n", "< < <'()\n", "< < '(8)\n", "> > (double-tree '())\n", "< < '()\n", "< <'((8))\n", "> >(double-tree '((3 4 5)))\n", "> > (double-tree '(3 4 5))\n", "> > >(double-tree 3)\n", "< < <6\n", "> > >(double-tree '(4 5))\n", "> > > (double-tree 4)\n", "< < < 8\n", "> > > (double-tree '(5))\n", "> > > >(double-tree 5)\n", "< < < <10\n", "> > > >(double-tree '())\n", "< < < <'()\n", "< < < '(10)\n", "< < <'(8 10)\n", "< < '(6 8 10)\n", "> > (double-tree '())\n", "< < '()\n", "< <'((6 8 10))\n", "< '(((8)) (6 8 10))\n", "<'((4 6 (8) ((10))) ((8)) (6 8 10))\n" ] }, { "data": { "text/html": [ "'((4 6 (8) ((10))) ((8)) (6 8 10))" ], "text/plain": [ "'((4 6 (8) ((10))) ((8)) (6 8 10))" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(double-tree s)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(pair? (cons 1 2))" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(1 . 2)" ], "text/plain": [ "'(1 . 2)" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons 1 2)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? (cons 1 2))" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? (cons 1 (cons 2 empty)))" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'()" ], "text/plain": [ "'()" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "empty" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Racket", "language": "racket", "name": "racket" }, "language_info": { "codemirror_mode": "scheme", "file_extension": ".rkt", "mimetype": "text/x-racket", "name": "Racket", "pygments_lexer": "racket", "version": "7.4" } }, "nbformat": 4, "nbformat_minor": 4 }