{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "# 　出発ゲート：一列にならんで、順番に　"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "***今回の話題:***\n",
    "\n",
    "----\n",
    "\n",
    "- リストとタブル　　一列に並んだデータを保持するデータ型\n",
    "   - mutableとimmutable\n",
    "- 繰り返し(for文、　リスト内包表記、ジェネレーター式)\n",
    "- 簡単なグラフの作成\n",
    "- (例外処理：入力値のチェック)\n",
    "\n",
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## 数列を作ってみる。\n",
    "\n",
    "前回はPythonで関数を定義する方法の基本を学びました。\n",
    "今回は、実際に数値の値を返す関数を定義して見ます。\n",
    "\n",
    "まずフィボナッチの数列を求める関数を定義してみましょう。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "## フィボナッチ数列とは？\n",
    "\n",
    "フィボナッチ数列([1, 1, 2, 3, 5, 8, 13, 21,...])とは\n",
    "\n",
    "\n",
    "$a_0 = 1$ \n",
    "\n",
    "$a_1 = 1$\n",
    "\n",
    "$a_n = a_{n-1} + a_{n-2} \\qquad \\text{ ( for}\\  n \\gt 1 \\text{ )}$\n",
    "\n",
    "で定義される数列です。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "漸化式を解いて、\n",
    "\n",
    "$a_=\\frac{1}{\\sqrt 5}\\left\\lbrace(\\frac{1+\\sqrt 5}{2})^{n+1} -  (\\frac{1-\\sqrt 5}{2})^{n+1}\\right\\rbrace$\n",
    "\n",
    "と定義することも可能です。　"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "##　関数としての数列\n",
    "整数$n$に対してフィボナッチの数列の$n$番目の数、$a_n$は一意に定りますから、これを関数と考えることもできます。\n",
    "\n",
    "$fib: n \\mapsto a_n$\n",
    "\n",
    "漸化式を直接使って、Pythonでこの数列を関数として表現して見ます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def fib(n:int)->int:\n",
    "    \"\"\"\n",
    "    フィボナッチ数列の　n番目(n:= 0,1,2,..)の値を求める。\n",
    "    例：\n",
    "    >>> fib(2)\n",
    "    2\n",
    "    >>> fib(5)\n",
    "    8\n",
    "    >>> fib(0), fib(1), fib(2), fib(3), fib(4), fib(5),\n",
    "    (1, 1, 2, 3, 5, 8)\n",
    "    \"\"\"\n",
    "    if n == 0 or n == 1:\n",
    "        value= 1\n",
    "    else:\n",
    "        value= fib(n-1) + fib(n-2)\n",
    "    return value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "notes"
    },
    "tags": []
   },
   "source": [
    "`return`は関数の呼び出し元に、関数の結果を送りだします。ここでは変数`value`の数値を返しています。Pythonでは、数値だけではなく様々なデータを返すことが可能です。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "## helpメッセージ\n",
    "`fib`の定義には`docstring`が含まれています。\n",
    "これを使うことで、実行時にこの関数の使い方を確認することができます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function fib in module __main__:\n",
      "\n",
      "fib(n: int) -> int\n",
      "    フィボナッチ数列の　n番目(n:= 0,1,2,..)の値を求める。\n",
      "    例：\n",
      "    >>> fib(2)\n",
      "    2\n",
      "    >>> fib(5)\n",
      "    8\n",
      "    >>> fib(0), fib(1), fib(2), fib(3), fib(4), fib(5),\n",
      "    (1, 1, 2, 3, 5, 8)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(fib)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "定義した関数 `fib(n)` が、正しくフィボナッチの数列を求めているかどうかを確かめて見ましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, 1, 2, 3, 5, 8)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fib(0), fib(1), fib(2), fib(3), fib(4), fib(5),"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "とフィボナッチ数が正しく計算されていることが確認できました。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## 繰り返し： for文\n",
    "\n",
    "さきほどは、`fib`関数の動作確認のために、`fib(0),...`などを書き並べました。\n",
    "\n",
    "しかし、いちいち引数の値を書くのは大変です。\n",
    "\n",
    "`for`文を使えば異なる引数の値に対して、関数の値を求めることも簡単です。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fib( 0 )= 1, fib( 1 )= 1, fib( 2 )= 2, fib( 3 )= 3, fib( 4 )= 5, fib( 5 )= 8, fib( 6 )= 13, fib( 7 )= 21, fib( 8 )= 34, fib( 9 )= 55, ...\n"
     ]
    }
   ],
   "source": [
    "for i in range(0,10,1):\n",
    "    print(\"fib(\",i,\")=\",fib(i), end=\", \")\n",
    "print(\"...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "printで　フォーマット文字列を使った例。次回の講座で説明します。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fib(0) = 1, fib(1) = 1, fib(2) = 2, fib(3) = 3, fib(4) = 5, fib(5) = 8, fib(6) = 13, fib(7) = 21, fib(8) = 34, fib(9) = 55, ...\n"
     ]
    }
   ],
   "source": [
    "for i in range(0,10,1):\n",
    "    print(f\"fib({i}) = {fib(i)}\", end=\", \")\n",
    "print(\"...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "### for文\n",
    "\n",
    "`for`文は\n",
    "\n",
    "```\n",
    "for <識別子> in <リストやタプルなどのオブジェクト:iterable> :\n",
    "    <プログラム文>\n",
    "    ...\n",
    "```\n",
    "\n",
    "の形をしています。この文を実行すると、`<リスト、タプル、などのオブジェクト>`の要素毎に、その値が`<識別子>`に割り当てられて、`:`の後に続く　`<プログラム文>`　が実行されます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fib( 0 )= 1, fib( 1 )= 1, fib( 2 )= 2, fib( 3 )= 3, fib( 4 )= 5, fib( 5 )= 8, fib( 6 )= 13, fib( 7 )= 21, fib( 8 )= 34, fib( 9 )= 55, ...\n"
     ]
    }
   ],
   "source": [
    "for i in range(0,10,1):\n",
    "    print(\"fib(\",i,\")=\", fib(i), end=\", \")\n",
    "print(\"...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "この繰り返しの中で、`i`は `0`から始まり、`i < 10`の範囲で`1`ずつ増えていることがわかります。　\n",
    "`list()`関数を使うことで明示的に値の範囲を確認できます。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "`range(0,10,1)`は0から始まって、10以下の、1づつ増える整数の列を表現しています。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(range(0, 10, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "notes"
    },
    "tags": []
   },
   "source": [
    "`list`は`type`型オブジェクトです。　\n",
    "`list()`は`list`型への変換あるいは`list`型オブジェクトの生成子ということになります。\n",
    "`range(0,10,1)`は`range`クラスの生成子を使っていることになります。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "range"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(range(0, 10, 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## リストとタプル(tuple)\n",
    "\n",
    "`for`文を使うことで、　一連の$n$の値に対して、フィボナッチ数列の値を求めることができました。　せっかく計算したこれらの結果を印刷するだけでなく、データとして再利用できれば便利です。　\n",
    "\n",
    "こうした一連のデータをまとめて表現するために、Pythonには`リスト` と `タブル` という2種類の一列に並んだデータを表現するデータ型が用意されています。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### リスト(list)\n",
    "1から5までの整数に対するフィボナッチ数列の値を要素に持つ`リスト`は次のようにして、作ることができます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 1, 2, 3, 5, 8]\n"
     ]
    }
   ],
   "source": [
    "list_of_fib=[ fib(0),fib(1),fib(2),fib(3),fib(4),fib(5) ]\n",
    "print(list_of_fib)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### タプル(tuple)\n",
    "一方、1から5までの整数に対するフィボナッチ数列の値を要素に持つ`タプル`は次のように作ることができます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 1, 2, 3, 5, 8)\n"
     ]
    }
   ],
   "source": [
    "tuple_of_fib=( fib(0),fib(1),fib(2),fib(3),fib(4),fib(5) )\n",
    "print(tuple_of_fib)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "このように、リストは`[]`で囲まれたデータの並び、タプルは`()`で囲まれたデータの並びとして表現されます。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "リストやタプルは、`for`文での変数の範囲を指定するために使うことができます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "for loop with a list:\n",
      "1, 1, 2, 3, 5, 8, \n",
      "***\n",
      "for loop with a tuple:\n",
      "1, 1, 2, 3, 5, 8, "
     ]
    }
   ],
   "source": [
    "print(\"for loop with a list:\")\n",
    "for i in list_of_fib:\n",
    "    print(i, end=\", \")\n",
    "\n",
    "print(\"\\n***\")\n",
    "print(\"for loop with a tuple:\")\n",
    "for i in tuple_of_fib:\n",
    "    print(i, end=\", \")\n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### リテラル定数としてのリストとタプル\n",
    "\n",
    "- `リスト`:記号`[`と`]`でリストの要素を`,`で区切って並べる。　(`[1,2,3]`と`[1,2,3,]`は同じ意味)\n",
    "- `タブル`:記号`(`と`)`でリストの要素を`,`で区切って並べる。(`(1,2,3)`と`(1,2,3,)`は同じ意味)\n",
    "- `[]` は空のリスト(要素数が0）を表す。\n",
    "- `()`は空のタプル（要素数が0）を表す。\n",
    "- ***要素数が1のタプルは　`(1,)`と書く必要がある。*** [`（1）`は 数式と解釈されるため]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, False)"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[1] == [1,], (1)==(1,)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### リスト／タプル　要素の取り出し。\n",
    "リストやタプルの要素を取り出すことができます。\n",
    "\n",
    "- 最初の要素は、 `S[0]` \n",
    "- 最後の要素は、　`S[-1]`\n",
    "- $n+1$番目の要素は　`S[n]`\n",
    "- $n+1$番目から$m+1$番目までの要素は　`S[n:m]`\n",
    "- $n+1$番目から最後の要素までは　`S[n:]`\n",
    "- $n+1$番目から$m+1$番目まで,$l$ 置きの要素は　`S[n:m:l]`\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### list/tupleと要素指定\n",
    "pythonでは*指標（インデックス）*は要素と要素の*間*を指していると考えると、理解しやすいでしょう。\n",
    "\n",
    "![Pythonでのリスト／タプルの要素とインデックスの関係](./_images/list-tuple-index.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 1, 2, 3, 5, 8]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list_of_fib"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### 部分列の取り出し(スライス）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 5]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list_of_fib[0:-1:2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "`0:-1:2`はpythonではスライスと呼ばれます。\n",
    "\n",
    "![Pythonでのリスト／タプルの要素とスライス](./_images/list-tuple-slice.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### 負の増分\n",
    "スライスの増分を負の整数にすれば、リスト／タプルを逆順に取り出すことも可能です。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[8, 5, 3, 2, 1, 1]"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list_of_fib[:-8:-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(8, 5, 3, 2, 1, 1)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tuple_of_fib[:-8:-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 2, 5]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s= slice(0,-1,2) #スライスオブジェクトを作成する。\n",
    "list_of_fib[s]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 5] (1, 5)\n",
      "[2, 3, 5] (2, 3, 5)\n",
      "[1, 2, 5] (1, 2, 5)\n"
     ]
    }
   ],
   "source": [
    "print([list_of_fib[0], list_of_fib[4]], (tuple_of_fib[0],tuple_of_fib[4]))\n",
    "print(list_of_fib[2:5], tuple_of_fib[2:5])\n",
    "print(list_of_fib[0:-1:2], tuple_of_fib[0:-1:2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 5] (1, 5)\n",
      "[2, 3, 5] (2, 3, 5)\n",
      "[1, 2, 5] (1, 2, 5)\n"
     ]
    }
   ],
   "source": [
    "print([list_of_fib[0], list_of_fib[4]], (tuple_of_fib[0],tuple_of_fib[4]))\n",
    "print(list_of_fib[2:5], tuple_of_fib[2:5])\n",
    "print(list_of_fib[0:-1:2], tuple_of_fib[0:-1:2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "### 変更可能か変更不可能か、それが問題 :リストとタプルの使い分け\n",
    "\n",
    "\n",
    "-　リストもタプルも　n番目(nは0始まり）の要素を　`[n]`を変数名(オブジェクト）に追加することで取り出すことができます。\n",
    "\n",
    "\n",
    "ところが\n",
    "\n",
    "- リストは要素を更新することができる。(mutable)\n",
    "- タプルは要素を変更することができない。(immutable)\n",
    "\n",
    "という大きな違いがあります。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "リストの最初の要素(0番の要素）を置き換えてみます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "list: [2, 1, 2, 3, 5, 8]\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    list_of_fib[0]=2; print(\"list:\", list_of_fib)\n",
    "except TypeError as err:\n",
    "    print(\"TypeError:\",err)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "tupleの要素を置き換えようとすると、..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TypeError: 'tuple' object does not support item assignment\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    tuple_of_fib[0]=2; print(\"tuple:\", tuple_of_fib)\n",
    "except TypeError as err:\n",
    "        print(\"TypeError:\",err)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "とエラー（TypeError 例外発生)となります。\n",
    "このように、　リストの要素を置き換えることはできますが、タプルの要素を置き換えることはできません。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "#### リストへの要素の追加\n",
    "\n",
    "リスト／タプルの要素の変更と同様に、リストに要素を追加する(`append`)ことはできますが、タプルに要素を追加することはできません。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "追加前： 140681530275776 [2, 1, 2, 3, 5, 8]\n",
      "追加後： 140681530275776 [2, 1, 2, 3, 5, 8, 13]\n"
     ]
    }
   ],
   "source": [
    "#リストへの要素の追加 append\n",
    "print(\"追加前：\", id(list_of_fib), list_of_fib )\n",
    "list_of_fib.append(fib(len(list_of_fib)))\n",
    "print(\"追加後：\", id(list_of_fib), list_of_fib)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要素の追加前後で、リストの　`id`が変わっていないことに注意してください。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### タプルへの要素の追加"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "追加前： 140681530408672 (1, 1, 2, 3, 5, 8)\n",
      "AttributeError: 'tuple' object has no attribute 'append'\n",
      "追加後： 140681530408576 (1, 1, 2, 3, 5, 8, 13)\n"
     ]
    }
   ],
   "source": [
    "# タプルへの要素の追加\n",
    "print(\"追加前：\",id(tuple_of_fib), tuple_of_fib)\n",
    "try:\n",
    "    tuple_of_fib.append(fib(len(tuple_of_fib)))\n",
    "    print(\"追加後：\", id(tuple_of_fib), tuple_of_fib)\n",
    "except AttributeError as err:\n",
    "    print(\"AttributeError:\", err)\n",
    "\n",
    "tuple_of_fib=tuple_of_fib + (fib(len(tuple_of_fib)),)\n",
    "print(\"追加後：\",id(tuple_of_fib), tuple_of_fib)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "というように、タプルに要素を追加することはできません。そもそもタプルは`.append`メソッドを持っていません（`AttributeError`）。\n",
    "\n",
    "タプルに新しい要素を付け加えたタプルを作り直して、変数に割り当て直すことは可能です。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## リスト内包表記(List comprehension)　と ジェネレーター式(Generator expression)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "### リスト内包表記(List comprehension)\n",
    "フィボナッチ数列の最初の10項を成分にもつリストはリスト内包表記を使って作ることができます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# リスト内包表記（list comprehension\n",
    "list_of_fibs=[fib(i) for i in range(10)]\n",
    "list_of_fibs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### ジェネレーター式(Generator expression)\n",
    "ジェネレーター式はリスト内包表記に似た形ですが、`()`で囲われています。(リスト内包表記は`[]`)\n",
    "\n",
    "ジェネレーター式の値は ジェネレーター　オブジェクトです。\n",
    "\n",
    "ジェネレーター　オブジェクトは、`next()`で呼ばれるたびに、次の要素を値として返します。\n",
    "[参考: このように、`next()`の呼び出しに対して、次々と値を返すオブジェクトを`iterable`と呼びます。]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "type of generator expression: <class 'generator'>\n",
      "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, "
     ]
    }
   ],
   "source": [
    "# ジェネレーター式\n",
    "g=(fib(i) for i in range(10))\n",
    "print (\"type of generator expression:\", type(g))\n",
    "for f in range(10):\n",
    "    print(next(g),end=\", \")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### for 文での　ジェネレーター式\n",
    "ジェネレーター式は`for()`文で変数の範囲を示すために利用できます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, "
     ]
    }
   ],
   "source": [
    "# ジェネレーター式\n",
    "g=(fib(i) for i in range(10))\n",
    "for i in g:\n",
    "    print(i,end=\", \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, "
     ]
    }
   ],
   "source": [
    "for x in (fib(i) for i in range(10)):\n",
    "    print (x, end=\", \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 1 2 3 5 8 13 21 34 55 \n"
     ]
    }
   ],
   "source": [
    "# generatorを使ってみる。\n",
    "g=((fib(i) for i in range(10)))\n",
    "try:\n",
    "    while(x:=next(g)): # walrus 式\n",
    "        print(x, end=\" \")\n",
    "except StopIteration:\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "### ジェネレーター式とリスト内包表記の一般形\n",
    "```\n",
    "# Generator expression -- returns generator object(iterable)\n",
    "stripped_iter = (line.strip() for line in line_list)\n",
    "\n",
    "# List comprehension -- returns list\n",
    "stripped_list = [line.strip() for line in line_list]\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 1, 2, 3, 5, 8]\n"
     ]
    }
   ],
   "source": [
    "print([fib(i) for i in range(6)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "### ジェネレーター式とリスト内包表記　とmap関数\n",
    "ジェネレーター式/リスト内包表記は`map`関数とほぼ等価です。 \n",
    "ジェネレーター式をつかえるようになって`map`の出番はなくなりました。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(<map at 0x7ff308ca81f0>, [1, 1, 2, 3, 5, 8, 13, 21, 34, 55])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#　mapとリスト内包表記\n",
    "map(fib, range(10)), list(map(fib, range(10)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, "
     ]
    },
    {
     "data": {
      "text/plain": [
       "map"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for v in  map(fib, range(10)):\n",
    "    print(v,end=\", \")\n",
    "\n",
    "type(map(fib, range(10)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Pythonに組み込みのデータ型\n",
    "pythonのリストおよびタプルというデータ型を紹介しました。\n",
    "\n",
    "ここで、pythonに組み込みのデータ型を挙げて見ます。今回取り上げないデータ型は後ほど取り上げてい行きます。\n",
    "Pythonではさらに`class`文を使い、　新しいデータ型を定義することができます。　`class`文はブジェクト志向プログラム（OOP:Object Oriented Program)で使われます。\n",
    "(`class`についてはまた別の機会に)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### 数\n",
    "- 整数型\n",
    "  - ***整数***　（`int`) ：明示的な桁数の制限はない(実行時にメモリが確保できる限りの桁数）\n",
    "  - ***論理定数型***　(`bool`) : `True` と `False`\n",
    "- *浮動小数点数* ($\\ne$実数)　( `float`) : 倍精度浮動小数点数　（4倍精度浮動小数点などにはnumpyなどのライブラリを使う。）\n",
    "    - mathモジュールdで`math.nan`, `math.inf`が定義されている。\n",
    "    - numpyモジュールにも`numpy.nan`, `numpy.inf`がある。\n",
    "- 複素数 (`complex`)　\n",
    "\n",
    "数データには通常の四則演算(`+-*/`）の他にも、整数除算(`//`),剰余(`%`)などの演算も定義されている。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### コンテナ(データの集まり）\n",
    "\n",
    "- シークェンス型: **immutable**\n",
    "   - ***タプル型*** (`tuple`)\n",
    "   - *文字列型*　　(`str`) : Pythonでは単独の文字というデータ型はありません。文字は1文字でも文字列型のデータとなります。　（文字列型　については次回取り上げます。）\n",
    "   - *バイト型* (`bytes`)　:\n",
    "   \n",
    "- シークェンス型: **mutable**\n",
    "   - ***リスト型*** ( `list` )\n",
    "   - *バイト配列型*( bytearray )\n",
    "   \n",
    "- 集合\n",
    "    -　集合型 (`set`　） :　mutableなオブジェクトは要素になれない。  例: { 1,2,3 ,\"a\",\"b\",\"c\"}\n",
    "    -　不変な集合型　（ `frozenset` ): immutable & hashable\n",
    "    \n",
    "- *辞書型*　（ `dict` ) : \n",
    "    - `key`と`value`の組を要素にもつ。　例：　`d={'a':1,　'b':2,　}`\n",
    "    -`key`による検索が可能。　　例: `d['a']` → 1\n",
    "    - LISPの連想配列や、javascriptのobjectに似ている。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "### プログラムの実行に係るデータ型\n",
    "\n",
    "- ***ユーザ定義関数*** ( function ) : 関数定義文で生成される関数オブジェクト\n",
    "- *クラス*　(`class`) ：`class`文によって生成される。\n",
    "- モジュール( module )\n",
    "- その他　....\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "## グラフを描いてみます。\n",
    "\n",
    "数字が一列に並んだものがあると、グラフにして見なくなるのは、物理研究者の（？　）習い性です。\n",
    "\n",
    "`matplotlib`ライブラリの中の`pyplot`モジュールを使って、フィボナッチ数列のグラフを描いてみます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as pyplot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXAAAAD4CAYAAAD1jb0+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjQuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Z1A+gAAAACXBIWXMAAAsTAAALEwEAmpwYAAAdQ0lEQVR4nO3deXxV9Z3/8dcnCwkhGyEhCSQhIJvsIKCIu1XRarXWwa0VhZH+qm21aq3t/Fp/M9P51dZprTNdGQGlVREVR0cFi2sFFUHWsC8BQszGkoWQ/X7nj1wQkCWQe3PuvXk/Hw8fufdwb87bA3nz5Xu/5xxzziEiIuEnyusAIiJyZlTgIiJhSgUuIhKmVOAiImFKBS4iEqZiOnJn6enpLj8/vyN3KSIS9j777LM9zrmMY7d3aIHn5+ezfPnyjtyliEjYM7Odx9uuKRQRkTClAhcRCVMqcBGRMKUCFxEJUypwEZEwpQIXEQlTKnARkTClAhcRCaKSqjp+8eYG9hxoCPj3VoGLiATRnI938l8fbqeusSXg31sFLiISJAcbm3lu6S6uGppFblpCwL+/ClxEJEjmryimqq6JaRf0Dcr3V4GLiASBz+eYtaSQETkpnNOne1D2oQIXEQmCDzZXsL2ilmkX9MXMgrIPFbiISBDMWlJIZnIcVw/LDto+VOAiIgG2qbSGD7fs4Y4J+XSJCV7NqsBFRAJs9pJC4mOjuG18XlD3owIXEQmgvQcamL+ymBvH5NC9W5eg7ksFLiISQM8u3UVjs4+pE4OzdPBIKnARkQBpaG7hL5/s5JJBGfTvmRj0/anARUQC5PXVJVTUNHTI6BtU4CIiAeGcY+biQgb0TOTCAekdsk8VuIhIACwt3Mf6kmqmBvHEnWOpwEVEAmDm4kLSunXh66N7d9g+21TgZrbDzNaa2SozW+7flmZmi8xsi/9rcE72FxEJcTv31vL2hjJuPzeP+NjoDtvv6YzAL3XOjXLOjfU/fwR4xzk3AHjH/1xEpNOZvWQHMVHGt87r06H7bc8UyvXAM/7HzwA3tDuNiEiYqa5v4sXlRVw3ohc9k+M7dN9tLXAH/M3MPjOz6f5tmc65Ev/jUiAz4OlERELcvGVF1Da2MDVI1/w+mZg2vu4C51yxmfUEFpnZxiN/0TnnzMwd743+wp8OkJcX3OsCiIh0pOYWH7OX7GB83zSG9U7p8P23aQTunCv2fy0HXgHGA2Vmlg3g/1p+gvfOcM6Ndc6NzcjICExqEZEQsGh9GcWVdUG7486pnLLAzaybmSUdegxcCRQArwFT/C+bArwarJAiIqFo5uJC8tIS+MrZ3swgt2UKJRN4xb8wPQZ4zjm30MyWAfPMbBqwE5gcvJgiIqFldVEly3fu52fXDiE6qmNO3DnWKQvcObcdGHmc7XuBy4MRSkQk1M1aUkhSXAyTx+V6lkFnYoqInKbSqnreWFPC5HG5JMa1dS1I4KnARURO0zMf78DnHHeen+9pDhW4iMhpqGts4bmlu7hqaBa5aQmeZlGBi4ichpdX7KaqrsmTE3eOpQIXEWkjn88xe0khI3JSGNvH++v3qcBFRNrogy0VbKuoZerEjrvm98mowEVE2mjW4kIyk+O4Zni211EAFbiISJtsLqvhwy17uGNCPl1iQqM6QyOFiEiIm7W4kPjYKG4bHzoX5VOBi4icwt4DDcxfWcyNY3Lo3q2L13EOU4GLiJzCc0t30djsY+rEfK+jHEUFLiJyEg3NLcz5ZCcXD8ygf88kr+McRQUuInISb6wpoaKmwbNrfp+MClxE5AScc8xcXMiAnolcOCDd6zhfogIXETmBpYX7WPd5NVMvCI0Td46lAhcROYFZiwvpnhDL10f39jrKcanARUSOY+feWhZtKOP2c/sQHxvtdZzjUoGLiBzH0x/tICbKuGNCH6+jnJAKXETkGNX1TcxbVsR1I3rRMzne6zgnpAIXETnGvGVF1Da2hMQ1v09GBS4icoTmFh+zl+xgfN80hvVO8TrOSanARUSOsGh9GcWVdUydGNqjb1CBi4gcZebiQvLSErhiSKbXUU5JBS4i4re6qJLlO/dz5/n5REeF3ok7x1KBi4j4zVpSSGJcDP8wNsfrKG2iAhcRAUqr6nljTQk3j8slKT7W6zhtogIXEQHmfLwDn3PceX6+11HaTAUuIp1eXWMLz326iyuHZJGbluB1nDZrc4GbWbSZrTSz1/3P+5rZUjPbamYvmFno3GdIROQ0zF+5m8qDTUy7MPSXDh7pdEbg9wEbjnj+S+AJ51x/YD8wLZDBREQ6gs/nmLW4kOG9Uxjbp7vXcU5LmwrczHKArwJP+Z8bcBnwkv8lzwA3BCGfiEhQfbClgm0VtUwL0Wt+n0xbR+C/BR4GfP7nPYBK51yz//lu4LgXzDWz6Wa23MyWV1RUtCeriEjAzVpcSGZyHNcMz/Y6ymk7ZYGb2bVAuXPuszPZgXNuhnNurHNubEZGxpl8CxGRoNhcVsOHW/Zwx4R8usSE35qOmDa8ZiLwNTO7BogHkoEngVQzi/GPwnOA4uDFFBEJvNlLComPjeK28XleRzkjp/wrxzn3Y+dcjnMuH7gFeNc5dzvwHnCT/2VTgFeDllJEJMD21TYyf0UxN47JoXu38FxE155/M/wIeMDMttI6Jz4zMJFERILv2U920tDsY+rEfK+jnLG2TKEc5px7H3jf/3g7MD7wkUREgqux2cecT3Zy8cAM+vdM8jrOGQu/WXsRkXZ6fc3nVNQ0hPwdd05FBS4inYpzjpmLCxnQM5GLBqR7HaddVOAi0ql8WriPdZ9XMzUMT9w5lgpcRDqVmYsL6Z4Qy9dHH/fcw7CiAheRTmPX3oMs2lDG7ef2IT422us47aYCF5FOY/ZHhcREGd+a0MfrKAGhAheRTqG6vol5y4q4dkQvMpPjvY4TECpwEekU5i0roraxhakTw3vp4JFU4CIS8Vp8jqc/2sH4/DSG56R4HSdgVOAiEvEWrS9l9/66sD9x51gqcBGJeDMXF5Kb1pUrhmR6HSWgVOAiEtHW7K5k2Y793Hl+X6KjwvvEnWOpwEUkos1aXEhiXAyTx+Z4HSXgVOAiErFKq+p5fU0JN4/LJSk+1us4AacCF5GI9ZdPduBzjjvPz/c6SlCowEUkItU2NPPs0l1cOSSL3LQEr+MEhQpcRCLSv76+nqq6Jr59cT+vowSNClxEIs7CghLmLiviOxefxei87l7HCRoVuIhElNKqeh6Zv5YROSnc/5WBXscJKhW4iEQMn8/x4IuraGjy8dubR9ElJrIrLrL/70SkU5m1pJAlW/fy6HVD6JeR6HWcoFOBi0hEWPd5Fb9auImrhmZy87hcr+N0CBW4iIS9usYW7pu7iu7dYnnsxhFhf6/LtorxOoCISHv9YsEGtpYf4K/TzqV7ty5ex+kwGoGLSFh7Z0MZcz7eyT9e0JcLBqR7HadDqcBFJGxV1DTw8EtrGJyVxA8nDfI6TofTFIqIhCXnHD98aTUHGpp5fvp5xMWE/13mT5dG4CISluZ8vJP3N1XwT189m4GZSV7H8cQpC9zM4s3sUzNbbWbrzOyf/dv7mtlSM9tqZi+YWef55EBEPLW5rIZ/e3MDlw7K4Fvn9fE6jmfaMgJvAC5zzo0ERgGTzOw84JfAE865/sB+YFrQUoqI+NU3tfD951eSHB/Dr24a2WmWDB7PKQvctTrgfxrr/88BlwEv+bc/A9wQjIAiIkd6/K1NbCyt4fGbRpKRFOd1HE+1aQ7czKLNbBVQDiwCtgGVzrlm/0t2A71P8N7pZrbczJZXVFQEILKIdFZ/31zBzMWFTJnQh0sH9/Q6jufaVODOuRbn3CggBxgPDG7rDpxzM5xzY51zYzMyMs4spYh0evtqG3nwxdUM6JnIj6852+s4IeG0VqE45yqB94AJQKqZHVqGmAMUBzaaiEgr5xw/enkNVQebePKW0cTHdr4lg8fTllUoGWaW6n/cFbgC2EBrkd/kf9kU4NUgZRSRTu75T4tYtL6MhycNYkivZK/jhIy2nMiTDTxjZtG0Fv4859zrZrYemGtmPwdWAjODmFNEOqmt5Qf4l9fXcUH/dKZO7Ot1nJByygJ3zq0BRh9n+3Za58NFRIKisdnH/S+sJD42ml9PHklUVOddMng8OpVeRELWE29vpqC4mj998xwyk+O9jhNydCq9iISkj7ft5U8fbOPW8blMGpbldZyQpAIXkZBTdbCJB+atom+Pbvz02iFexwlZmkIRkZDinOMnr6yloqaB+fecT0IX1dSJaAQuIiHl5RXFvLG2hAeuHMiInFSv44Q0FbiIhIyde2t59NUCzu2bxrcvOsvrOCFPBS4iIaGpxcd9c1cRHWU8cfMoorVk8JQ0uSQiIeE/393KqqJKfnfbaHqldvU6TljQCFxEPLd8xz5+9+4WvjEmh2tH9PI6TthQgYuIp6rrm7j/hVX07t6V//c1LRk8HZpCERFPPfrqOkqq6pn37Qkkxcd6HSesaAQuIp55dVUxr6ws5nuX9eecPt29jhN2VOAi4omifQf5v68UMCYvle9e2t/rOGFJBS4iHa7F53hg3ioc8NubRxMTrSo6E5oDF5EO98f3t7Jsx35+M3kkeT0SvI4TtvTXnoh0qFVFlTzx9hauG9mLr48+7r3QpY1U4CLSYWobmrl/7kqykuP5+Q3DMNPZlu2hKRQR6TD/8j/r2bnvIHPvPo+Urloy2F4agYtIh1iwtoQXlhdxzyVncW6/Hl7HiQgqcBEJupKqOh6Zv5YROSnc/5WBXseJGCpwEQkqn8/x4LzVNDb7+O3No4jVksGA0ZEUkaB6avF2Ptq2l0evG0K/jESv40QUFbiIBE1BcRWPv7WJq4ZmcvO4XK/jRBwVuIgERV1jC/fNXUlaty48duMILRkMAi0jFJGg+Lc317Otopa/TjuX7t26eB0nImkELiIB9/b6Mv76yS7uvrAvFwxI9zpOxFKBi0hAfbZzHw++uJqzs5N56KpBXseJaKcscDPLNbP3zGy9ma0zs/v829PMbJGZbfF/1cV8RTq5N9aUcOt/LaV7Qix//uY5xMVEex0porVlBN4MPOicGwKcB9xrZkOAR4B3nHMDgHf8z0WkE3LO8acPtnHvcysY0TuF+fdM1FUGO8ApP8R0zpUAJf7HNWa2AegNXA9c4n/ZM8D7wI+CklJEQlZzi4+fvbaO55bu4rqRvXj8phHEx2rk3RFOaxWKmeUDo4GlQKa/3AFKgcwTvGc6MB0gLy/vjIOKSOg50NDMvc+u4IPNFdxzyVk8dOUgoqK0XLCjtLnAzSwReBm43zlXfeSaTuecMzN3vPc552YAMwDGjh173NeISPgpqapj6tPL2VxWwy9uHM6t4zVA62htKnAzi6W1vJ91zs33by4zs2znXImZZQPlwQopIqFl3edVTH16GbUNLcy6cxwXD8zwOlKn1JZVKAbMBDY4535zxC+9BkzxP54CvBr4eCISat7bVM7kP31MlBkv/p8JKm8PtWUEPhH4FrDWzFb5t/0EeAyYZ2bTgJ3A5KAkFJGQ8dzSXfz01QIGZSYx685xZKXEex2pU2vLKpTFwIk+lbg8sHFEJBT5fI5fvrWRP3+wnUsHZfCft40hMU5X4vCafgdE5KTqm1p4cN5q3lhbwu3n5vHPXxtKjK7pHRJU4CJyQvtqG7l7znI+27mfn1wzmLsv7KerCoYQFbiIHFfhnlrumv0pJVX1/OH2MVwzPNvrSHIMFbiIfMmyHfu4e85yosx4fvp5jMnTpY5CkQpcRI7y2urPeWjeanK6d2X2XePo06Ob15HkBFTgIgK0XpDqD+9v4/G3NjE+P40Zd5xDaoJuxBDKVOAiQlOLj5/+dwFzlxVx/ahe/OqmEboUbBhQgYt0cjX1Tdzz7Ao+3LKH713WnweuGKiVJmFCBS7SiX1eWcfUp5extfwAv/rGCCbrzvFhRQUu0kkVFLdekKqusYWn7xqve1eGIRW4SCf03sZy7n1uBaldY3npO+czKCvJ60hyBlTgIp3MXz7ewaOvrWNIr2RmTRlHz2RdkCpcqcBFOgmfz/HYwo3M+Pt2Lh/ck/+4dTTddEGqsKbfPZFOoL6phR+8sIoFBaVMmdCHn103lGjd+izsqcBFItyeAw3cPWc5q4oq+em1Q5g6MV/LBCOEClwkgm2rOMBds5dRXlPPH28/h0nDsryOJAGkAheJUEu372X6Xz4jNtqYO30Co3JTvY4kAaYCF4lAr64q5ocvriE3rStP3zWe3LQEryNJEKjARSKIc47fvbuVXy/azHn90vjzN8eSkhDrdSwJEhW4SIQorqzj/7+5gTfWlHDj6N489o0RdInRrc8imQpcJMxVHmzkD+9v4+mPdgDw0JUDuffS/lpp0gmowEXCVH1TC09/tIM/vLeVmoZmbhydwwNXDqR3alevo0kHUYGLhJkWn+PlFbt5YtFmSqrquXRQBg9PGszZ2cleR5MOpgIXCRPOOd7bVM4vF2xiU1kNI3NS+M3kUUw4q4fX0cQjKnCRMLBy135+sWAjnxbuI79HAr+/bQzXDM/SPHcnpwIXCWHbKw7w+FubWFBQSnpiF/71+qHcMj6P2GitLhEVuEhIKq+p58m3tzB3WRFxMVHcd/kA7r6oH4m6eqAcQX8aRELIgYZmZvx9O099uJ3GZh+3jc/j+5cPICMpzutoEoJOWeBmNgu4Fih3zg3zb0sDXgDygR3AZOfc/uDFFIlsjc0+nv90F//xzhb21jby1eHZPHTVIPqmd/M6moSwtozAnwZ+B8w5YtsjwDvOucfM7BH/8x8FPp5IZPP5HG+sLeHf/7aJnXsPcl6/NGZefbYuPCVtcsoCd8793czyj9l8PXCJ//EzwPuowEVOy0db9/DYwo2s2V3F4KwkZt81jksGZmhlibTZmc6BZzrnSvyPS4HME73QzKYD0wHy8vLOcHcikWP959X8cuFGPthcQa+UeH79DyO5YXRv3SFHTlu7P8R0zjkzcyf59RnADICxY8ee8HUikW73/oP85m+beWVVMcnxsfzkmsHcMSGf+Nhor6NJmDrTAi8zs2znXImZZQPlgQwlEkn21zby+/e2MufjnWAw/aJ+3HNxf13mVdrtTAv8NWAK8Jj/66sBSyQSIeqbWpi1pJA/vr+N2oZmvjEmhx9cMZBeutiUBEhblhE+T+sHlulmtht4lNbinmdm04CdwORghhQJJ80tPv/FprZQWl3P5YN78vCkwQzKSvI6mkSYtqxCufUEv3R5gLOIhDXnHG9vKOdXCzeypfwAo3JTefKWUZzbTxebkuDQmZgi7dTQ3MLiLXv40wfbWLZjP/3Su/HH28cwaZguNiXBpQIXOQN1jS18sLmcBQWlvLuhnJqGZtIT4/j5DcO4eVyuLjYlHUIFLtJGBxqaeXdjOQsLSnhvYwV1TS10T4jlmuHZTBqexcSz0nUPSulQKnCRk6g62MSiDWUsLCjh71v20NjsIyMpjpvOyeHqYVmM75tGjEbb4hEVuMgx9hxo4G/rylhQUMLH2/bS7HP0Sonnm+f24erhWZyT150onTUpIUAFLgKUVtXz1rpSFhSU8GnhPnwO+vRI4B8v7MfVw7IYkZOiDyQl5KjApdMq2neQhQWtpb1iVyUAA3om8t1L+zNpWDZnZyeptCWkqcClU9lecYAFBaUsLChlbXEVAEN7JfPQlQOZNCyb/j0TPU4o0nYqcIlozjk2ldWwYG1raW8qqwFgVG4qP756MFcPyyavR4LHKUXOjApcIo5zjoLiahYUlLCgoJTCPbWYwbj8NB69bghXDc3S9UgkIqjAJSL4fI6VRftbR9rrStm9v47oKGNCvx5Mu6AvVw7NpGdSvNcxRQJKBS5hq6y6npW7Kvlo2x7eWldKWXUDXaKjuGBAOt+/fABXnJ1J925dvI4pEjQqcAkLdY0tFHxexcpd+1lVVMnKXZWUVNUDEB8bxcUDM7hmeDaXDu5Jcryusy2dgwpcQo7P5yjcW8vKXZWsKtrPyl2VbCytocXXekOn3LSujMtPY1RuKqPyUhmSnay72kinpAIXz+2vbWwdVRdVsqqoklW79lNd3wxAYlwMI3NT+M7FZx0u7PTEOI8Ti4QGFbh0qMZmHxtKqluLuqiSlbv2s2PvQQCiDAZmJvHVEb0YnZvK6LxUzspI1GnrIiegApegcc5RXFnnnwppLeuCz6tpbPYB0DMpjtF5qdw8Lo/ReakM751Ctzj9kRRpK/20SMAcaGhmjX8q5FBp7znQAEBcTBQjclKYMqEPo/O6Myo3leyUeJ2qLtIOKnA5Iwcaminad5DVh6dCKtlcXoNr/ZyRfhnduGhgun8qpDuDspJ0kwORAFOBy1Gcc+w/2ERJVR1l1fWUVNVTWtX69cjnBxqaD78nNSGWUbmpXD08q3V0nZNKSoKW8okEmwq8E2nxOSpqGiitrqe0qq61jKu/KOhS//NDc9SHRBlkJseTmRzPgJ6JXDggnazkeLJTuzK8dwr5PRI0FSLiARV4hGhobqG8uoGSqvovjZ4PlXR5TcPhtdSHdImJIis5nqyUeEbnpR5+nJ0ST1ZKV7KS40lP7KK7zoiEIBV4GKhtaD6mjOu+NKWxt7bxS+9LjIs5XMYT+6f7Szn+iJLuSveEWI2eRcKUCtxDzjkqDzYdM41RR+kxo+ea+uYvvTetW5fDRTwyN5Xs5Hgy/WWdndI63ZGkU8pFIpoKPEhafI69Bw5NaRw5Wj569NxwnPnmnkmtZXxWRiIT+6d/MaXhL+zM5HidOi4iKvAz0djso6z6i7nlo1dp1FFaVU/Z8eabo6PITIkjO7krI3JSuXJoaylnp3wxes5IjNN8s4i0SVgU+D+9spZPC/d5HQMHVB5sOnxyypESukT7py+6MuGs4803x5PWrYvmm0UkYNpV4GY2CXgSiAaecs49FpBUx+iV2pUBmaFxr8KUrl2Oms44NHpOiotROYtIhzrjAjezaOD3wBXAbmCZmb3mnFsfqHCH3Htp/0B/SxGRsNeeydbxwFbn3HbnXCMwF7g+MLFERORU2lPgvYGiI57v9m8TEZEOEPTlDmY23cyWm9nyioqKYO9ORKTTaE+BFwO5RzzP8W87inNuhnNurHNubEZGRjt2JyIiR2pPgS8DBphZXzPrAtwCvBaYWCIicipnvArFOddsZt8F3qJ1GeEs59y6gCUTEZGTatc6cOfcm8CbAcoiIiKnQedsi4iEKXPOnfpVgdqZWQWw8wzfng7sCWCccKfj8QUdi6PpeBwtEo5HH+fcl1aBdGiBt4eZLXfOjfU6R6jQ8fiCjsXRdDyOFsnHQ1MoIiJhSgUuIhKmwqnAZ3gdIMToeHxBx+JoOh5Hi9jjETZz4CIicrRwGoGLiMgRVOAiImEqLArczCaZ2SYz22pmj3idxytmlmtm75nZejNbZ2b3eZ0pFJhZtJmtNLPXvc7iNTNLNbOXzGyjmW0wswleZ/KKmf3A/3NSYGbPm1m815kCLeQL/Ig7/1wNDAFuNbMh3qbyTDPwoHNuCHAecG8nPhZHug/Y4HWIEPEksNA5NxgYSSc9LmbWG/g+MNY5N4zW6zXd4m2qwAv5Akd3/jnMOVfinFvhf1xD6w9np76JhpnlAF8FnvI6i9fMLAW4CJgJ4JxrdM5VehrKWzFAVzOLARKAzz3OE3DhUOC6889xmFk+MBpY6nEUr/0WeBjweZwjFPQFKoDZ/imlp8ysm9ehvOCcKwb+HdgFlABVzrm/eZsq8MKhwOUYZpYIvAzc75yr9jqPV8zsWqDcOfeZ11lCRAwwBvijc240UAt0ys+MzKw7rf9S7wv0ArqZ2Te9TRV44VDgbbrzT2dhZrG0lvezzrn5Xufx2ETga2a2g9aptcvM7K/eRvLUbmC3c+7Qv8peorXQO6OvAIXOuQrnXBMwHzjf40wBFw4Frjv/+JmZ0Tq/ucE59xuv83jNOfdj51yOcy6f1j8X7zrnIm6U1VbOuVKgyMwG+TddDqz3MJKXdgHnmVmC/+fmciLwA9123dChI+jOP0eZCHwLWGtmq/zbfuK/sYYIwPeAZ/2Dne3AXR7n8YRzbqmZvQSsoHX11koi8JR6nUovIhKmwmEKRUREjkMFLiISplTgIiJhSgUuIhKmVOAiImFKBS4iEqZU4CIiYep/ARBzRU8XGG1+AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "x=range(10)\n",
    "pyplot.plot(x,[fib(n) for n in x])\n",
    "        \n",
    "pyplot.savefig(\"./_images/fibonacci.png\")\n",
    "pyplot.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "###  fib()関数の実行速度についてのコメント\n",
    "「Python入門講座」でのご質問の中で、`fib()`関数の実行測度についての回答にちょっと不正確なところがあったので、改めてご説明します。\n",
    "\n",
    "まずここで定義したフィボナッチ関数の実行時間は、`timeit`モジュールを使うと`fib(40)`の計算に35秒かかっていることがわかります。(python3.9.7/macOS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "35.871306792"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import timeit\n",
    "timeit.timeit(\"(fib(40))\", globals=globals(), number=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "講座のお話の中では、Pythonホームページに掲載されているフィボナッチ数列のプログラムが早いというお話をしました。よくこのプログラムをみてみるとここで定義されている関数は、\n",
    "引数に与えられた整数`n`以下のフィボナッチ数列を印刷するプログラムでした。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, "
     ]
    }
   ],
   "source": [
    "# version in Python.org\n",
    "def fib_python_homepage(n):\n",
    "    a, b = 0, 1\n",
    "    while b < n :\n",
    "        a, b = b, a+b\n",
    "        print(a, end=\", \")\n",
    "\n",
    "fib_python_homepage(1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "このプログラムを変更して、同じアルゴリズムで`n`番目のフィボナッチ数を求めるプログラムを`fib_python（）`としました。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def fib_python(n):\n",
    "    a, b = 0, 1\n",
    "    while n > 0 :\n",
    "        a, b = b, a+b\n",
    "        n-=1\n",
    "    return b\n",
    "\n",
    "[fib_python(n) for n in range(16)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "`fib_python()`の実行時間は、`fib_python(40)`で 5マイクロ秒、`fib_python(1000)`で　100マイクロ秒となりました。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4.625000002533852e-06\n",
      "0.00010066600000158132\n"
     ]
    }
   ],
   "source": [
    "print(timeit.timeit(\"(fib_python(40))\", globals=globals(), number=1))\n",
    "print(timeit.timeit(\"(fib_python(1000))\", globals=globals(), number=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "ご質問にあった「途中結果を保存しておいて、高速化する」バージョンとして、次の`fibb()`を\n",
    "定義してみました。\n",
    "`fibb(1000)`の実行結果は、1回目が　700マイクロ秒前後、2回目が　1マイクロ秒前後となっています。1回目は`fib_python`に比べると遅いわけですが、同じ計算を繰り返し行う時は、効果が高くなっています。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0006977500000004966\n",
      "7.089999982667905e-07\n",
      "0.00043099999999896\n",
      "4.999999987376214e-07\n"
     ]
    }
   ],
   "source": [
    "def fibb(n, buf={}):\n",
    "    if n not in buf:\n",
    "        if n in (0,1):\n",
    "            buf[n]=1\n",
    "        else:\n",
    "            buf[n]=fibb(n-2,buf)+fibb(n-1,buf)\n",
    "    return buf[n]\n",
    "\n",
    "num=1\n",
    "print(timeit.timeit(\"(fibb(1000))\", globals=globals(), number=num)/num)\n",
    "print(timeit.timeit(\"(fibb(1000))\", globals=globals(), number=num)/num,)\n",
    "buf={}\n",
    "print(timeit.timeit(\"(fibb(1000,buf))\", globals=globals(), number=num)/num)\n",
    "print(timeit.timeit(\"(fibb(1000,buf))\", globals=globals(), number=num)/num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.5916000002391683e-07\n"
     ]
    }
   ],
   "source": [
    "num=100\n",
    "print(timeit.timeit(\"(fibb(1000,buf))\", globals=globals(), number=num)/num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "# エラーの処理：例外(Exception)の送出と受信"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "フィボナッチ数列　$a_n$　は整数　$n$　について定義されています。\n",
    "では、ここで定義した関数　`fib(n)`の`n`に整数でない数値をあたえると何が起きるでしょう？ 一例として`fib(1.1)`を計算してみます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error: maximum recursion depth exceeded in comparison\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    fib(1.1)\n",
    "except Exception as e:\n",
    "        print(\"Error:\",e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "整数でない入力値`n`にたいしては停止条件`n==0` あるいは`n==1`が決して満たされないため、システムの条件までプログラムはある意味暴走してしまいます。\n",
    "このように、関数に与えられた引数が適切なものであるかを判断し、条件が満たされない場合には、そのことを関数の呼び出しもとにつたえるための仕組みとして、**例外(Exception)**の仕組みが`python`には用意されています。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "入力値のチェックを組み込んだ`fib`関数をご覧ください。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def fib(n:int)->int:\n",
    "    \"\"\"\n",
    "    return a value of fibonacci series for integer n.\n",
    "\n",
    "    >>> fib(10)\n",
    "    89\n",
    "    >>> fib(-1)\n",
    "    Traceback (most recent call last):\n",
    "    ...\n",
    "    ValueError: n must be >= 0\n",
    "    >>> fib(1.0)\n",
    "    Traceback (most recent call last):\n",
    "    ...\n",
    "    TypeError: n must be an exact integer\n",
    "    \"\"\"\n",
    "    if type(n) is not int:\n",
    "        raise TypeError(\"n must be an exact integer\")      \n",
    "    if not n >= 0:\n",
    "        raise ValueError(\"n must be >= 0\")\n",
    "    if n == 0 or n == 1:\n",
    "        value = 1\n",
    "    else:\n",
    "        value= fib(n-1) + fib(n-2)\n",
    "    return value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "この関数で実際に`n=1.1`の`fib`関数を計算して見ます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error: n must be an exact integer\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    fib(1.1)\n",
    "except Exception as e:\n",
    "        print(\"Error:\",e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "このように、整数以外の数値が引数として与えられたことがエラーの原因であることがすぐにわかります。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "この定義ではdocstringも入れてあるので、`help`メッセージも表示されます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function fib in module __main__:\n",
      "\n",
      "fib(n: int) -> int\n",
      "    return a value of fibonacci series for integer n.\n",
      "    \n",
      "    >>> fib(10)\n",
      "    89\n",
      "    >>> fib(-1)\n",
      "    Traceback (most recent call last):\n",
      "    ...\n",
      "    ValueError: n must be >= 0\n",
      "    >>> fib(1.0)\n",
      "    Traceback (most recent call last):\n",
      "    ...\n",
      "    TypeError: n must be an exact integer\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(fib)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "さらに、`docstring`にはこの関数で予期される動作の例も書き込まれているので、\n",
    "`doctest` をつかって、この定義が設計を満たしているかどうかを確認することが\n",
    "できます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Trying:\n",
      "    fib(10)\n",
      "Expecting:\n",
      "    89\n",
      "ok\n",
      "Trying:\n",
      "    fib(-1)\n",
      "Expecting:\n",
      "    Traceback (most recent call last):\n",
      "    ...\n",
      "    ValueError: n must be >= 0\n",
      "ok\n",
      "Trying:\n",
      "    fib(1.0)\n",
      "Expecting:\n",
      "    Traceback (most recent call last):\n",
      "    ...\n",
      "    TypeError: n must be an exact integer\n",
      "ok\n",
      "4 items had no tests:\n",
      "    __main__\n",
      "    __main__.fib_python\n",
      "    __main__.fib_python_homepage\n",
      "    __main__.fibb\n",
      "1 items passed all tests:\n",
      "   3 tests in __main__.fib\n",
      "3 tests in 5 items.\n",
      "3 passed and 0 failed.\n",
      "Test passed.\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "TestResults(failed=0, attempted=3)"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import doctest\n",
    "doctest.testmod(verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TestResults(failed=0, attempted=3)"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "doctest.testmod(verbose=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "34\n"
     ]
    }
   ],
   "source": [
    "n=8\n",
    "try:\n",
    "    r=fib(n)\n",
    "except TypeError as err:\n",
    "    print(\"TypeError:\", err)\n",
    "except ValueError as err:\n",
    "    print(\"ValueError:\", err)\n",
    "else:\n",
    "    print(r)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "##　おまけ：プログラムの実行速度を測ってみよう\n",
    "`timeit` モジュールを使って、関数の実行速度を調べて見ます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.1374999995771304e-07"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import timeit\n",
    "timeit.timeit(\"fib(1)\",setup=\"from __main__ import fib\", number=100)/100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.144791000003977e-05"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(10)\",setup=\"from __main__ import fib\", number=100)/100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.003866404200000062"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(20)\",setup=\"from __main__ import fib\", number=10)/10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.45605662920000045"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(30)\",setup=\"from __main__ import fib\", number=10)/10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.9436441659999986"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(33)\",setup=\"from __main__ import fib\", number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8.220809000000003"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(36)\",setup=\"from __main__ import fib\", number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "56.210244083999996"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(40)\", setup=\"from __main__ import fib\", number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7ff2f9a74a30>]"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "pyplot.semilogy()\n",
    "pyplot.plot(\n",
    "    [1,5,10,15,20,22,25,30,32],\n",
    "    [timeit.timeit(\"fib(1)\", setup=\"from __main__ import fib\", number=100)/100,\n",
    "     timeit.timeit(\"fib(5)\", setup=\"from __main__ import fib\", number=100)/100,\n",
    "     timeit.timeit(\"fib(10)\", setup=\"from __main__ import fib\", number=100)/100,\n",
    "     timeit.timeit(\"fib(15)\", setup=\"from __main__ import fib\", number=10)/10,\n",
    "     timeit.timeit(\"fib(20)\", setup=\"from __main__ import fib\", number=10)/10,\n",
    "     timeit.timeit(\"fib(22)\", setup=\"from __main__ import fib\", number=10)/10,\n",
    "     timeit.timeit(\"fib(25)\", setup=\"from __main__ import fib\", number=1)/1,\n",
    "     timeit.timeit(\"fib(30)\", setup=\"from __main__ import fib\", number=1)/1,\n",
    "     timeit.timeit(\"fib(32)\", setup=\"from __main__ import fib\", number=1)/1,\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7ff2e873a130>]"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "tl=[1,5,10,15,20,22,25]\n",
    "pyplot.semilogy()\n",
    "\n",
    "pyplot.plot(\n",
    "    tl,\n",
    "    [timeit.timeit(\"fib({})\".format(t), setup=\"from __main__ import fib\",\n",
    "                   number=5)/5\n",
    "     for t in tl])\n",
    "\n",
    "pyplot.plot(\n",
    "    tl,\n",
    "    [timeit.timeit(f\"fib({t})\", setup=\"from __main__ import fib\",\n",
    "                   number=5)/5\n",
    "     for t in tl])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "# おまけその2：高速化に挑戦して見ましょう。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import logging\n",
    "logging.getLogger('root').setLevel(logging.WARN)\n",
    "#logging.getLogger('root').setLevel(logging.DEBUG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def fib(n:int)->int:\n",
    "    logging.debug(\"-> fib({})\".format(n))\n",
    "    if n == 0 or n == 1:\n",
    "        value= 1\n",
    "    else:\n",
    "        value= fib(n-1) + fib(n-2)\n",
    "    logging.debug(f\"fib({n}) -> {value}\")\n",
    "    return value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fib(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def fibb(n, buf={}):\n",
    "    logging.debug(\"-> fibb({},{})\".format(n,buf))\n",
    "    if n not in buf:\n",
    "        if n == 0 or n == 1:\n",
    "            buf[n]=1\n",
    "        else:\n",
    "            buf[n]=fibb(n-2,buf)+fibb(n-1,buf)\n",
    "    logging.debug(f\"fibb({n},{buf})->{buf[n]}\")\n",
    "    return buf[n]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fibb(4,{})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "89"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fibb(10,{})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "89"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fibb(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "89"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d={}\n",
    "fibb(10,d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "89"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fibb(10,d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def fibb(n, /, buf={}):\n",
    "    if n not in buf:\n",
    "        if n == 0 or n == 1:\n",
    "            buf[n]=1\n",
    "        else:\n",
    "            buf[n]=fibb(n-2,buf)+fibb(n-1,buf)\n",
    "    return buf[n]\n",
    "[fibb(n) for n in range(16)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7ff2e84c4790>]"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "pyplot.plot([fibb(n) for n in range(100)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "import timeit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5.550000000198452e-05"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\n",
    "\"fib(100)\",\n",
    "setup=\"\"\"\n",
    "def fib(n, buf={}):\n",
    "    if n not in buf:\n",
    "        if n == 0 or n == 1:\n",
    "            buf[n]=1\n",
    "        else:\n",
    "            buf[n]=fib(n-2)+fib(n-1)\n",
    "    return buf[n]\n",
    "\"\"\",number=100)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.3065893330000051"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\n",
    "\"\"\"\n",
    "fib(30)\n",
    "\"\"\",\n",
    "    setup=\n",
    "\"\"\"\n",
    "def fib(n,buf=dict()):\n",
    "    if n == 0 or n == 1:\n",
    "        return 1\n",
    "    else:\n",
    "        return fib(n-1)+fib(n-2)\n",
    "    return buf[n] \n",
    "\"\"\",\n",
    "    number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "m=0\n",
    "d=m\n",
    "def fib(n):\n",
    "    m=n\n",
    "    d=2*m\n",
    "    def fib1(n,buf={}):\n",
    "        nonlocal m\n",
    "        global d\n",
    "        d -=1\n",
    "        logging.debug(f\"{m} {d} {n} {buf}\")\n",
    "        if n not in buf:\n",
    "            if n in (0,1):\n",
    "                buf[n]=1\n",
    "            else:\n",
    "                buf[n]=fib1(n-2)+fib1(n-1)\n",
    "        logging.debug(f\"{m} {d} {n} {buf} -> {buf[n]}\")\n",
    "        return buf[n]\n",
    "    return fib1(n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "89"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fib(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "144"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fib(11)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.6705705832999996"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(1000)\",setup=\"from __main__ import fib\",number=10)/10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.6665530840000002"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"fib(1000)\",globals=globals(),number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def fib_b(n):\n",
    "    def fib1(n,buf={}):\n",
    "        #print(n,buf)\n",
    "        if n not in buf:\n",
    "            if n in (0,1):\n",
    "                buf[n]=1\n",
    "            else:\n",
    "                buf[n]=fib1(n-2)+fib1(n-1)\n",
    "        #print(n,buf,\"->\",buf[n])\n",
    "        return buf[n]\n",
    "    return fib1(n)\n",
    "\n",
    "def fib_c(n):\n",
    "    buf={}\n",
    "    def fib1(n):\n",
    "        nonlocal buf # python version >= 3\n",
    "        #print(n,buf)\n",
    "        if n not in buf:\n",
    "            if n in (0,1):\n",
    "                buf[n]=1\n",
    "            else:\n",
    "                buf[n]=fib1(n-2)+fib1(n-1)\n",
    "        #print(n,buf,\"->\",buf[n])\n",
    "        return buf[n]\n",
    "    return fib1(n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.00044004100000449853"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "tl=[1,5,10,15,20,22,25]\n",
    "pyplot.plot(\n",
    "    tl,\n",
    "    [timeit.timeit(f\"fibb({t},dict())\", setup=\"from __main__ import fibb\",\n",
    "                   number=5)/5\n",
    "     for t in tl])\n",
    "\n",
    "pyplot.plot(\n",
    "    tl,\n",
    "    [timeit.timeit(\"fib({})\".format(t), setup=\"from __main__ import fib\",\n",
    "                   number=5)/5\n",
    "     for t in tl])\n",
    "\n",
    "import timeit\n",
    "timeit.timeit(\"(fibb(1000))\", globals=globals(), number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, "
     ]
    },
    {
     "data": {
      "text/plain": [
       "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# version in Python.org\n",
    "def fib_upto_n_python(n):\n",
    "    a, b = 0, 1\n",
    "    while b < n :\n",
    "        a, b = b, a+b\n",
    "        print(a, end=\", \")\n",
    "\n",
    "fib_upto_n_python(1000)\n",
    "    \n",
    "def fib_python(n):\n",
    "    a, b = 0, 1\n",
    "    while n > 0 :\n",
    "        a, b = b, a+b\n",
    "        n-=1\n",
    "    return b\n",
    "\n",
    "[fib_python(n) for n in range(16)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, "
     ]
    },
    {
     "data": {
      "text/plain": [
       "0.000437207999993916"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import timeit\n",
    "timeit.timeit(\"(fib_upto_n_python(1000))\", globals=globals(), number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.00010179099999163554"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "timeit.timeit(\"(fib_python(1000))\", globals=globals(), number=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.0004622919999945907, 0.00043295800000464624)"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(timeit.timeit(\"(fib_b(1000))\", globals=globals(), number=1),\n",
    " timeit.timeit(\"(fib_b(1000))\", globals=globals(), number=1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.00043241600000953895, 5.410000056826902e-07, 0.0004169589999918344)"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def fibb(n, /, buf={}):\n",
    "    if n not in buf:\n",
    "        if n in (0,1):\n",
    "            buf[n]=1\n",
    "        else:\n",
    "            buf[n]=fibb(n-2,buf)+fibb(n-1,buf)\n",
    "    return buf[n]\n",
    "(timeit.timeit(\"(fibb(1000))\", globals=globals(), number=1),\n",
    "timeit.timeit(\"(fibb(1000))\", globals=globals(), number=1),\n",
    " timeit.timeit(\"(fibb(1000,{}))\", globals=globals(), number=1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.0004345420000078093, 0.00039570799999921746)"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(timeit.timeit(\"(fib_c(1000))\", globals=globals(), number=1),\n",
    " timeit.timeit(\"(fib_c(1000))\", globals=globals(), number=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "# Collatz 予想\n",
    "数列の例としては、Collatz予想の数列も候補の一つです。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def collatz(n:int)->int:\n",
    "    \"\"\"\n",
    "    Collatz 予想：整数$n$から出発し、\n",
    "    nが偶数であれば2で割った値、さもなければ3*n+1を次の値とすることを\n",
    "    繰り返したとき、この数列は有限回で $1$に到達する。\n",
    "    \"\"\"\n",
    "    if n % 2 == 0:\n",
    "        return n//2\n",
    "    else:\n",
    "        return 3*n+1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, "
     ]
    }
   ],
   "source": [
    "n=14\n",
    "while((n:=collatz(n))!=1):\n",
    "    print(n, end=\", \")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "もう帰るの？ さようなら\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "またきてくださいね\n"
     ]
    }
   ],
   "source": [
    "# while文の例\n",
    "# 「初めてのプログラミング」　7.3 ループの例題から\n",
    "while (instr:=input(\"もう帰るの？\"))!=\"さようなら\":\n",
    "    print(instr)\n",
    "\n",
    "print(\"またきてくださいね\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "def func(p1, p2, po1=0, po2=1,/,k1=2,k2=4,*args,ko1,ko2,**env):\n",
    "    return f\"({p1=},{p2=},{po1=},{po2=},{k1=},{k2=},{ko1=}\"\\\n",
    " f\",{ko2=},{args=},{env=})\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"(p1=1,p2=2,po1=3,po2=4,k1=5,k2=6,ko1=0,ko2=1,args=(7,),env={'po2': 2, 'p1': 0})\""
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func(1,2,3,4,5,6,7,po2=2,p1=0,ko1=0,ko2=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "sys.version_info(major=3, minor=9, micro=7, releaselevel='final', serial=0)"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import sys\n",
    "sys.version_info"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9",
   "language": "python",
   "name": "python3.9"
  },
  "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
}
