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

\n", "" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "(require racket)\n", "(require racket/base)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Defining local variables with let and let*\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "let creates local variables within your procedure.\n", "It does not pollute the global namespace. Below we create two local variables, y and z." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "(define (f x)\n", " (let ((y (* x 2 ))\n", " (z (+ x 2)))\n", " (list x y z)))" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(10 20 12)" ], "text/plain": [ "'(10 20 12)" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(f 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "let* is like let, but you can refer to other local variables within let*." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "(define (g x)\n", " (let* ((y (* x 2)) \n", " (z (+ y 2)))\n", " (list x y z)))" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(10 20 22)" ], "text/plain": [ "'(10 20 22)" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(g 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is what you would need to do if you did not have let*." ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [], "source": [ "(define (h x)\n", " (let ((y (* x 2)))\n", " (let ((z (+ y 2)))\n", " (list x y z))))" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(10 20 22)" ], "text/plain": [ "'(10 20 22)" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(h 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining Functions with Optional Arguments\n", "\n", "Below we define a function size which returns the size of an object. By default, it uses length as the measure of size, but you can specify another function to specify the size. Note the dot notation to specify the optional argument proc" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "(define (size lst . proc)\n", " (let ((func (if (null? proc)\n", " length\n", " (car proc))))\n", " (func lst)))" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/html": [ "6" ], "text/plain": [ "6" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(size '(1 2 3 4 5 2))" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "max: contract violation\n", " expected: real?\n", " given: '(1 2 3 4 2)\n" ] } ], "source": [ "(size '(1 2 3 4 2) max)" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(max 1 2 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The problem is that max unlike length takes a variable number of arguments, not a single list." ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/html": [ "4" ], "text/plain": [ "4" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(size '(1 2 3 4 2) (lambda (x) (apply max x)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use square brackets to specify the default value for an optional argument: [optional-arg default-value]" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [], "source": [ "(define (size2 lst [proc length])\n", " (proc lst))" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/html": [ "6" ], "text/plain": [ "6" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(size2 '(1 2 8 4 5 2))" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "max: contract violation\n", " expected: real?\n", " given: '(1 2 8 4 5)\n" ] } ], "source": [ "(size2 '(1 2 8 4 5) max)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/html": [ "8" ], "text/plain": [ "8" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(size2 '(1 2 8 4 5) (lambda (x) (apply max x)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Recursive Integer Operations\n", "\n", "We define the following functions:\n", "- last-digit return the low order decimal digit of an integer\n", "- split break an integer into a list of decimal digits\n", "- join convert a list of decimal digits into an integer" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [], "source": [ "(define d 736241)\n", "(define x '(7 3 6 2 4 1))" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [], "source": [ "(define (last-digit n)\n", " (abs (remainder n 10)))" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(last-digit d)" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(last-digit -1236337373)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [], "source": [ "(define (split n)\n", " (reverse (split-aux n)))\n", "\n", "(define (split-aux n)\n", " (if (< n 10)\n", " (cons n '())\n", " (cons (last-digit n) (split-aux (quotient n 10)))))" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(7 3 6 2 4 1)" ], "text/plain": [ "'(7 3 6 2 4 1)" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(split d)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(1 4 2 6 3 7)" ], "text/plain": [ "'(1 4 2 6 3 7)" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(split-aux d)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "(define (join lst)\n", " (join-aux lst 0))\n", "\n", "(define (join-aux lst num)\n", " (if (null? lst)\n", " num\n", " (join-aux (cdr lst) (+ (car lst) (* 10 num)))))" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/html": [ "736241" ], "text/plain": [ "736241" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(join x)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/html": [ "736241" ], "text/plain": [ "736241" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(join (split d))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that both split and join used an auxiliary function to implement the recursion. We can write join without a helper function by using a default parameter for the initial result." ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [], "source": [ "(define (join2 lst [num 0])\n", " (if (null? lst)\n", " num\n", " (join2 (cdr lst) (+ (car lst) (* 10 num)))))" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/html": [ "736241" ], "text/plain": [ "736241" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(join2 x)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/html": [ "736241" ], "text/plain": [ "736241" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(join2 (split d))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

More Recursion: append and replicate

\n", "\n", "append splices together two lists. Note that cons is slightly different. We can define my-append using recursion." ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(1 2 3 4 5 6)" ], "text/plain": [ "'(1 2 3 4 5 6)" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(append '(1 2 3) '(4 5 6))" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((1 2 3) 4 5 6)" ], "text/plain": [ "'((1 2 3) 4 5 6)" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons '(1 2 3) '(4 5 6))" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "(define (my-append list1 list2)\n", " (if (null? list1) list2\n", " (cons (car list1) (my-append (cdr list1) list2))))" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(1 2 3 4 5 6)" ], "text/plain": [ "'(1 2 3 4 5 6)" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(my-append '(1 2 3) '(4 5 6))" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [], "source": [ "(require racket/trace)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "(trace my-append)" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(my-append '(1 2 3) '(4 5 6))\n", "> (my-append '(2 3) '(4 5 6))\n", "> >(my-append '(3) '(4 5 6))\n", "> > (my-append '() '(4 5 6))\n", "< < '(4 5 6)\n", "< <'(3 4 5 6)\n", "< '(2 3 4 5 6)\n", "<'(1 2 3 4 5 6)\n" ] }, { "data": { "text/html": [ "'(1 2 3 4 5 6)" ], "text/plain": [ "'(1 2 3 4 5 6)" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(my-append '(1 2 3) '(4 5 6))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define replicate which creates n copies of an object in a list." ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [], "source": [ "(define (replicate obj n)\n", " (if (zero? n)\n", " empty ;; '()\n", " (cons obj (replicate obj (- n 1)))))" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(x x x x x x x x x x)" ], "text/plain": [ "'(x x x x x x x x x x)" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(replicate 'x 10)" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(hi hi hi hi hi)" ], "text/plain": [ "'(hi hi hi hi hi)" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(replicate 'hi 5)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((1 2 3) (1 2 3) (1 2 3))" ], "text/plain": [ "'((1 2 3) (1 2 3) (1 2 3))" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(replicate '(1 2 3) 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Tail Recursion\n", "\n", "Both my-append and replicate are recursive. We note that both \n", "have to wait for the return of the recursive call before returning a final value.\n", "Since recursion is normally implemented by pushing recursive calls onto a memory stack, these recursive calls can consume lots of resources. \n", "\n", "Guy Steele and Gerry Sussman of MIT in the 1970's noted that there is a way to avoid the overhead of the recursive calls piling up on the stack using tail recursion. The idea is simple and elegant. You write the procedure so that the last call in the function is the recursive call. That way, there is no need to wait around for the result." ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "(require racket/trace)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [], "source": [ "(trace replicate)" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(replicate 'x 10)\n", "> (replicate 'x 9)\n", "> >(replicate 'x 8)\n", "> > (replicate 'x 7)\n", "> > >(replicate 'x 6)\n", "> > > (replicate 'x 5)\n", "> > > >(replicate 'x 4)\n", "> > > > (replicate 'x 3)\n", "> > > > >(replicate 'x 2)\n", "> > > > > (replicate 'x 1)\n", "> > > >[10] (replicate 'x 0)\n", "< < < <[10] '()\n", "< < < < < '(x)\n", "< < < < <'(x x)\n", "< < < < '(x x x)\n", "< < < <'(x x x x)\n", "< < < '(x x x x x)\n", "< < <'(x x x x x x)\n", "< < '(x x x x x x x)\n", "< <'(x x x x x x x x)\n", "< '(x x x x x x x x x)\n", "<'(x x x x x x x x x x)\n" ] }, { "data": { "text/html": [ "'(x x x x x x x x x x)" ], "text/plain": [ "'(x x x x x x x x x x)" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(replicate 'x 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The nested calls to replicate indicate the number of items sitting on the stack.\n", "\n", "We can define a tail recursive version of replicate for comparison" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [], "source": [ "(define (trreplicate obj n)\n", " (trrepaux obj n empty))\n", "\n", "(define (trrepaux obj n result)\n", " (if (zero? n)\n", " result\n", " (trrepaux obj (- n 1) (cons obj result))))" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(x x x x x x x x x x)" ], "text/plain": [ "'(x x x x x x x x x x)" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trreplicate 'x 10)" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [], "source": [ "(trace trreplicate)" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(trreplicate 'x 10)\n", "<'(x x x x x x x x x x)\n" ] }, { "data": { "text/html": [ "'(x x x x x x x x x x)" ], "text/plain": [ "'(x x x x x x x x x x)" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trreplicate 'x 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to trace the helper function trrepaux" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [], "source": [ "(trace trrepaux)" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(trreplicate 'x 10)\n", ">(trrepaux 'x 10 '())\n", ">(trrepaux 'x 9 '(x))\n", ">(trrepaux 'x 8 '(x x))\n", ">(trrepaux 'x 7 '(x x x))\n", ">(trrepaux 'x 6 '(x x x x))\n", ">(trrepaux 'x 5 '(x x x x x))\n", ">(trrepaux 'x 4 '(x x x x x x))\n", ">(trrepaux 'x 3 '(x x x x x x x))\n", ">(trrepaux 'x 2 '(x x x x x x x x))\n", ">(trrepaux 'x 1 '(x x x x x x x x x))\n", ">(trrepaux 'x 0 '(x x x x x x x x x x))\n", "<'(x x x x x x x x x x)\n" ] }, { "data": { "text/html": [ "'(x x x x x x x x x x)" ], "text/plain": [ "'(x x x x x x x x x x)" ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trreplicate 'x 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Each call to trrepaux is independent. They do not have to leave calls on the stack.\n", "\n", "We can redefine trreplicate using the optional parameter and avoid the helper function." ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "(define (trreplicate2 obj n [result empty])\n", " (if (zero? n)\n", " result\n", " (trreplicate2 obj (- n 1) (cons obj result))))" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(x x x)" ], "text/plain": [ "'(x x x)" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trreplicate2 'x 3)" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [], "source": [ "(trace trreplicate2)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(trreplicate2 'x 3)\n", ">(trreplicate2 'x 2 '(x))\n", ">(trreplicate2 'x 1 '(x x))\n", ">(trreplicate2 'x 0 '(x x x))\n", "<'(x x x)\n" ] }, { "data": { "text/html": [ "'(x x x)" ], "text/plain": [ "'(x x x)" ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trreplicate2 'x 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can create tail recursive versions of familiar functions such as \n", "- length\n", "- sum\n", "- max\n", "- power Racket has the expt function.\n", "\n", "both with and without helper functions." ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [], "source": [ "(define (length lst)\n", " (if (null? lst) 0\n", " (+ 1 (length (cdr lst)))))\n", "\n", "(define (trlength lst)\n", " (trlength-aux lst 0))\n", "\n", "(define (trlength-aux lst length)\n", " (if (null? lst)\n", " length\n", " (trlength-aux (cdr lst) (+ 1 length))))\n", "\n", "(define (trlength2 lst [length 0])\n", " (if (null? lst)\n", " length\n", " (trlength2 (cdr lst) (+ 1 length))))" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(length '(a b c d e f g))" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 99, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trlength '(a b c d e f g))" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trlength2 '(a b c d e f g))" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [], "source": [ "(define (sum lst)\n", " (if (null? lst) 0\n", " (+ (car lst) (sum (cdr lst)))))\n", "\n", "(define (trsum lst)\n", " (trsum-aux lst 0))\n", "\n", "(define (trsum-aux lst sum)\n", " (if (null? lst)\n", " sum\n", " (trsum-aux (cdr lst) (+ (car lst) sum))))\n", "\n", "(define (trsum2 lst [sum 0])\n", " (if (null? lst)\n", " sum\n", " (trsum2 (cdr lst) (+ (car lst) sum))))" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/html": [ "20" ], "text/plain": [ "20" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(sum '(2 4 6 8))" ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "data": { "text/html": [ "20" ], "text/plain": [ "20" ] }, "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trsum '(2 4 6 8))" ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/html": [ "20" ], "text/plain": [ "20" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trsum2 '(2 4 6 8))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In racket, -inf.0 is negative infinity as a real." ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(integer? -inf.0)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(real? -inf.0)" ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [], "source": [ "(define (max lst)\n", " (if (null? lst)\n", " -inf.0\n", " (if (> (car lst) (max (cdr lst)))\n", " (car lst)\n", " (max (cdr lst)))))\n", "\n", "(define (trmax lst)\n", " (trmax-aux lst -inf.0))\n", "\n", "(define (trmax-aux lst max)\n", " (if (null? lst)\n", " max\n", " (if (> (car lst) max)\n", " (trmax-aux (cdr lst) (car lst))\n", " (trmax-aux (cdr lst) max))))\n", "\n", "(define (trmax2 lst [max -inf.0])\n", " (if (null? lst)\n", " max\n", " (if (> (car lst) max)\n", " (trmax2 (cdr lst) (car lst))\n", " (trmax2 (cdr lst) max))))" ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "max: contract violation\n", " expected: real?\n", " given: '(3 33 0)\n" ] } ], "source": [ "(max '(-3 3 33 0))" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/html": [ "33" ], "text/plain": [ "33" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trmax '(-3 3 33 0))" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/html": [ "33" ], "text/plain": [ "33" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trmax2 '(-3 3 33 0))" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [], "source": [ "(define (power x n)\n", " (if (zero? n)\n", " 1\n", " (* x (power x (- n 1)))))\n", "\n", "(define (trpower x n)\n", " (trpower-aux x n 1))\n", "\n", "(define (trpower-aux x n result)\n", " (if (zero? n)\n", " result\n", " (trpower-aux x (- n 1) (* x result))))\n", "\n", "(define (trpower2 x n [result 1])\n", " (if (zero? n)\n", " result\n", " (trpower2 x (- n 1) (* x result))))" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "text/html": [ "1024" ], "text/plain": [ "1024" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(power 2 10)" ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/html": [ "1024" ], "text/plain": [ "1024" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trpower 2 10)" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/html": [ "1024" ], "text/plain": [ "1024" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(trpower2 2 10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Even more power.\n", "\n", "There is a faster algorithm for exponentiation. See page 48 of The Algorithm Design Manual by Skienna. pdf\n", "\n", "It is faster than expt" ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [], "source": [ "(define (lgpower a n)\n", " (if (zero? n)\n", " 1\n", " (let ((x (lgpower a (quotient n 2))))\n", " (if (even? n)\n", " (* x x)\n", " (* a x x)))))" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "data": { "text/html": [ "1024" ], "text/plain": [ "1024" ] }, "execution_count": 115, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(lgpower 2 10)" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "data": { "text/html": [ "1267650600228229401496703205376" ], "text/plain": [ "1267650600228229401496703205376" ] }, "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(lgpower 2 100)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Exercise: Rewrite the my-append function as a tail-recursive function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Ackermann Function\n", "\n", "See (https://en.wikipedia.org/wiki/Ackermann_function)\n", "\n", "Ackermann was an interesting guy. He gave up his career for love, or at least marriage. Guy Steele had a similar shadow on his career." ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [], "source": [ "(define (ack m n)\n", " (cond ((= m 0) (+ n 1))\n", " ((= n 0) (ack (- m 1) 1)) ;; m > 0\n", " (else\n", " (ack (- m 1) (ack m (- n 1))))))" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(ack 2 2)" ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [], "source": [ "(trace ack)" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(ack 2 2)\n", "> (ack 2 1)\n", "> >(ack 2 0)\n", "> >(ack 1 1)\n", "> > (ack 1 0)\n", "> > (ack 0 1)\n", "< < 2\n", "> >(ack 0 2)\n", "< <3\n", "> (ack 1 3)\n", "> >(ack 1 2)\n", "> > (ack 1 1)\n", "> > >(ack 1 0)\n", "> > >(ack 0 1)\n", "< < <2\n", "> > (ack 0 2)\n", "< < 3\n", "> >(ack 0 3)\n", "< <4\n", "> (ack 0 4)\n", "< 5\n", ">(ack 1 5)\n", "> (ack 1 4)\n", "> >(ack 1 3)\n", "> > (ack 1 2)\n", "> > >(ack 1 1)\n", "> > > (ack 1 0)\n", "> > > (ack 0 1)\n", "< < < 2\n", "> > >(ack 0 2)\n", "< < <3\n", "> > (ack 0 3)\n", "< < 4\n", "> >(ack 0 4)\n", "< <5\n", "> (ack 0 5)\n", "< 6\n", ">(ack 0 6)\n", "<7\n" ] }, { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(ack 2 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we will memoize ack to avoid repetitive computations." ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [], "source": [ "(define (ack m n)\n", " (cond ((= m 0) (+ n 1))\n", " ((= n 0) (ack (- m 1) 1)) ;; m > 0\n", " (else\n", " (ack (- m 1) (ack m (- n 1))))))\n", "\n", "(define (memoize func [table (make-hash)])\n", " (lambda (arg1 arg2)\n", " (cond ((hash-has-key? table (list arg1 arg2))\n", " (begin\n", " (display (list 'used-hash arg1 arg2))\n", " (newline)\n", " (hash-ref table (list arg1 arg2))))\n", " (else\n", " (hash-set! table (list arg1 arg2) (func arg1 arg2))\n", " (hash-ref table (list arg1 arg2))))))" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [], "source": [ "(define ack (memoize ack))" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(used-hash 1 1)\n", "(used-hash 1 3)\n" ] }, { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(ack 2 2)" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [], "source": [ "(trace ack)" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(ack 3 3)\n", "> (ack 3 2)\n", "> >(ack 3 1)\n", "> > (ack 3 0)\n", "> > >(ack 2 1)\n", "(used-hash 2 1)\n", "< < <5\n", "< < 5\n", "> > (ack 2 5)\n", "> > >(ack 2 4)\n", "> > > (ack 2 3)\n", "> > > >(ack 2 2)\n", "(used-hash 2 2)\n", "< < < <7\n", "> > > >(ack 1 7)\n", "> > > > (ack 1 6)\n", "> > > > >(ack 1 5)\n", "(used-hash 1 5)\n", "< < < < <7\n", "> > > > >(ack 0 7)\n", "< < < < <8\n", "< < < < 8\n", "> > > > (ack 0 8)\n", "< < < < 9\n", "< < < <9\n", "< < < 9\n", "> > > (ack 1 9)\n", "> > > >(ack 1 8)\n", "> > > > (ack 1 7)\n", "(used-hash 1 7)\n", "< < < < 9\n", "> > > > (ack 0 9)\n", "< < < < 10\n", "< < < <10\n", "> > > >(ack 0 10)\n", "< < < <11\n", "< < < 11\n", "< < <11\n", "> > >(ack 1 11)\n", "> > > (ack 1 10)\n", "> > > >(ack 1 9)\n", "(used-hash 1 9)\n", "< < < <11\n", "> > > >(ack 0 11)\n", "< < < <12\n", "< < < 12\n", "> > > (ack 0 12)\n", "< < < 13\n", "< < <13\n", "< < 13\n", "< <13\n", "> >(ack 2 13)\n", "> > (ack 2 12)\n", "> > >(ack 2 11)\n", "> > > (ack 2 10)\n", "> > > >(ack 2 9)\n", "> > > > (ack 2 8)\n", "> > > > >(ack 2 7)\n", "> > > > > (ack 2 6)\n", "> > > >[10] (ack 2 5)\n", "(used-hash 2 5)\n", "< < < <[10] 13\n", "> > > >[10] (ack 1 13)\n", "> > > >[11] (ack 1 12)\n", "> > > >[12] (ack 1 11)\n", "(used-hash 1 11)\n", "< < < <[12] 13\n", "> > > >[12] (ack 0 13)\n", "< < < <[12] 14\n", "< < < <[11] 14\n", "> > > >[11] (ack 0 14)\n", "< < < <[11] 15\n", "< < < <[10] 15\n", "< < < < < 15\n", "> > > > > (ack 1 15)\n", "> > > >[10] (ack 1 14)\n", "> > > >[11] (ack 1 13)\n", "(used-hash 1 13)\n", "< < < <[11] 15\n", "> > > >[11] (ack 0 15)\n", "< < < <[11] 16\n", "< < < <[10] 16\n", "> > > >[10] (ack 0 16)\n", "< < < <[10] 17\n", "< < < < < 17\n", "< < < < <17\n", "> > > > >(ack 1 17)\n", "> > > > > (ack 1 16)\n", "> > > >[10] (ack 1 15)\n", "(used-hash 1 15)\n", "< < < <[10] 17\n", "> > > >[10] (ack 0 17)\n", "< < < <[10] 18\n", "< < < < < 18\n", "> > > > > (ack 0 18)\n", "< < < < < 19\n", "< < < < <19\n", "< < < < 19\n", "> > > > (ack 1 19)\n", "> > > > >(ack 1 18)\n", "> > > > > (ack 1 17)\n", "(used-hash 1 17)\n", "< < < < < 19\n", "> > > > > (ack 0 19)\n", "< < < < < 20\n", "< < < < <20\n", "> > > > >(ack 0 20)\n", "< < < < <21\n", "< < < < 21\n", "< < < <21\n", "> > > >(ack 1 21)\n", "> > > > (ack 1 20)\n", "> > > > >(ack 1 19)\n", "(used-hash 1 19)\n", "< < < < <21\n", "> > > > >(ack 0 21)\n", "< < < < <22\n", "< < < < 22\n", "> > > > (ack 0 22)\n", "< < < < 23\n", "< < < <23\n", "< < < 23\n", "> > > (ack 1 23)\n", "> > > >(ack 1 22)\n", "> > > > (ack 1 21)\n", "(used-hash 1 21)\n", "< < < < 23\n", "> > > > (ack 0 23)\n", "< < < < 24\n", "< < < <24\n", "> > > >(ack 0 24)\n", "< < < <25\n", "< < < 25\n", "< < <25\n", "> > >(ack 1 25)\n", "> > > (ack 1 24)\n", "> > > >(ack 1 23)\n", "(used-hash 1 23)\n", "< < < <25\n", "> > > >(ack 0 25)\n", "< < < <26\n", "< < < 26\n", "> > > (ack 0 26)\n", "< < < 27\n", "< < <27\n", "< < 27\n", "> > (ack 1 27)\n", "> > >(ack 1 26)\n", "> > > (ack 1 25)\n", "(used-hash 1 25)\n", "< < < 27\n", "> > > (ack 0 27)\n", "< < < 28\n", "< < <28\n", "> > >(ack 0 28)\n", "< < <29\n", "< < 29\n", "< <29\n", "< 29\n", "> (ack 2 29)\n", "> >(ack 2 28)\n", "> > (ack 2 27)\n", "> > >(ack 2 26)\n", "> > > (ack 2 25)\n", "> > > >(ack 2 24)\n", "> > > > (ack 2 23)\n", "> > > > >(ack 2 22)\n", "> > > > > (ack 2 21)\n", "> > > >[10] (ack 2 20)\n", "> > > >[11] (ack 2 19)\n", "> > > >[12] (ack 2 18)\n", "> > > >[13] (ack 2 17)\n", "> > > >[14] (ack 2 16)\n", "> > > >[15] (ack 2 15)\n", "> > > >[16] (ack 2 14)\n", "> > > >[17] (ack 2 13)\n", "(used-hash 2 13)\n", "< < < <[17] 29\n", "> > > >[17] (ack 1 29)\n", "> > > >[18] (ack 1 28)\n", "> > > >[19] (ack 1 27)\n", "(used-hash 1 27)\n", "< < < <[19] 29\n", "> > > >[19] (ack 0 29)\n", "< < < <[19] 30\n", "< < < <[18] 30\n", "> > > >[18] (ack 0 30)\n", "< < < <[18] 31\n", "< < < <[17] 31\n", "< < < <[16] 31\n", "> > > >[16] (ack 1 31)\n", "> > > >[17] (ack 1 30)\n", "> > > >[18] (ack 1 29)\n", "(used-hash 1 29)\n", "< < < <[18] 31\n", "> > > >[18] (ack 0 31)\n", "< < < <[18] 32\n", "< < < <[17] 32\n", "> > > >[17] (ack 0 32)\n", "< < < <[17] 33\n", "< < < <[16] 33\n", "< < < <[15] 33\n", "> > > >[15] (ack 1 33)\n", "> > > >[16] (ack 1 32)\n", "> > > >[17] (ack 1 31)\n", "(used-hash 1 31)\n", "< < < <[17] 33\n", "> > > >[17] (ack 0 33)\n", "< < < <[17] 34\n", "< < < <[16] 34\n", "> > > >[16] (ack 0 34)\n", "< < < <[16] 35\n", "< < < <[15] 35\n", "< < < <[14] 35\n", "> > > >[14] (ack 1 35)\n", "> > > >[15] (ack 1 34)\n", "> > > >[16] (ack 1 33)\n", "(used-hash 1 33)\n", "< < < <[16] 35\n", "> > > >[16] (ack 0 35)\n", "< < < <[16] 36\n", "< < < <[15] 36\n", "> > > >[15] (ack 0 36)\n", "< < < <[15] 37\n", "< < < <[14] 37\n", "< < < <[13] 37\n", "> > > >[13] (ack 1 37)\n", "> > > >[14] (ack 1 36)\n", "> > > >[15] (ack 1 35)\n", "(used-hash 1 35)\n", "< < < <[15] 37\n", "> > > >[15] (ack 0 37)\n", "< < < <[15] 38\n", "< < < <[14] 38\n", "> > > >[14] (ack 0 38)\n", "< < < <[14] 39\n", "< < < <[13] 39\n", "< < < <[12] 39\n", "> > > >[12] (ack 1 39)\n", "> > > >[13] (ack 1 38)\n", "> > > >[14] (ack 1 37)\n", "(used-hash 1 37)\n", "< < < <[14] 39\n", "> > > >[14] (ack 0 39)\n", "< < < <[14] 40\n", "< < < <[13] 40\n", "> > > >[13] (ack 0 40)\n", "< < < <[13] 41\n", "< < < <[12] 41\n", "< < < <[11] 41\n", "> > > >[11] (ack 1 41)\n", "> > > >[12] (ack 1 40)\n", "> > > >[13] (ack 1 39)\n", "(used-hash 1 39)\n", "< < < <[13] 41\n", "> > > >[13] (ack 0 41)\n", "< < < <[13] 42\n", "< < < <[12] 42\n", "> > > >[12] (ack 0 42)\n", "< < < <[12] 43\n", "< < < <[11] 43\n", "< < < <[10] 43\n", "> > > >[10] (ack 1 43)\n", "> > > >[11] (ack 1 42)\n", "> > > >[12] (ack 1 41)\n", "(used-hash 1 41)\n", "< < < <[12] 43\n", "> > > >[12] (ack 0 43)\n", "< < < <[12] 44\n", "< < < <[11] 44\n", "> > > >[11] (ack 0 44)\n", "< < < <[11] 45\n", "< < < <[10] 45\n", "< < < < < 45\n", "> > > > > (ack 1 45)\n", "> > > >[10] (ack 1 44)\n", "> > > >[11] (ack 1 43)\n", "(used-hash 1 43)\n", "< < < <[11] 45\n", "> > > >[11] (ack 0 45)\n", "< < < <[11] 46\n", "< < < <[10] 46\n", "> > > >[10] (ack 0 46)\n", "< < < <[10] 47\n", "< < < < < 47\n", "< < < < <47\n", "> > > > >(ack 1 47)\n", "> > > > > (ack 1 46)\n", "> > > >[10] (ack 1 45)\n", "(used-hash 1 45)\n", "< < < <[10] 47\n", "> > > >[10] (ack 0 47)\n", "< < < <[10] 48\n", "< < < < < 48\n", "> > > > > (ack 0 48)\n", "< < < < < 49\n", "< < < < <49\n", "< < < < 49\n", "> > > > (ack 1 49)\n", "> > > > >(ack 1 48)\n", "> > > > > (ack 1 47)\n", "(used-hash 1 47)\n", "< < < < < 49\n", "> > > > > (ack 0 49)\n", "< < < < < 50\n", "< < < < <50\n", "> > > > >(ack 0 50)\n", "< < < < <51\n", "< < < < 51\n", "< < < <51\n", "> > > >(ack 1 51)\n", "> > > > (ack 1 50)\n", "> > > > >(ack 1 49)\n", "(used-hash 1 49)\n", "< < < < <51\n", "> > > > >(ack 0 51)\n", "< < < < <52\n", "< < < < 52\n", "> > > > (ack 0 52)\n", "< < < < 53\n", "< < < <53\n", "< < < 53\n", "> > > (ack 1 53)\n", "> > > >(ack 1 52)\n", "> > > > (ack 1 51)\n", "(used-hash 1 51)\n", "< < < < 53\n", "> > > > (ack 0 53)\n", "< < < < 54\n", "< < < <54\n", "> > > >(ack 0 54)\n", "< < < <55\n", "< < < 55\n", "< < <55\n", "> > >(ack 1 55)\n", "> > > (ack 1 54)\n", "> > > >(ack 1 53)\n", "(used-hash 1 53)\n", "< < < <55\n", "> > > >(ack 0 55)\n", "< < < <56\n", "< < < 56\n", "> > > (ack 0 56)\n", "< < < 57\n", "< < <57\n", "< < 57\n", "> > (ack 1 57)\n", "> > >(ack 1 56)\n", "> > > (ack 1 55)\n", "(used-hash 1 55)\n", "< < < 57\n", "> > > (ack 0 57)\n", "< < < 58\n", "< < <58\n", "> > >(ack 0 58)\n", "< < <59\n", "< < 59\n", "< <59\n", "> >(ack 1 59)\n", "> > (ack 1 58)\n", "> > >(ack 1 57)\n", "(used-hash 1 57)\n", "< < <59\n", "> > >(ack 0 59)\n", "< < <60\n", "< < 60\n", "> > (ack 0 60)\n", "< < 61\n", "< <61\n", "< 61\n", "<61\n" ] }, { "data": { "text/html": [ "61" ], "text/plain": [ "61" ] }, "execution_count": 125, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(ack 3 3)" ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(ack 3 3)\n", "(used-hash 3 3)\n", "<61\n" ] }, { "data": { "text/html": [ "61" ], "text/plain": [ "61" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(ack 3 3)" ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(ack 2 2)\n", "(used-hash 2 2)\n", "<7\n" ] }, { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(ack 2 2)" ] }, { "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 }