'''\n",
"Recursion Tools\n",
"===============\n",
"\n",
"Contain tools to print the call stack and cache the return values\n",
"of a recursive function.\n",
"'''\n",
"\n",
"import functools\n",
"\n",
"\n",
"def _argument_string(*args, **kwargs):\n",
" """Return the string representation of the list of arguments."""\n",
" return "({})".format(\n",
" ", ".join(\n",
" [\n",
" *["{!r}".format(v) for v in args], # arguments\n",
" *[\n",
" "{}={!r}".format(k, v) for k, v in kwargs.items()\n",
" ], # keyword arguments\n",
" ]\n",
" )\n",
" )\n",
"\n",
"\n",
"def print_function_call(f):\n",
" """Decorate a recursive function to print the call stack.\n",
"\n",
" The decorator also keep tracks of the number and depth of a recursive call to print the call stack.\n",
"\n",
" Parameters\n",
" ----------\n",
" f: Callable\n",
" A recursive function.\n",
"\n",
" Returns\n",
" -------\n",
" Callable:\n",
" The decorated function that also prints the function call\n",
" when called.\n",
"\n",
" Examples:\n",
" ---------\n",
" >>> @print_function_call\n",
" ... def fibonacci(n):\n",
" ... return fibonacci(n - 1) + fibonacci(n - 2) if n > 1 else 1 if n == 1 else 0\n",
" ...\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" 2:|fibonacci(4)\n",
" 3:||fibonacci(3)\n",
" 4:|||fibonacci(2)\n",
" 5:||||fibonacci(1)\n",
" 6:||||fibonacci(0)\n",
" 7:|||fibonacci(1)\n",
" 8:||fibonacci(2)\n",
" 9:|||fibonacci(1)\n",
" 10:|||fibonacci(0)\n",
" 11:|fibonacci(3)\n",
" 12:||fibonacci(2)\n",
" 13:|||fibonacci(1)\n",
" 14:|||fibonacci(0)\n",
" 15:||fibonacci(1)\n",
" Done\n",
" 5\n",
" """\n",
"\n",
" @functools.wraps(f) # give wrapper the identity of f and more\n",
" def wrapper(*args, **kwargs):\n",
" nonlocal count, depth\n",
" count += 1\n",
" depth += 1\n",
" call = "{}{}".format(f.__name__, _argument_string(*args, **kwargs))\n",
" print("{:>3}:{}{}".format(count, "|" * depth, call))\n",
"\n",
" value = f(*args, **kwargs) # calls f\n",
"\n",
" depth -= 1\n",
" if depth == -1:\n",
" print("Done")\n",
" count = 0\n",
" return value\n",
"\n",
" count, depth = 0, -1\n",
" return wrapper # return the decorated function\n",
"\n",
"\n",
"def caching(f):\n",
" """Cache the return value of a function that takes a single argument.\n",
"\n",
" Parameters:\n",
" -----------\n",
" f: Callable\n",
" A function that takes a single argument.\n",
"\n",
" Returns:\n",
" --------\n",
" Callable:\n",
" The function same as f but has its return valued automatically cached\n",
" when called. It has a method clear_cache to clear its cache.\n",
"\n",
" Examples:\n",
" ---------\n",
" >>> @print_function_call\n",
" ... @caching\n",
" ... def fibonacci(n):\n",
" ... return fibonacci(n - 1) + fibonacci(n - 2) if n > 1 else 1 if n == 1 else 0\n",
" ...\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" 2:|fibonacci(4)\n",
" 3:||fibonacci(3)\n",
" 4:|||fibonacci(2)\n",
" 5:||||fibonacci(1)\n",
" 6:||||fibonacci(0)\n",
" 7:|||fibonacci(1)\n",
" read from cache\n",
" 8:||fibonacci(2)\n",
" read from cache\n",
" 9:|fibonacci(3)\n",
" read from cache\n",
" Done\n",
" 5\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" read from cache\n",
" Done\n",
" 5\n",
" >>> fibonacci.clear_cache()\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" 2:|fibonacci(4)\n",
" 3:||fibonacci(3)\n",
" 4:|||fibonacci(2)\n",
" 5:||||fibonacci(1)\n",
" 6:||||fibonacci(0)\n",
" 7:|||fibonacci(1)\n",
" read from cache\n",
" 8:||fibonacci(2)\n",
" read from cache\n",
" 9:|fibonacci(3)\n",
" read from cache\n",
" Done\n",
" 5\n",
" """\n",
"\n",
" @functools.wraps(f)\n",
" def wrapper(n):\n",
" if n not in cache:\n",
" cache[n] = f(n)\n",
" else:\n",
" print("read from cache")\n",
" return cache[n]\n",
"\n",
" cache = {}\n",
" wrapper.clear_cache = lambda: cache.clear() # add method to clear cache\n",
" return wrapper\n",
" \n"
],
"text/latex": [
"\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
"\\PY{l+s+sd}{\\PYZsq{}\\PYZsq{}\\PYZsq{}}\n",
"\\PY{l+s+sd}{Recursion Tools}\n",
"\\PY{l+s+sd}{===============}\n",
"\n",
"\\PY{l+s+sd}{Contain tools to print the call stack and cache the return values}\n",
"\\PY{l+s+sd}{of a recursive function.}\n",
"\\PY{l+s+sd}{\\PYZsq{}\\PYZsq{}\\PYZsq{}}\n",
"\n",
"\\PY{k+kn}{import} \\PY{n+nn}{functools}\n",
"\n",
"\n",
"\\PY{k}{def} \\PY{n+nf}{\\PYZus{}argument\\PYZus{}string}\\PY{p}{(}\\PY{o}{*}\\PY{n}{args}\\PY{p}{,} \\PY{o}{*}\\PY{o}{*}\\PY{n}{kwargs}\\PY{p}{)}\\PY{p}{:}\n",
" \\PY{l+s+sd}{\\PYZdq{}\\PYZdq{}\\PYZdq{}Return the string representation of the list of arguments.\\PYZdq{}\\PYZdq{}\\PYZdq{}}\n",
" \\PY{k}{return} \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{(}\\PY{l+s+si}{\\PYZob{}\\PYZcb{}}\\PY{l+s+s2}{)}\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{.}\\PY{n}{format}\\PY{p}{(}\n",
" \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{, }\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{.}\\PY{n}{join}\\PY{p}{(}\n",
" \\PY{p}{[}\n",
" \\PY{o}{*}\\PY{p}{[}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+si}{\\PYZob{}!r\\PYZcb{}}\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{.}\\PY{n}{format}\\PY{p}{(}\\PY{n}{v}\\PY{p}{)} \\PY{k}{for} \\PY{n}{v} \\PY{o+ow}{in} \\PY{n}{args}\\PY{p}{]}\\PY{p}{,} \\PY{c+c1}{\\PYZsh{} arguments}\n",
" \\PY{o}{*}\\PY{p}{[}\n",
" \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+si}{\\PYZob{}\\PYZcb{}}\\PY{l+s+s2}{=}\\PY{l+s+si}{\\PYZob{}!r\\PYZcb{}}\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{.}\\PY{n}{format}\\PY{p}{(}\\PY{n}{k}\\PY{p}{,} \\PY{n}{v}\\PY{p}{)} \\PY{k}{for} \\PY{n}{k}\\PY{p}{,} \\PY{n}{v} \\PY{o+ow}{in} \\PY{n}{kwargs}\\PY{o}{.}\\PY{n}{items}\\PY{p}{(}\\PY{p}{)}\n",
" \\PY{p}{]}\\PY{p}{,} \\PY{c+c1}{\\PYZsh{} keyword arguments}\n",
" \\PY{p}{]}\n",
" \\PY{p}{)}\n",
" \\PY{p}{)}\n",
"\n",
"\n",
"\\PY{k}{def} \\PY{n+nf}{print\\PYZus{}function\\PYZus{}call}\\PY{p}{(}\\PY{n}{f}\\PY{p}{)}\\PY{p}{:}\n",
" \\PY{l+s+sd}{\\PYZdq{}\\PYZdq{}\\PYZdq{}Decorate a recursive function to print the call stack.}\n",
"\n",
"\\PY{l+s+sd}{ The decorator also keep tracks of the number and depth of a recursive call to print the call stack.}\n",
"\n",
"\\PY{l+s+sd}{ Parameters}\n",
"\\PY{l+s+sd}{ \\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}}\n",
"\\PY{l+s+sd}{ f: Callable}\n",
"\\PY{l+s+sd}{ A recursive function.}\n",
"\n",
"\\PY{l+s+sd}{ Returns}\n",
"\\PY{l+s+sd}{ \\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}}\n",
"\\PY{l+s+sd}{ Callable:}\n",
"\\PY{l+s+sd}{ The decorated function that also prints the function call}\n",
"\\PY{l+s+sd}{ when called.}\n",
"\n",
"\\PY{l+s+sd}{ Examples:}\n",
"\\PY{l+s+sd}{ \\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}}\n",
"\\PY{l+s+sd}{ \\PYZgt{}\\PYZgt{}\\PYZgt{} @print\\PYZus{}function\\PYZus{}call}\n",
"\\PY{l+s+sd}{ ... def fibonacci(n):}\n",
"\\PY{l+s+sd}{ ... return fibonacci(n \\PYZhy{} 1) + fibonacci(n \\PYZhy{} 2) if n \\PYZgt{} 1 else 1 if n == 1 else 0}\n",
"\\PY{l+s+sd}{ ...}\n",
"\\PY{l+s+sd}{ \\PYZgt{}\\PYZgt{}\\PYZgt{} fibonacci(5)}\n",
"\\PY{l+s+sd}{ 1:fibonacci(5)}\n",
"\\PY{l+s+sd}{ 2:|fibonacci(4)}\n",
"\\PY{l+s+sd}{ 3:||fibonacci(3)}\n",
"\\PY{l+s+sd}{ 4:|||fibonacci(2)}\n",
"\\PY{l+s+sd}{ 5:||||fibonacci(1)}\n",
"\\PY{l+s+sd}{ 6:||||fibonacci(0)}\n",
"\\PY{l+s+sd}{ 7:|||fibonacci(1)}\n",
"\\PY{l+s+sd}{ 8:||fibonacci(2)}\n",
"\\PY{l+s+sd}{ 9:|||fibonacci(1)}\n",
"\\PY{l+s+sd}{ 10:|||fibonacci(0)}\n",
"\\PY{l+s+sd}{ 11:|fibonacci(3)}\n",
"\\PY{l+s+sd}{ 12:||fibonacci(2)}\n",
"\\PY{l+s+sd}{ 13:|||fibonacci(1)}\n",
"\\PY{l+s+sd}{ 14:|||fibonacci(0)}\n",
"\\PY{l+s+sd}{ 15:||fibonacci(1)}\n",
"\\PY{l+s+sd}{ Done}\n",
"\\PY{l+s+sd}{ 5}\n",
"\\PY{l+s+sd}{ \\PYZdq{}\\PYZdq{}\\PYZdq{}}\n",
"\n",
" \\PY{n+nd}{@functools}\\PY{o}{.}\\PY{n}{wraps}\\PY{p}{(}\\PY{n}{f}\\PY{p}{)} \\PY{c+c1}{\\PYZsh{} give wrapper the identity of f and more}\n",
" \\PY{k}{def} \\PY{n+nf}{wrapper}\\PY{p}{(}\\PY{o}{*}\\PY{n}{args}\\PY{p}{,} \\PY{o}{*}\\PY{o}{*}\\PY{n}{kwargs}\\PY{p}{)}\\PY{p}{:}\n",
" \\PY{k}{nonlocal} \\PY{n}{count}\\PY{p}{,} \\PY{n}{depth}\n",
" \\PY{n}{count} \\PY{o}{+}\\PY{o}{=} \\PY{l+m+mi}{1}\n",
" \\PY{n}{depth} \\PY{o}{+}\\PY{o}{=} \\PY{l+m+mi}{1}\n",
" \\PY{n}{call} \\PY{o}{=} \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+si}{\\PYZob{}\\PYZcb{}}\\PY{l+s+si}{\\PYZob{}\\PYZcb{}}\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{.}\\PY{n}{format}\\PY{p}{(}\\PY{n}{f}\\PY{o}{.}\\PY{n+nv+vm}{\\PYZus{}\\PYZus{}name\\PYZus{}\\PYZus{}}\\PY{p}{,} \\PY{n}{\\PYZus{}argument\\PYZus{}string}\\PY{p}{(}\\PY{o}{*}\\PY{n}{args}\\PY{p}{,} \\PY{o}{*}\\PY{o}{*}\\PY{n}{kwargs}\\PY{p}{)}\\PY{p}{)}\n",
" \\PY{n+nb}{print}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+si}{\\PYZob{}:\\PYZgt{}3\\PYZcb{}}\\PY{l+s+s2}{:}\\PY{l+s+si}{\\PYZob{}\\PYZcb{}}\\PY{l+s+si}{\\PYZob{}\\PYZcb{}}\\PY{l+s+s2}{\\PYZdq{}}\\PY{o}{.}\\PY{n}{format}\\PY{p}{(}\\PY{n}{count}\\PY{p}{,} \\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{|}\\PY{l+s+s2}{\\PYZdq{}} \\PY{o}{*} \\PY{n}{depth}\\PY{p}{,} \\PY{n}{call}\\PY{p}{)}\\PY{p}{)}\n",
"\n",
" \\PY{n}{value} \\PY{o}{=} \\PY{n}{f}\\PY{p}{(}\\PY{o}{*}\\PY{n}{args}\\PY{p}{,} \\PY{o}{*}\\PY{o}{*}\\PY{n}{kwargs}\\PY{p}{)} \\PY{c+c1}{\\PYZsh{} calls f}\n",
"\n",
" \\PY{n}{depth} \\PY{o}{\\PYZhy{}}\\PY{o}{=} \\PY{l+m+mi}{1}\n",
" \\PY{k}{if} \\PY{n}{depth} \\PY{o}{==} \\PY{o}{\\PYZhy{}}\\PY{l+m+mi}{1}\\PY{p}{:}\n",
" \\PY{n+nb}{print}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{Done}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{)}\n",
" \\PY{n}{count} \\PY{o}{=} \\PY{l+m+mi}{0}\n",
" \\PY{k}{return} \\PY{n}{value}\n",
"\n",
" \\PY{n}{count}\\PY{p}{,} \\PY{n}{depth} \\PY{o}{=} \\PY{l+m+mi}{0}\\PY{p}{,} \\PY{o}{\\PYZhy{}}\\PY{l+m+mi}{1}\n",
" \\PY{k}{return} \\PY{n}{wrapper} \\PY{c+c1}{\\PYZsh{} return the decorated function}\n",
"\n",
"\n",
"\\PY{k}{def} \\PY{n+nf}{caching}\\PY{p}{(}\\PY{n}{f}\\PY{p}{)}\\PY{p}{:}\n",
" \\PY{l+s+sd}{\\PYZdq{}\\PYZdq{}\\PYZdq{}Cache the return value of a function that takes a single argument.}\n",
"\n",
"\\PY{l+s+sd}{ Parameters:}\n",
"\\PY{l+s+sd}{ \\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}}\n",
"\\PY{l+s+sd}{ f: Callable}\n",
"\\PY{l+s+sd}{ A function that takes a single argument.}\n",
"\n",
"\\PY{l+s+sd}{ Returns:}\n",
"\\PY{l+s+sd}{ \\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}}\n",
"\\PY{l+s+sd}{ Callable:}\n",
"\\PY{l+s+sd}{ The function same as f but has its return valued automatically cached}\n",
"\\PY{l+s+sd}{ when called. It has a method clear\\PYZus{}cache to clear its cache.}\n",
"\n",
"\\PY{l+s+sd}{ Examples:}\n",
"\\PY{l+s+sd}{ \\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}\\PYZhy{}}\n",
"\\PY{l+s+sd}{ \\PYZgt{}\\PYZgt{}\\PYZgt{} @print\\PYZus{}function\\PYZus{}call}\n",
"\\PY{l+s+sd}{ ... @caching}\n",
"\\PY{l+s+sd}{ ... def fibonacci(n):}\n",
"\\PY{l+s+sd}{ ... return fibonacci(n \\PYZhy{} 1) + fibonacci(n \\PYZhy{} 2) if n \\PYZgt{} 1 else 1 if n == 1 else 0}\n",
"\\PY{l+s+sd}{ ...}\n",
"\\PY{l+s+sd}{ \\PYZgt{}\\PYZgt{}\\PYZgt{} fibonacci(5)}\n",
"\\PY{l+s+sd}{ 1:fibonacci(5)}\n",
"\\PY{l+s+sd}{ 2:|fibonacci(4)}\n",
"\\PY{l+s+sd}{ 3:||fibonacci(3)}\n",
"\\PY{l+s+sd}{ 4:|||fibonacci(2)}\n",
"\\PY{l+s+sd}{ 5:||||fibonacci(1)}\n",
"\\PY{l+s+sd}{ 6:||||fibonacci(0)}\n",
"\\PY{l+s+sd}{ 7:|||fibonacci(1)}\n",
"\\PY{l+s+sd}{ read from cache}\n",
"\\PY{l+s+sd}{ 8:||fibonacci(2)}\n",
"\\PY{l+s+sd}{ read from cache}\n",
"\\PY{l+s+sd}{ 9:|fibonacci(3)}\n",
"\\PY{l+s+sd}{ read from cache}\n",
"\\PY{l+s+sd}{ Done}\n",
"\\PY{l+s+sd}{ 5}\n",
"\\PY{l+s+sd}{ \\PYZgt{}\\PYZgt{}\\PYZgt{} fibonacci(5)}\n",
"\\PY{l+s+sd}{ 1:fibonacci(5)}\n",
"\\PY{l+s+sd}{ read from cache}\n",
"\\PY{l+s+sd}{ Done}\n",
"\\PY{l+s+sd}{ 5}\n",
"\\PY{l+s+sd}{ \\PYZgt{}\\PYZgt{}\\PYZgt{} fibonacci.clear\\PYZus{}cache()}\n",
"\\PY{l+s+sd}{ \\PYZgt{}\\PYZgt{}\\PYZgt{} fibonacci(5)}\n",
"\\PY{l+s+sd}{ 1:fibonacci(5)}\n",
"\\PY{l+s+sd}{ 2:|fibonacci(4)}\n",
"\\PY{l+s+sd}{ 3:||fibonacci(3)}\n",
"\\PY{l+s+sd}{ 4:|||fibonacci(2)}\n",
"\\PY{l+s+sd}{ 5:||||fibonacci(1)}\n",
"\\PY{l+s+sd}{ 6:||||fibonacci(0)}\n",
"\\PY{l+s+sd}{ 7:|||fibonacci(1)}\n",
"\\PY{l+s+sd}{ read from cache}\n",
"\\PY{l+s+sd}{ 8:||fibonacci(2)}\n",
"\\PY{l+s+sd}{ read from cache}\n",
"\\PY{l+s+sd}{ 9:|fibonacci(3)}\n",
"\\PY{l+s+sd}{ read from cache}\n",
"\\PY{l+s+sd}{ Done}\n",
"\\PY{l+s+sd}{ 5}\n",
"\\PY{l+s+sd}{ \\PYZdq{}\\PYZdq{}\\PYZdq{}}\n",
"\n",
" \\PY{n+nd}{@functools}\\PY{o}{.}\\PY{n}{wraps}\\PY{p}{(}\\PY{n}{f}\\PY{p}{)}\n",
" \\PY{k}{def} \\PY{n+nf}{wrapper}\\PY{p}{(}\\PY{n}{n}\\PY{p}{)}\\PY{p}{:}\n",
" \\PY{k}{if} \\PY{n}{n} \\PY{o+ow}{not} \\PY{o+ow}{in} \\PY{n}{cache}\\PY{p}{:}\n",
" \\PY{n}{cache}\\PY{p}{[}\\PY{n}{n}\\PY{p}{]} \\PY{o}{=} \\PY{n}{f}\\PY{p}{(}\\PY{n}{n}\\PY{p}{)}\n",
" \\PY{k}{else}\\PY{p}{:}\n",
" \\PY{n+nb}{print}\\PY{p}{(}\\PY{l+s+s2}{\\PYZdq{}}\\PY{l+s+s2}{read from cache}\\PY{l+s+s2}{\\PYZdq{}}\\PY{p}{)}\n",
" \\PY{k}{return} \\PY{n}{cache}\\PY{p}{[}\\PY{n}{n}\\PY{p}{]}\n",
"\n",
" \\PY{n}{cache} \\PY{o}{=} \\PY{p}{\\PYZob{}}\\PY{p}{\\PYZcb{}}\n",
" \\PY{n}{wrapper}\\PY{o}{.}\\PY{n}{clear\\PYZus{}cache} \\PY{o}{=} \\PY{k}{lambda}\\PY{p}{:} \\PY{n}{cache}\\PY{o}{.}\\PY{n}{clear}\\PY{p}{(}\\PY{p}{)} \\PY{c+c1}{\\PYZsh{} add method to clear cache}\n",
" \\PY{k}{return} \\PY{n}{wrapper}\n",
"\\end{Verbatim}\n"
],
"text/plain": [
"'''\n",
"Recursion Tools\n",
"===============\n",
"\n",
"Contain tools to print the call stack and cache the return values\n",
"of a recursive function.\n",
"'''\n",
"\n",
"import functools\n",
"\n",
"\n",
"def _argument_string(*args, **kwargs):\n",
" \"\"\"Return the string representation of the list of arguments.\"\"\"\n",
" return \"({})\".format(\n",
" \", \".join(\n",
" [\n",
" *[\"{!r}\".format(v) for v in args], # arguments\n",
" *[\n",
" \"{}={!r}\".format(k, v) for k, v in kwargs.items()\n",
" ], # keyword arguments\n",
" ]\n",
" )\n",
" )\n",
"\n",
"\n",
"def print_function_call(f):\n",
" \"\"\"Decorate a recursive function to print the call stack.\n",
"\n",
" The decorator also keep tracks of the number and depth of a recursive call to print the call stack.\n",
"\n",
" Parameters\n",
" ----------\n",
" f: Callable\n",
" A recursive function.\n",
"\n",
" Returns\n",
" -------\n",
" Callable:\n",
" The decorated function that also prints the function call\n",
" when called.\n",
"\n",
" Examples:\n",
" ---------\n",
" >>> @print_function_call\n",
" ... def fibonacci(n):\n",
" ... return fibonacci(n - 1) + fibonacci(n - 2) if n > 1 else 1 if n == 1 else 0\n",
" ...\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" 2:|fibonacci(4)\n",
" 3:||fibonacci(3)\n",
" 4:|||fibonacci(2)\n",
" 5:||||fibonacci(1)\n",
" 6:||||fibonacci(0)\n",
" 7:|||fibonacci(1)\n",
" 8:||fibonacci(2)\n",
" 9:|||fibonacci(1)\n",
" 10:|||fibonacci(0)\n",
" 11:|fibonacci(3)\n",
" 12:||fibonacci(2)\n",
" 13:|||fibonacci(1)\n",
" 14:|||fibonacci(0)\n",
" 15:||fibonacci(1)\n",
" Done\n",
" 5\n",
" \"\"\"\n",
"\n",
" @functools.wraps(f) # give wrapper the identity of f and more\n",
" def wrapper(*args, **kwargs):\n",
" nonlocal count, depth\n",
" count += 1\n",
" depth += 1\n",
" call = \"{}{}\".format(f.__name__, _argument_string(*args, **kwargs))\n",
" print(\"{:>3}:{}{}\".format(count, \"|\" * depth, call))\n",
"\n",
" value = f(*args, **kwargs) # calls f\n",
"\n",
" depth -= 1\n",
" if depth == -1:\n",
" print(\"Done\")\n",
" count = 0\n",
" return value\n",
"\n",
" count, depth = 0, -1\n",
" return wrapper # return the decorated function\n",
"\n",
"\n",
"def caching(f):\n",
" \"\"\"Cache the return value of a function that takes a single argument.\n",
"\n",
" Parameters:\n",
" -----------\n",
" f: Callable\n",
" A function that takes a single argument.\n",
"\n",
" Returns:\n",
" --------\n",
" Callable:\n",
" The function same as f but has its return valued automatically cached\n",
" when called. It has a method clear_cache to clear its cache.\n",
"\n",
" Examples:\n",
" ---------\n",
" >>> @print_function_call\n",
" ... @caching\n",
" ... def fibonacci(n):\n",
" ... return fibonacci(n - 1) + fibonacci(n - 2) if n > 1 else 1 if n == 1 else 0\n",
" ...\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" 2:|fibonacci(4)\n",
" 3:||fibonacci(3)\n",
" 4:|||fibonacci(2)\n",
" 5:||||fibonacci(1)\n",
" 6:||||fibonacci(0)\n",
" 7:|||fibonacci(1)\n",
" read from cache\n",
" 8:||fibonacci(2)\n",
" read from cache\n",
" 9:|fibonacci(3)\n",
" read from cache\n",
" Done\n",
" 5\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" read from cache\n",
" Done\n",
" 5\n",
" >>> fibonacci.clear_cache()\n",
" >>> fibonacci(5)\n",
" 1:fibonacci(5)\n",
" 2:|fibonacci(4)\n",
" 3:||fibonacci(3)\n",
" 4:|||fibonacci(2)\n",
" 5:||||fibonacci(1)\n",
" 6:||||fibonacci(0)\n",
" 7:|||fibonacci(1)\n",
" read from cache\n",
" 8:||fibonacci(2)\n",
" read from cache\n",
" 9:|fibonacci(3)\n",
" read from cache\n",
" Done\n",
" 5\n",
" \"\"\"\n",
"\n",
" @functools.wraps(f)\n",
" def wrapper(n):\n",
" if n not in cache:\n",
" cache[n] = f(n)\n",
" else:\n",
" print(\"read from cache\")\n",
" return cache[n]\n",
"\n",
" cache = {}\n",
" wrapper.clear_cache = lambda: cache.clear() # add method to clear cache\n",
" return wrapper"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from IPython.display import Code\n",
"\n",
"Code(filename=\"recurtools.py\", language=\"python\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The module provides the decorators `print_function_call` and `caching` defined earlier."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"ExecuteTime": {
"end_time": "2020-10-31T02:44:10.246664Z",
"start_time": "2020-10-31T02:44:10.238773Z"
},
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"import recurtools as rc\n",
"\n",
"\n",
"@rc.print_function_call\n",
"@rc.caching\n",
"def factorial(n):\n",
" return factorial(n - 1) if n > 1 else 1"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"ExecuteTime": {
"end_time": "2020-10-31T02:44:10.254591Z",
"start_time": "2020-10-31T02:44:10.249209Z"
},
"scrolled": true,
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 1:factorial(5)\n",
" 2:|factorial(4)\n",
" 3:||factorial(3)\n",
" 4:|||factorial(2)\n",
" 5:||||factorial(1)\n",
"Done\n",
" 1:factorial(5)\n",
"read from cache\n",
"Done\n",
" 1:factorial(5)\n",
" 2:|factorial(4)\n",
" 3:||factorial(3)\n",
" 4:|||factorial(2)\n",
" 5:||||factorial(1)\n",
"Done\n"
]
},
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"factorial(5)\n",
"factorial(5)\n",
"factorial.clear_cache()\n",
"factorial(5)"
]
}
],
"metadata": {
"celltoolbar": "Create Assignment",
"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.8.12"
},
"latex_envs": {
"LaTeX_envs_menu_present": true,
"autoclose": false,
"autocomplete": true,
"bibliofile": "biblio.bib",
"cite_by": "apalike",
"current_citInitial": 1,
"eqLabelWithNumbers": true,
"eqNumInitial": 1,
"hotkeys": {
"equation": "Ctrl-E",
"itemize": "Ctrl-I"
},
"labels_anchors": false,
"latex_user_defs": false,
"report_style_numbering": false,
"user_envs_cfg": false
},
"rise": {
"enable_chalkboard": true,
"scroll": true,
"theme": "white"
},
"toc": {
"base_numbering": 1,
"nav_menu": {
"height": "195px",
"width": "330px"
},
"number_sections": true,
"sideBar": true,
"skip_h1_title": true,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {
"height": "454.418px",
"left": "1533px",
"top": "110.284px",
"width": "261px"
},
"toc_section_display": true,
"toc_window_display": false
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}