{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Функции\n",
    "## 1. Основные сведения и примеры"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Функция** — это обособленный участок кода, который можно вызывать, обратившись к нему по имени, которым он был назван. При вызове происходит выполнение команд *тела* функции. \n",
    "\n",
    "Функции можно сравнить с небольшими программками, которые сами по себе, т. е. автономно, не исполняются, а встраиваются в обычную программу. Нередко их так и называют – *подпрограммы*. \n",
    "\n",
    "С точки зрения Python функция — это объект, принимающий аргументы и возвращающий значение. В отличие от основной программы функции обычно получают данные не с устройства ввода (клавиатуры, файла и др.), а из вызывающей программы. Сюда же они возвращают результат своей работы.\n",
    "\n",
    "Вы уже знакомы с *встроенными* функциями, например, `input`, `abs`, `min`, `print` и т.д. Сейчас речь пойдет о тех функциях, которые может создавать программист.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# синтаксис описания функции\n",
    "def add2(x, y, z):\n",
    "    result = x**2 + y**2 + z**2\n",
    "    return result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(add2(1,2,5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В предыдущем примере `add2`— имя функции, `x, y, z` — аргументы функции. В данном случае функция вычисляет (или, как говорят, ***возвращает***) сумму квадратов своих аргументов.\n",
    "\n",
    "Используем функцию `add2` для решения задачи:\n",
    "\n",
    "|  Найти значение выражения |\n",
    "|---|\n",
    "$$y = -7\\frac{\\sqrt[3]{a^2+b^2+c^2}}{a^2 + (b-a)^2+\\frac{c^2}{4}}$$\n",
    "при следующих значениях переменных:\n",
    "$a=3, b=4, c=10$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a, b, c = 3, 4, 10\n",
    "y = -7*add2(a, b, c)**(1/3) / add2(a, b-a, c/2)\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "y = -7*(a**2 + b**2 + c**2)**(1/3) / (a**2 + (b-a)**2 + (c/2)**2) \n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Аргументы и параметры, или Немножко филологии**\n",
    "\n",
    "*Математика*:\\\n",
    "$y = f(x)$   \n",
    "$x$ &ndash; аргумент, а $y$ &ndash; значение функции $f$.\n",
    "\n",
    "*Программирование*:\n",
    "```python\n",
    "def f(t):\n",
    "    return t**5\n",
    "y = f(x)\n",
    "```\n",
    "`t` &ndash; параметр (формальный параметр) функции `f`  \n",
    "`x` &ndash; аргумент (фактический параметр) функции `f`  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Функция может быть любой сложности и возвращать любые объекты (числа, логические значения, строки, списки, кортежи, и даже функции). Функция может и не заканчиваться инструкцией `return`, при этом функция вернет значение `None`.\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Пример с функцией `add2` выше не очень показателен — использование функции не привело к заметному *упрощению* или *сокращению* кода.\\\n",
    "Рассмотрим другой пример. \n",
    "\n",
    "**Задача**. Даны длины четырех отрезков: $a$, $b$, $c$, $d$. Сколько есть возможностей выбрать три из них так, чтобы можно было сложить треугольник?\n",
    "\n",
    "\n",
    "Начинаем перебирать варианты: \\\n",
    "$a$, $b$, $c$ \\\n",
    "$a$, $b$, $d$ \\\n",
    "$a$, $c$, $d$ \\\n",
    "$b$, $c$, $d$\n",
    "\n",
    "Смотрим на код:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a, b, c, d  = 2, 3, 4, 6\n",
    "k = 0\n",
    "if max(a, b, c) < a+b+c - max(a, b, c):\n",
    "    k += 1\n",
    "if max(a, b, d) < a+b+d - max(a, b, d):\n",
    "    k += 1\n",
    "if max(a, c, d) < a+c+d - max(a, c, d):\n",
    "    k += 1\n",
    "if max(b, c, d) < b+c+d - max(a, b, c):\n",
    "    k += 1    \n",
    "print(k)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Читать такой код сложно. Явно видны повторы. Можно легко запутаться при разработке и при исправлении.\n",
    "\n",
    "Проще один раз написать правильную функцию и пользоваться ей:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_3(a, b, c):\n",
    "    '''\n",
    "    функция возвращает True,\n",
    "    если из отрезков a, b, c\n",
    "    можно сложить треугольник\n",
    "    '''\n",
    "    return  max(a, b, c) < a+b+c - max(a, b, c)\n",
    "    \n",
    "k = is_3(a, b, c) + is_3(a, b, d) + is_3(a, c, d) + is_3(b, c, d)\n",
    "print(k)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Польза функций не только в возможности многократного вызова одного и того же кода из разных мест программы. Не менее важно, что благодаря им программа приобретает **структуру**. Функции как бы разделяют ее на обособленные части, каждая из которых выполняет свою конкретную задачу. В результате код становится более *прозрачным*, его гораздо проще понимать, корректировать, находить и исправлять ошибки. Каждую функцию можно отлаживать отдельно, а потом из готовых работающих кирпичиков собирать программу."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Что может быть параметром функции?  \n",
    "**Всё!!!**  \n",
    "Параметром может быть любой объект, в том числе - функция.\n",
    "\n",
    "Еще один пример с вычислением математического выражения.\n",
    "\n",
    "|  Найти значение выражения |\n",
    "|---|\n",
    "$$\\frac{\\ln\\alpha + \\ln \\gamma + \\ln\\gamma^2}\n",
    "       {\\cos\\beta+ \\cos(\\beta+\\pi)+\\cos(\\beta-\\pi)}$$\n",
    "при следующих значениях переменных:\n",
    "$\\alpha=1, \\beta=\\pi, \\gamma = e$.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from math import cos, log, pi, e\n",
    "alpha = 1\n",
    "beta = pi\n",
    "gamma = e\n",
    "\n",
    "# аргумент функции - некоторая другая функция\n",
    "def add_f(f, a, b, c):\n",
    "    return f(a) + f(b) +f(c)\n",
    "\n",
    "z1 = add_f(log, alpha, gamma, gamma*gamma)\n",
    "z2 = add_f(cos, beta, beta+pi, beta-pi)\n",
    "print(z1/z2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Решение задач\n",
    "### Func1."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Описать функцию `sign(x)` целого типа, <br>возвращающую для вещественного числа `x` следующие значения: \n",
    "$$\\left\\{\n",
    "\\begin{array}{ll}\n",
    "−1, &\\mbox{ если }x < 0; \\\\\n",
    "0, &\\mbox{ если }x = 0; \\\\\n",
    "1, &\\mbox{ если }x > 0 \n",
    "\\end{array}\n",
    "\\right.$$\n",
    "С помощью этой функции найти значение выражения `sign(a) + sign(b)` для данных вещественных чисел `a` и `b`. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sign(x):\n",
    "    if x>0:\n",
    "        return 1\n",
    "    elif x<0:\n",
    "        return -1\n",
    "    return 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# блок тестирования функции\n",
    "# тест 1\n",
    "a = 1\n",
    "b = -2\n",
    "print(f'sign({a})+sign({b}) = {sign(a)+sign(b)}')\n",
    "\n",
    "# тест 2\n",
    "a = 1\n",
    "b = 25\n",
    "print(f'sign({a})+sign({b}) = {sign(a)+sign(b)}')\n",
    "\n",
    "# тест 3\n",
    "a = -25\n",
    "b = -25\n",
    "print(f'sign({a})+sign({b}) = {sign(a)+sign(b)}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# блок тестирования функции - 2\n",
    "assert sign(1)==1,   'Test 1'\n",
    "assert sign(10)==1,  'Test 2'\n",
    "assert sign(0)==0,   'Test 3'\n",
    "assert sign(-2)==-1, 'Test 4'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Func9."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Описать функцию `even(k)` логического типа, возвращающую `True`, если целый параметр `k` является четным, и `False` в противном случае. С ее помощью найти количество четных чисел в наборе из 10 целых чисел. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# напишите текст функции ниже\n",
    "def even(k):\n",
    "    return k%2==0\n",
    "   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# блок тестирования функции\n",
    "integers10 = 1, 121, 14, 24, 23, 565, 1, 11, 0, -5\n",
    "k = 0\n",
    "for n in integers10:\n",
    "    if even(n):\n",
    "        k += 1\n",
    "print(k)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Func15. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Описать функцию `digit_n(k, n)` целого типа, возвращающую `n`-ю цифру целого положительного числа `k` (цифры в числе нумеруются справа налево, правая цифра имеет номер 1). Если количество цифр в числе `k` меньше `n`, то функция возвращает −1. Для каждого из пяти данных целых положительных чисел `k1`, `k2`, …, `k5` вызвать функцию `digit_n` с параметром `n`, изменяющимся от 1 до 5. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# напишите текст функции ниже\n",
    "def digit_n(k, n):\n",
    "    k //= 10**(n-1)\n",
    "    if k==0:\n",
    "        return -1\n",
    "    return k%10   \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# блок тестирования функции\n",
    "k1 = 12345\n",
    "k2 = 54321\n",
    "k3 = 123\n",
    "k4 = 23405600\n",
    "k5 = 314159265\n",
    "for k in k1,k2,k3,k4,k5:\n",
    "    print(f'{k=}:')\n",
    "    for n in range(1,6):\n",
    "        print(digit_n(k,n), end=' ')\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Func24. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Описать функцию `mean(x, y)`, вычисляющую среднее арифметическое $\\displaystyle\\frac{x+y}2$ и среднее геометрическое $\\sqrt{x\\cdot y}$ двух положительных чисел $x$ и $y$ и возвращающую результат в виде двух вещественных чисел (`x` и `y` — вещественные параметры). С помощью этой функции найти среднее арифметическое и среднее геометрическое для пар `(a, b)`, `(a, c)`, `(a, d)`, если даны `a`, `b`, `c`, `d`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# напишите текст функции ниже\n",
    "def mean(x, y):\n",
    "    a = (x+y)/2\n",
    "    g = (x*y)**0.5\n",
    "    return a, g\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# блок тестирования функции\n",
    "a = 10.0\n",
    "b = 1000.0\n",
    "c = 40.0\n",
    "d = 360.0\n",
    "print(mean(a,b))\n",
    "mean_a, mean_g = mean(a,c)\n",
    "print(mean_a, mean_g)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Модули"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Написанные и проверенные функции хороши тем, что их можно использовать не только в одной программе. Но если у программиста 100 полезных функций, копировать их текст из программы в программу не очень удобно. Для решения этой проблемы служат *модули*.\n",
    "\n",
    "Строго говоря, модуль в Python &ndash; это любой файл с программой. Однако в данном контексте представляет интерес файл, содержащий написанные (и проверенные!) функции. Подключение его к программе осуществляется командой `import`.\n",
    "\n",
    "---\n",
    "\n",
    "В качестве примера рассмотрим задачу создания модуля `geometry`, содержащего набор функций для вычисления площадей геометрических фигур. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Файл geometry.py\n",
    "# Файл необходимо поместить в ту же папку, \n",
    "#      что и файл-notebook\n",
    "# Еще раз обратите внимание на оформление комментариев!\n",
    "def rectangle(a, b):\n",
    "    '''\n",
    "    функция вычисляет площадь прямоугольника\n",
    "    ширины a и высоты b\n",
    "    '''\n",
    "    return a*b\n",
    "\n",
    "def circle(r):\n",
    "    '''\n",
    "    функция вычисляет площадь круга\n",
    "    радиуса r\n",
    "    '''\n",
    "    return 3.1415926535*r*r\n",
    "\n",
    "def triangle(a, b, c):\n",
    "    '''\n",
    "    функция вычисляет площадь треугольника\n",
    "    со сторонами a, b и c\n",
    "    '''\n",
    "    p = (a+b+c)/2\n",
    "    return (p*(p-a)*(p-b)*(p-c))**0.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "314.15926535\n"
     ]
    }
   ],
   "source": [
    "# для экспериментов\n",
    "import geometry\n",
    "\n",
    "print(geometry.circle(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Как узнать, какие функции есть в модуле?\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__builtins__',\n",
       " '__cached__',\n",
       " '__doc__',\n",
       " '__file__',\n",
       " '__loader__',\n",
       " '__name__',\n",
       " '__package__',\n",
       " '__spec__',\n",
       " 'circle',\n",
       " 'rectangle',\n",
       " 'triangle']"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dir(geometry)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function triangle in module geometry:\n",
      "\n",
      "triangle(a, b, c)\n",
      "    функция вычисляет площадь треугольника\n",
      "    со сторонами a, b и c\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(geometry.triangle)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Как назвать модуль?\n",
    "\n",
    "Поскольку модуль будет импортироваться и использоваться в качестве переменной, то к его названию предъявляются общие требования к именам. Имя модуля не может начинаться с цифры и не может совпадать с одним из *ключевых слов* языка Python, список которых можно посмотреть, выполнив следующий код"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']\n"
     ]
    }
   ],
   "source": [
    "import keyword\n",
    "print(keyword.kwlist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Не рекомендуется называть модуль так же, как какую-либо из встроенных функций. \n",
    "\n",
    "#### Куда поместить модуль?\n",
    "\n",
    "Самый простой вариант &ndash; в одну папку с программой, которая его вызывает. Если говорить более точно, то модуль должен находиться в той папке, в которой интерпретатор Python сможет его найти.  Путь поиска модулей указан в переменной `sys.path` стандартной библиотеки `sys`. В него включены текущая директория (та самая папка с основной программой), а также директории, в которых установлен Python. Кроме того, переменную `sys.path` можно изменять вручную, что позволяет программисту разместить все свои модули в удобном для себя месте. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['C:\\\\Program Files\\\\Python312\\\\python312.zip',\n",
       " 'C:\\\\Program Files\\\\Python312\\\\DLLs',\n",
       " 'C:\\\\Program Files\\\\Python312\\\\Lib',\n",
       " 'C:\\\\Program Files\\\\Python312',\n",
       " '',\n",
       " 'C:\\\\Users\\\\karyakin\\\\AppData\\\\Roaming\\\\Python\\\\Python312\\\\site-packages',\n",
       " 'C:\\\\Program Files\\\\Python312\\\\Lib\\\\site-packages',\n",
       " 'C:\\\\Program Files\\\\Python312\\\\Lib\\\\site-packages\\\\win32',\n",
       " 'C:\\\\Program Files\\\\Python312\\\\Lib\\\\site-packages\\\\win32\\\\lib',\n",
       " 'C:\\\\Program Files\\\\Python312\\\\Lib\\\\site-packages\\\\Pythonwin']"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import sys\n",
    "sys.path"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Можно ли использовать модуль как самостоятельную программу?\n",
    "\n",
    "Можно. Обычно так часто делают, когда хотят проверить корректность работы всех функций модуля. Однако надо помнить, что при импортировании модуля его код выполняется полностью, то есть, если в модуле кроме описания функций есть и текст программы, их вызывающей и что-то при этом выводящей на экран, то в процессе импорта эта программа полностью выполнится, что-то напечатает и т.п.  \n",
    "\n",
    "Чтобы этого избежать, нужно проверить, запущен ли скрипт как программа, или импортирован. Это можно сделать с помощью переменной `__name__`, которая определена в любой программе и равна `'__main__'`, если скрипт запущен в качестве главной программы, и имени модуля, если он импортирован. Например, geometry.py может выглядеть вот так:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Файл geometry.py\n",
    "# Файл необходимо поместить в ту же папку, что и файл-notebook\n",
    "def rectangle(a, b):\n",
    "    '''\n",
    "    функция вычисляет площадь прямоугольника\n",
    "    ширины a и высоты b\n",
    "    '''\n",
    "    return a*b\n",
    "def circle(r):\n",
    "    '''\n",
    "    функция вычисляет площадь круга\n",
    "    радиуса r\n",
    "\n",
    "    '''\n",
    "    return 3.1415926535*r*r\n",
    "def triangle(a, b, c):\n",
    "    '''\n",
    "    функция вычисляет площадь треугольника\n",
    "    со сторонами a, b и c\n",
    "    '''\n",
    "    p = (a+b+c)/2\n",
    "    return (p*(p-a)*(p-b)*(p-c))**0.5\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # тестируем функции\n",
    "    print(rectangle(2,3))\n",
    "    print(circle(10))\n",
    "    print(triangle(30,40,50))\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "geometry_1\n"
     ]
    }
   ],
   "source": [
    "import geometry_1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The Zen of Python, by Tim Peters\n",
      "\n",
      "Beautiful is better than ugly.\n",
      "Explicit is better than implicit.\n",
      "Simple is better than complex.\n",
      "Complex is better than complicated.\n",
      "Flat is better than nested.\n",
      "Sparse is better than dense.\n",
      "Readability counts.\n",
      "Special cases aren't special enough to break the rules.\n",
      "Although practicality beats purity.\n",
      "Errors should never pass silently.\n",
      "Unless explicitly silenced.\n",
      "In the face of ambiguity, refuse the temptation to guess.\n",
      "There should be one-- and preferably only one --obvious way to do it.\n",
      "Although that way may not be obvious at first unless you're Dutch.\n",
      "Now is better than never.\n",
      "Although never is often better than *right* now.\n",
      "If the implementation is hard to explain, it's a bad idea.\n",
      "If the implementation is easy to explain, it may be a good idea.\n",
      "Namespaces are one honking great idea -- let's do more of those!\n"
     ]
    }
   ],
   "source": [
    "import this"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Тонкие вопросы "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.1 Ключевые и позиционные аргументы. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25.0\n",
      "0.04\n"
     ]
    }
   ],
   "source": [
    "def velocity(distance, time):\n",
    "    return distance/time\n",
    "\n",
    "print(velocity(50, 2))\n",
    "print(velocity(2, 50))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Проблема**: глядя на код\n",
    "```python \n",
    "print(velocity(2, 50))\n",
    "```\n",
    "трудно вспомнить/понять, где тут время, а где - расстояние.  \n",
    "**Решение**:\n",
    "```python \n",
    "print(velocity(distance=2, time=50))\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.04\n",
      "25.0\n",
      "10.0\n"
     ]
    }
   ],
   "source": [
    "# поэкспериментируем\n",
    "print(velocity(distance=2, time=50))\n",
    "print(velocity(time=2, distance=50))\n",
    "print(velocity(50, time=5))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "velocity() got multiple values for argument 'distance'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[28], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m# А так?\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[43mvelocity\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m20\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdistance\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m5\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[1;31mTypeError\u001b[0m: velocity() got multiple values for argument 'distance'"
     ]
    }
   ],
   "source": [
    "# А так?\n",
    "v = velocity(20, distance=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "positional argument follows keyword argument (2871288058.py, line 2)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  Cell \u001b[1;32mIn[29], line 2\u001b[1;36m\u001b[0m\n\u001b[1;33m    v = velocity(time=20, 5)\u001b[0m\n\u001b[1;37m                           ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m positional argument follows keyword argument\n"
     ]
    }
   ],
   "source": [
    "# А так?\n",
    "v = velocity(time=20, 5)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "positional argument follows keyword argument (745141039.py, line 2)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  Cell \u001b[1;32mIn[30], line 2\u001b[1;36m\u001b[0m\n\u001b[1;33m    print(end='***', 'hello')\u001b[0m\n\u001b[1;37m                            ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m positional argument follows keyword argument\n"
     ]
    }
   ],
   "source": [
    "# или так\n",
    "print(end='***', 'hello')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2  Значение по умолчанию\n",
    "\n",
    "Предположим, мы хотим написать функцию вычисления веса объекта, имеющего массу `m`. Из школьной физики известна формула веса $P = m g$, где $m$ &ndash; масса объекта, а $g$ &ndash; ускорение свободного падения. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def p(m, g):\n",
    "    return m*g"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Приведенная функция годится для любой планеты, главное &ndash; правильно задать величину ускорения свободного падения. Но если большинство программистов будут использовать эту функцию для вычисления веса объектов, находящихся на Земле, то хорошо бы освободить их от необходимости постоянно писать 9.81 в качестве значения параметра $g$. Делается это так:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "98100.0 38600.0\n"
     ]
    }
   ],
   "source": [
    "def p(m, g=9.81):\n",
    "    return m*g\n",
    "\n",
    "mass = 10000 # 10 тонн\n",
    "p_earth = p(mass) # вес одной тонны на Земле; использовано значение g=9.81\n",
    "p_mars = p(mass, 3.86) # вес одной тонны на Марсе\n",
    "print(p_earth, p_mars)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Число 3.86 &ndash; ускорение свободного падения на Марсе &ndash; найдено в табличке на [Википедии](https://ru.wikipedia.org/wiki/Ускорение_свободного_падения)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.3  Функции с произвольным количеством аргументов\n",
    "Мы уже знакомы со стандартными функциями, количество аргументов у которых может быть любым:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-10 100 90 -110\n"
     ]
    }
   ],
   "source": [
    "a = min(1,2,3,-1,-2,-10,20,30) # 8 аргументов\n",
    "b = max(a*a, a+20, 25)         # 3 аргумента\n",
    "print(a, b, a+b, a-b)          # 4 аргумента"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Напишем функцию `summ`, которая вычисляет сумму всех своих аргументов, сколько бы их не было"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "3\n",
      "14\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "def summ(*args):\n",
    "    s = 0\n",
    "    for x in args:\n",
    "        s += x\n",
    "    return s\n",
    "\n",
    "print(summ(1))\n",
    "print(summ(1,2))\n",
    "print(summ(1,3,10))\n",
    "print(summ())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "А как написать ***хорошую*** функцию, вычисляющую среднее арифметическое своих аргументов?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "def mean(a, *args):\n",
    "    s = a\n",
    "    n = 1\n",
    "    for x in args:\n",
    "        s += x\n",
    "        n += 1\n",
    "    return s/n    \n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.0"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mean(1,2,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "mean() missing 1 required positional argument: 'a'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[36], line 2\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[38;5;66;03m# а так?\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m \u001b[43mmean\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[1;31mTypeError\u001b[0m: mean() missing 1 required positional argument: 'a'"
     ]
    }
   ],
   "source": [
    "# а так?\n",
    "mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Количество ключевых аргументов тоже может быть произвольным. Но чтобы работать с ними, нужно сначала познакомиться с такой структурой данных как словарь. Займемся этим позже..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.4 Области видимости переменных"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Областью видимости** имени переменной называется та часть программного кода, в которой к переменной с этим именем гарантируется однозначный доступ. Под переменной в этом контексте понимаются все *именованные* объекты языка &ndash; собственно переменные, функции, объекты и т.д. Переменную можно использовать только в пределах ее области видимости. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Переменная _видна_ в том блоке, где она первый раз появилась или определена. В таблице ниже &ndash; _причины_ появления переменной:\n",
    "\n",
    "| Действие| Оператор |\n",
    "| :-- | :-- |\n",
    "|Присваивание| \t`x = value`|\n",
    "|Подключение модулей|`import module` <br> `from module import name`|\n",
    "|Определение функции| \t`def my_func():` <br>&nbsp;&nbsp;&nbsp;`    ...`\n",
    "|\n",
    "|Задание параметров функции| \t`def my_func(arg1, arg2,... argN):`<br>&nbsp;&nbsp;&nbsp;`    ...`|\n",
    "|Описание класса \t| `class MyClass:`<br>&nbsp;&nbsp;&nbsp;`    ...` |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Области видимости в Python"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"legb.png\" width=\"150\" align=left> \n",
    "\n",
    "\n",
    "  &nbsp; **L**ocal &ndash; локальная,\n",
    "  \n",
    "  &nbsp; **E**nclosing &ndash; объемлющая (другое название - nonlocal),\n",
    "  \n",
    "  &nbsp; **G**lobal &ndash; глобальная,\n",
    "  \n",
    "  &nbsp; **B**uilt-in &ndash; встроенная\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Локальная область видимости &ndash; это блок кода (или тело) любой функции. В ней находятся имена, которые вы определяете внутри функции. Эти имена будут видны только в пределах тела функции. Локальная область видимости создается при вызове функции, а не при ее определении, поэтому у вас будет столько же различных локальных областей, сколько различных вызовов функций произошло в программе. Каждый вызов приводит к созданию новой локальной области.\n",
    "\n",
    "* Объемлющая (или охватывающая, или нелокальная) область видимости &ndash; это локальная область видимости функции *с точки зрения* вложенной (описанной внутри нее) функции. В примере ниже переменная `a` будет создана в объемлющей области с точки зрения функции `g` и в локальной области с точки зрения функции `f`.\n",
    "\n",
    "```python\n",
    "def f(x):\n",
    "    a = 1\n",
    "    def g(z):   # пример вложенной функции\n",
    "        return z+2\n",
    "    return g(a+x)    \n",
    "```\n",
    "\n",
    "* Глобальная область видимости &ndash; это самая верхняя область в программе или модуле. Имена в этой области видны (т. е. доступны) всюду в программном коде.\n",
    "\n",
    "* Встроенная область видимости создается или загружается всякий раз, когда вы запускаете скрипт или открываете интерактивный сеанс. Эта область содержит такие имена как ключевые слова, функции, исключения и другие атрибуты, встроенные в Python. Имена из этой области также доступны всюду в программе. \n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "global\n"
     ]
    }
   ],
   "source": [
    "x1 = 'global'\n",
    "def f():\n",
    "    def g():\n",
    "        print(x1)\n",
    "    g()\n",
    "\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "enclosing\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    x2 = 'enclosing'\n",
    "    def g():\n",
    "        print(x2)\n",
    "    g()\n",
    "\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "local\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    def g():\n",
    "        x3 = 'local'\n",
    "        print(x3)\n",
    "    g()\n",
    "\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<built-in function min>\n"
     ]
    }
   ],
   "source": [
    "def f():\n",
    "    def g():\n",
    "        print(min)\n",
    "    g()\n",
    "\n",
    "f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'x3' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[40], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mx3\u001b[49m\n",
      "\u001b[1;31mNameError\u001b[0m: name 'x3' is not defined"
     ]
    }
   ],
   "source": [
    "x3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Еще раз.** Локальные переменные доступны только внутри тела функции. Попытка их использовать \"снаружи\" приводит к ошибке:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5+10=15\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'local' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[42], line 8\u001b[0m\n\u001b[0;32m      6\u001b[0m b \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m10\u001b[39m\n\u001b[0;32m      7\u001b[0m add(a,b)\n\u001b[1;32m----> 8\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mlocal\u001b[49m)    \n",
      "\u001b[1;31mNameError\u001b[0m: name 'local' is not defined"
     ]
    }
   ],
   "source": [
    "def add(a,b):\n",
    "    local = a+b\n",
    "    print(f'{a}+{b}={local}')\n",
    "\n",
    "a = 5\n",
    "b = 10\n",
    "add(a,b)\n",
    "print(local)    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Механизмы устранения \"конфликтов\" имен**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Что выведет программа, 10 или 100?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "::::::> 100\n",
      "10\n"
     ]
    }
   ],
   "source": [
    "z = 10\n",
    "def z_print():\n",
    "    z = 100\n",
    "    print(f'::::::> {z}')\n",
    "\n",
    "z_print()\n",
    "print(z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В общем случае поиск переменной идет по схеме **L $\\to$ E $\\to$ G $\\to$ B**\n",
    "\n",
    "Но посмотрите на следующий пример. Почему при вызове второй функции возникает ошибка?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "х плюс 1 = 101\n"
     ]
    },
    {
     "ename": "UnboundLocalError",
     "evalue": "cannot access local variable 'x' where it is not associated with a value",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mUnboundLocalError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 11\u001b[39m\n\u001b[32m      8\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mx плюс 1 = \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n\u001b[32m     10\u001b[39m x_print1()\n\u001b[32m---> \u001b[39m\u001b[32m11\u001b[39m \u001b[43mx_print2\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 7\u001b[39m, in \u001b[36mx_print2\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      6\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mx_print2\u001b[39m():\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m     x = \u001b[43mx\u001b[49m+\u001b[32m1\u001b[39m\n\u001b[32m      8\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mx плюс 1 = \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n",
      "\u001b[31mUnboundLocalError\u001b[39m: cannot access local variable 'x' where it is not associated with a value"
     ]
    }
   ],
   "source": [
    "x = 100\n",
    "\n",
    "def x_print1():\n",
    "    print(f'х плюс 1 = {x+1}')\n",
    "\n",
    "def x_print2():\n",
    "    x = x+1\n",
    "    print(f'x плюс 1 = {x}')\n",
    "\n",
    "x_print1()\n",
    "x_print2()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Кроме правила поиска имени **L $\\to$ E $\\to$ G $\\to$ B** нужно помнить правила _появления_ переменных (Таблица выше). Если мы что-то присваиваем переменной внутри тела функции, переменная создается в этот момент, а значит, является локальной. Локальная переменная `x` внутри функции `x_print2` еще не имеет  значения, поэтому выражение `x + 1` не может быть вычислено."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**А если очень-очень нужно...** <br/>\n",
    "Может ли функция `x_print2` все-таки изменить значение глобальной переменной `x`? \n",
    "\n",
    "Да. Для этого используется описатель `global` в теле функции. В Python версии 3 появился ещё и описатель `nonlocal`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x плюс 1 = 101\n",
      "101\n"
     ]
    }
   ],
   "source": [
    "x = 100\n",
    "\n",
    "def x_print2():\n",
    "    global x\n",
    "    x = x+1\n",
    "    print(f'x плюс 1 = {x}')\n",
    "    \n",
    "x_print2()\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Глобальные переменные и нелокальные переменные"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Значение локальной функции: 11\n"
     ]
    }
   ],
   "source": [
    "# использование глобальной переменной\n",
    "def enclosing_f():\n",
    "    p = 20 \n",
    "    def local_f(x):\n",
    "        global  p\n",
    "        p += x\n",
    "        return p\n",
    "    print (f'Значение локальной функции: {local_f(1)}')\n",
    "\n",
    "p = 10 # используется это значение!\n",
    "enclosing_f()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Значение локальной функции: 21\n",
      "Значение локальной функции: 21\n"
     ]
    }
   ],
   "source": [
    "# использование нелокальной переменной\n",
    "def enclosing_f():\n",
    "    p = 20 # используется это значение!\n",
    "    def local_f(x):\n",
    "        nonlocal p\n",
    "        p += x\n",
    "        return p\n",
    "    print (f'Значение локальной функции: {local_f(1)}')\n",
    "\n",
    "p = 10\n",
    "enclosing_f()\n",
    "enclosing_f()\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "no binding for nonlocal 'p' found (2595057416.py, line 7)",
     "output_type": "error",
     "traceback": [
      "  \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[31m    \u001b[39m\u001b[31mnonlocal p\u001b[39m\n    ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m no binding for nonlocal 'p' found\n"
     ]
    }
   ],
   "source": [
    "# попытка объявления нелокальной переменной,\n",
    "# отсутствующей в объемлющей функции, \n",
    "# приводит к ошибке на этапе компиляции\n",
    "def enclosing_function():\n",
    "    q = 20\n",
    "    def local_function(x):\n",
    "        nonlocal p \n",
    "        p += x\n",
    "        return p\n",
    "    print (local_function(10))\n",
    "\n",
    "p = 10\n",
    "enclosing_function()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Можно ли придумать осмысленный пример, когда функция **должна** менять значение глобальной переменной?\n",
    "\n",
    "Да, один пример см. ниже. Таким же образом будет использована глобальная переменная при решении задачи **Recur4**. Однако существует общее мнение: по возможности лучше избегать использования глобальных переменных вообще, и их изменения внутри функции &ndash; в особенности. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Теперь у меня 6 конфет.\n",
      "Теперь у меня 7 конфет.\n",
      "Теперь у меня 8 конфет.\n",
      "8\n"
     ]
    }
   ],
   "source": [
    "# Пример с изменением глобальной переменной\n",
    "def get_candy():\n",
    "    global candy \n",
    "    candy += 1\n",
    "    print(f'Теперь у меня {candy} конфет.')\n",
    "\n",
    "candy = 5\n",
    "get_candy()\n",
    "get_candy()\n",
    "get_candy()\n",
    "print(candy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Про области видимости, локальные и глобальные переменные можно ещё почитать, например, тут:<br>\n",
    "https://realpython.com/python-scope-legb-rule/ (на английском)<br>\n",
    "https://habr.com/ru/company/otus/blog/487952/ (на русском)<br>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Для закрепления.**\\\n",
    "Сколько раз будет выведено `x=20`, а сколько `x=10`?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "** вне f. 1: x=20.01, id=1604034930288\n",
      "внутри f. 1: x=20.01, id=1604034930288\n",
      "внутри f. 2: x=10, id=140721886111128\n",
      "** вне f. 2: x=20.01, id=1604034930288\n"
     ]
    }
   ],
   "source": [
    "def f(x):\n",
    "    print(f'внутри f. 1: x={x}, id={id(x)}')\n",
    "    x = 10\n",
    "    print(f'внутри f. 2: x={x}, id={id(x)}')\n",
    "\n",
    "x = 20.01\n",
    "print(f'** вне f. 1: x={x}, id={id(x)}')\n",
    "f(x)\n",
    "print(f'** вне f. 2: x={x}, id={id(x)}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "** вне f. 1: x=20, id=140721886111448\n"
     ]
    },
    {
     "ename": "UnboundLocalError",
     "evalue": "cannot access local variable 'x' where it is not associated with a value",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mUnboundLocalError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 10\u001b[39m\n\u001b[32m      8\u001b[39m x = \u001b[32m20\u001b[39m\n\u001b[32m      9\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33m** вне f. 1: x=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, id=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mid\u001b[39m(x)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m10\u001b[39m \u001b[43mf\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m     11\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33m** вне f. 2: x=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, id=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mid\u001b[39m(x)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 4\u001b[39m, in \u001b[36mf\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m      3\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mf\u001b[39m():\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mвнутри f. 1: x=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mx\u001b[49m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, id=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mid\u001b[39m(x)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n\u001b[32m      5\u001b[39m     x = \u001b[32m10\u001b[39m\n\u001b[32m      6\u001b[39m     \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m'\u001b[39m\u001b[33mвнутри f. 2: x=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mx\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, id=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mid\u001b[39m(x)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m'\u001b[39m)\n",
      "\u001b[31mUnboundLocalError\u001b[39m: cannot access local variable 'x' where it is not associated with a value"
     ]
    }
   ],
   "source": [
    "# А если так?\n",
    "\n",
    "def f():\n",
    "    print(f'внутри f. 1: x={x}, id={id(x)}')\n",
    "    x = 10\n",
    "    print(f'внутри f. 2: x={x}, id={id(x)}')\n",
    "\n",
    "x = 20\n",
    "print(f'** вне f. 1: x={x}, id={id(x)}')\n",
    "f()\n",
    "print(f'** вне f. 2: x={x}, id={id(x)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.5 Оператор-заглушка. Ключевое слово `pass`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "При разработке программ сложной структуры часто сначала составляется список функций, а потом начинается пошаговое написание их кода и тестирование. Однако код, приведенный ниже, не позволит запустить тестирование из-за синтаксических ошибок."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "ename": "IndentationError",
     "evalue": "expected an indented block after function definition on line 4 (3786102511.py, line 7)",
     "output_type": "error",
     "traceback": [
      "  \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[31m    \u001b[39m\u001b[31mif __name__ == \"__main__\":\u001b[39m\n    ^\n\u001b[31mIndentationError\u001b[39m\u001b[31m:\u001b[39m expected an indented block after function definition on line 4\n"
     ]
    }
   ],
   "source": [
    "def rectangle(a, b):\n",
    "    return a*b\n",
    "\n",
    "def circle(r):\n",
    "    # напишем потом\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # тестируем функции\n",
    "    print(rectangle(2,3))\n",
    "    # потестируем потом\n",
    "    # print(circle(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Возможный выход &ndash; использование ключевого слова `pass`, которое означает в данном контексте \"не делаем ничего, переходим к следующему участку кода\". Данное ключевое слово часто используется в процессе разработки и отладки собственных классов (тема \"ООП на Питон\" будет позже). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "def rectangle(a, b):\n",
    "    return a*b\n",
    "\n",
    "def circle(r):\n",
    "    ...\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    # тестируем функции\n",
    "    print(rectangle(2,3))\n",
    "    #print(circle(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Рекурсия\n",
    "\n",
    "<div align=right><em>\n",
    "    Чтобы понять рекурсию, <br/>нужно сначала понять рекурсию</em>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы видели, функция может вызывать *другую* функцию. Но функция также может вызывать и саму себя! Такой вызов называется **рекурсией**, а сама функция называется рекурсивной. \n",
    "\n",
    "Рассмотрим это на примере функции вычисления факториала. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recur1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Описать **рекурсивную** функцию `fact_r(n)` целого типа, вычисляющую значение факториала \n",
    "$$n! = 1·2·…·n$$ \n",
    "(`n` > 0 — параметр целого типа). С помощью этой функции найти факториалы пяти данных целых чисел. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рекурсивное определение факториала:\n",
    "$$\n",
    "  n! = (n-1)!\\cdot n\n",
    "$$\n",
    "$$\n",
    "  0! \\equiv 1\n",
    "$$  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# напишите текст функции ниже\n",
    "def fact_r(n):\n",
    "    if n==0:\n",
    "        return 1\n",
    "    return fact_r(n-1)*n    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10! = 3628800\n",
      "5! = 120\n",
      "50! = 30414093201713378043612608166064768844377641568960512000000000000\n",
      "1! = 1\n",
      "8! = 40320\n"
     ]
    }
   ],
   "source": [
    "# блок тестирования функции\n",
    "nn = 10, 5, 50, 1, 8\n",
    "for n in nn:\n",
    "    print(f'{n}! = {fact_r(n)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Некоторые выводы**<br/>\n",
    "* В описании рекурсивной функции должна присутствовать проверка условия, определяющего, по какой ветке пойдет вычисление: по рекурсивной — в этой ветке произойдёт вызов функцией себя самой, или **терминальной**, которая закончит вычисление и вернёт результат.\n",
    "* Отсутствие терминальной ветки или ошибки в реализации условия перехода на эту ветку приведут к зацикливанию программы. \n",
    "* К тестированию рекурсивных функций нужно относиться особенно ответственно. Особенно важно проверять срабатывание условия завершения рекурсии."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Знаменитая рекурсия** &ndash; но программировать так не надо :)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def short_story():\n",
    "    print(\"У попа была собака, он её любил.\")\n",
    "    print(\"Она съела кусок мяса, он её убил,\")\n",
    "    print(\"В землю закопал,\")\n",
    "    print(\"Надпись написал, что\")\n",
    "    short_story()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Запускать на свой страх и риск!\n",
    "# В 99% случаев вызывает крах jupyter-сервера\n",
    "short_story()\n",
    "\n",
    "#Можно запустиь в IDLE - там не страшно, см. ниже про RecursionError"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В литературе рекурсия встречается довольно часто. Вот, например, цитата из Станислава Лема (\"Звездные дневники Ийона Тихого. Путешествие четырнадцатое\"), где главный герой ищет в энциклопедии, что такое ***сепульки***:          "
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "     Нашел следующие краткие сведения:\n",
    "     \"СЕПУЛЬКИ  -  важный  элемент цивилизации ардритов  (см.)  с  планеты\n",
    "Энтеропия (см.). См. СЕПУЛЬКАРИИ\".\n",
    "     Я последовал этому совету и прочел:\n",
    "     \"СЕПУЛЬКАРИИ - устройства для сепуления (см.)\".\n",
    "     Я поискал \"Сепуление\"; там значилось:\n",
    "     \"СЕПУЛЕНИЕ  - занятие ардритов (см.) с планеты Энтеропия  (см.).  См.\n",
    "СЕПУЛЬКИ\".\n",
    "     Круг  замкнулся, больше искать было негде.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Рекурсия без терминальной ветви имеет еще одно название &ndash; **порочный круг.** "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Глубина рекурсии. Стек. \n",
    "\n",
    "Существует предел глубины возможной рекурсии, который зависит от реализации Python. Когда предел достигнут, возникает исключение `RecursionError`.\n",
    "\n",
    "Сколько раз сработала функция в предыдущем примере? <br/> Чтобы узнать это, немножко откорректируем текст, убрав вывод на печать - из-за большого объема этого вывода и может произойти крах jupiter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Unexpected exception formatting exception. Falling back to standard exception\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Traceback (most recent call last):\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\IPython\\core\\interactiveshell.py\", line 3699, in run_code\n",
      "    exec(code_obj, self.user_global_ns, self.user_ns)\n",
      "    ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "  File \"C:\\Users\\karyakin\\AppData\\Local\\Temp\\ipykernel_10936\\2648910071.py\", line 11, in <module>\n",
      "    short_story()\n",
      "    ~~~~~~~~~~~^^\n",
      "  File \"C:\\Users\\karyakin\\AppData\\Local\\Temp\\ipykernel_10936\\2648910071.py\", line 9, in short_story\n",
      "    short_story()\n",
      "    ~~~~~~~~~~~^^\n",
      "  File \"C:\\Users\\karyakin\\AppData\\Local\\Temp\\ipykernel_10936\\2648910071.py\", line 9, in short_story\n",
      "    short_story()\n",
      "    ~~~~~~~~~~~^^\n",
      "  File \"C:\\Users\\karyakin\\AppData\\Local\\Temp\\ipykernel_10936\\2648910071.py\", line 9, in short_story\n",
      "    short_story()\n",
      "    ~~~~~~~~~~~^^\n",
      "  [Previous line repeated 24 more times]\n",
      "RecursionError: maximum recursion depth exceeded\n",
      "\n",
      "During handling of the above exception, another exception occurred:\n",
      "\n",
      "Traceback (most recent call last):\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\IPython\\core\\interactiveshell.py\", line 2194, in showtraceback\n",
      "    stb = self.InteractiveTB.structured_traceback(\n",
      "        etype, value, tb, tb_offset=tb_offset\n",
      "    )\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\IPython\\core\\ultratb.py\", line 1185, in structured_traceback\n",
      "    return FormattedTB.structured_traceback(\n",
      "           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n",
      "        self, etype, evalue, etb, tb_offset, context\n",
      "        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "    )\n",
      "    ^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\IPython\\core\\ultratb.py\", line 1056, in structured_traceback\n",
      "    return VerboseTB.structured_traceback(\n",
      "           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n",
      "        self, etype, evalue, etb, tb_offset, context\n",
      "        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "    )\n",
      "    ^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\IPython\\core\\ultratb.py\", line 864, in structured_traceback\n",
      "    formatted_exceptions: list[list[str]] = self.format_exception_as_a_whole(\n",
      "                                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^\n",
      "        etype, evalue, etb, context, tb_offset\n",
      "        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "    )\n",
      "    ^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\IPython\\core\\ultratb.py\", line 749, in format_exception_as_a_whole\n",
      "    records = self.get_records(etb, context, tb_offset) if etb else []\n",
      "              ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\IPython\\core\\ultratb.py\", line 851, in get_records\n",
      "    res = list(stack_data.FrameInfo.stack_data(etb, options=options))[tb_offset:]\n",
      "          ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\stack_data\\core.py\", line 597, in stack_data\n",
      "    yield from collapse_repeated(\n",
      "    ...<4 lines>...\n",
      "    )\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\stack_data\\utils.py\", line 83, in collapse_repeated\n",
      "    yield from map(mapper, original_group)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\stack_data\\core.py\", line 587, in mapper\n",
      "    return cls(f, options)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\stack_data\\core.py\", line 551, in __init__\n",
      "    self.executing = Source.executing(frame_or_tb)\n",
      "                     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\executing\\executing.py\", line 224, in executing\n",
      "    source = cls.for_frame(frame)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\executing\\executing.py\", line 143, in for_frame\n",
      "    return cls.for_filename(frame.f_code.co_filename, frame.f_globals or {}, use_cache)\n",
      "           ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\executing\\executing.py\", line 172, in for_filename\n",
      "    return cls._for_filename_and_lines(filename, tuple(lines))\n",
      "           ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\executing\\executing.py\", line 183, in _for_filename_and_lines\n",
      "    result = source_cache[(filename, lines)] = cls(filename, lines)\n",
      "                                               ~~~^^^^^^^^^^^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\executing\\executing.py\", line 134, in __init__\n",
      "    visitor.visit(self.tree)\n",
      "    ~~~~~~~~~~~~~^^^^^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 506, in visit\n",
      "    return visitor(node)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 514, in generic_visit\n",
      "    self.visit(item)\n",
      "    ~~~~~~~~~~^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 506, in visit\n",
      "    return visitor(node)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\site-packages\\executing\\executing.py\", line 424, in visit_FunctionDef\n",
      "    self.visit(child)\n",
      "    ~~~~~~~~~~^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 506, in visit\n",
      "    return visitor(node)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 516, in generic_visit\n",
      "    self.visit(value)\n",
      "    ~~~~~~~~~~^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 506, in visit\n",
      "    return visitor(node)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 516, in generic_visit\n",
      "    self.visit(value)\n",
      "    ~~~~~~~~~~^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 506, in visit\n",
      "    return visitor(node)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 516, in generic_visit\n",
      "    self.visit(value)\n",
      "    ~~~~~~~~~~^^^^^^^\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 506, in visit\n",
      "    return visitor(node)\n",
      "  File \"C:\\Program Files\\Python314\\Lib\\ast.py\", line 510, in generic_visit\n",
      "    for field, value in iter_fields(node):\n",
      "                        ~~~~~~~~~~~^^^^^^\n",
      "RecursionError: maximum recursion depth exceeded\n"
     ]
    }
   ],
   "source": [
    "story_counter = 0\n",
    "def short_story():\n",
    "    global story_counter\n",
    "#     print(\"У попа была собака, он её любил.\")\n",
    "#     print(\"Она съела кусок мяса, он её убил,\")\n",
    "#     print(\"В землю закопал,\")\n",
    "#     print(\"Надпись написал, что\")\n",
    "    story_counter += 1\n",
    "    short_story()\n",
    "    \n",
    "short_story()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "27"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "story_counter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Узнать и/или изменить предел глубины рекурсии можно с помощью модуля `sys`:\n",
    "\n",
    "```python\n",
    "import sys\n",
    "sys.setrecursionlimit(limit)\n",
    "sys.getrecursionlimit()\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# покспериментируем здесь и потом снова вызовем функцию short_story:\n",
    "import sys\n",
    "sys.setrecursionlimit(1000\n",
    "                     )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recur3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Описать рекурсивную функцию `power_n(x, n)` вещественного типа, находящую значение `n`-й степени числа `x` по формулам: \n",
    "$$\\small\n",
    "x^n=\\left\\{\\begin{array}{ll}\n",
    "1, &\\mbox{если }n=0,\\\\\n",
    "\\displaystyle\\left(x^{\\frac{n}2}\\right)^2, &\\text{при четных }n>0,\\\\\n",
    "\\displaystyle x\\cdot x^{n-1}, &\\text{при  нечетных }n>0,\\\\\n",
    "\\displaystyle 1/{x^{-n}}, &\\text{при } n<0\n",
    "\\end{array}\n",
    "\\right.\n",
    "$$\n",
    "($x \\neq 0$ — вещественное число, $n$ — целое; в формуле для четных $n$ должна использоваться операция целочисленного деления).\n",
    "\n",
    "С помощью этой функции найти значения $x^n$ для данного `x` при пяти данных значениях `n`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# напишите текст функции ниже\n",
    "def power_n(x, n):\n",
    "    if n==0:\n",
    "        return 1\n",
    "    if n<0:\n",
    "        return 1/power_n(x, -n)\n",
    "    elif n%2==0:\n",
    "        a = power_n(x, n//2)\n",
    "        return a*a\n",
    "    else:\n",
    "        return x*power_n(x, n-1)\n",
    "    \n",
    "\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.25\n",
      "1\n",
      "1024.0\n",
      "32.0\n",
      "1.0715086071862673e+301\n"
     ]
    }
   ],
   "source": [
    "# блок тестирования функции\n",
    "x = 2.0\n",
    "nn = -2, 0, 10, 5, 1000 \n",
    "for n in nn:\n",
    "    print(power_n(x, n))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "2**1000"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Recur 4."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Описать рекурсивную функцию `fib1(n)` целого типа, вычисляющую `n`-й элемент последовательности *чисел Фибоначчи* (`n` — целое число): \n",
    "$$f_1 = f_2 = 1, \\ f_k = f_{k-2} + f_{k-1}, \\    k = 3, 4, … .$$\n",
    "С помощью этой функции найти пять чисел Фибоначчи с данными номерами, и вывести эти числа вместе с количеством рекурсивных вызовов функции `fib1`, потребовавшихся для их нахождения. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "counter = 0\n",
    "# напишите текст функции ниже\n",
    "def fib1(n):\n",
    "    global counter\n",
    "    counter += 1\n",
    "    if n==1 or n==2:\n",
    "        return 1\n",
    "    return fib1(n-2) + fib1(n-1)    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5 9\n",
      "55 109\n",
      "610 1219\n",
      "6765 13529\n",
      "832040 1664079\n"
     ]
    }
   ],
   "source": [
    "# блок тестирования функции\n",
    "nn = 5, 10, 15, 20, 30\n",
    "for n in nn:\n",
    "    counter = 0\n",
    "    print(fib1(n), counter)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Попробуем сделать вывод..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"#0000FF\">\n",
    "\n",
    "**Связь рекурсии и стека**: задача «Разворот последовательности» c сайта http://pythontutor.ru <br>\n",
    "\n",
    "Дана последовательность целых чисел, заканчивающаяся числом 0. Выведите эту последовательность в обратном порядке. При решении этой задачи ***нельзя*** пользоваться массивами и прочими динамическими структурами данных. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def reverse():\n",
    "    x = int(input(\"x = (0 для завершения)\"))\n",
    "    if x!=0:\n",
    "        reverse()\n",
    "    print(x)\n",
    "\n",
    "reverse()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ханойские башни\n",
    "\n",
    "<img src=\"hanoi.jpg\" width=\"50%\" align = left style=\"padding-right: 20px\">\n",
    "    \n",
    "**Оригинальная головоломка**. Даны три стержня, на один из которых нанизаны восемь колец, причём кольца отличаются размером и лежат меньшее на большем. Задача состоит в том, чтобы перенести пирамиду из восьми колец за наименьшее число ходов на другой стержень. За один раз разрешается переносить только одно кольцо, причём нельзя класть большее кольцо на меньшее. \n",
    "\n",
    "**Легенда**. В Великом храме города Бенарес, под собором, отмечающим середину мира, находится бронзовый диск, на котором укреплены 3 алмазных стержня, высотой в один локоть и толщиной с пчелу. Давным-давно, в самом начале времён, монахи этого монастыря провинились перед богом Брахмой. Разгневанный Брахма воздвиг три высоких стержня и на один из них возложил 64 диска, сделанных из чистого золота. Причём так, что каждый меньший диск лежит на большем. \n",
    "Как только все 64 диска будут переложены со стержня, на который Брахма сложил их при создании мира, на другой стержень, башня вместе с храмом обратятся в пыль и под громовые раскаты погибнет мир. \n",
    "\n",
    "Источник информации: <a href=\"https://ru.wikipedia.org/wiki/%D0%A5%D0%B0%D0%BD%D0%BE%D0%B9%D1%81%D0%BA%D0%B0%D1%8F_%D0%B1%D0%B0%D1%88%D0%BD%D1%8F\">статья в Википедии</a>.\n",
    "    \n",
    "<a href=\"towers.swf\">Демонстрация</a>\n",
    "\n",
    "**Задача**. Разработать программу, перемещающую диски с левого стержня на правый по указанному правилу.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": true,
    "jupyter": {
     "outputs_hidden": true
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 ====>  2\n",
      "1 ====>  3\n",
      "2 ====>  3\n",
      "1 ====>  2\n",
      "3 ====>  1\n",
      "3 ====>  2\n",
      "1 ====>  2\n",
      "1 ====>  3\n",
      "2 ====>  3\n",
      "2 ====>  1\n",
      "3 ====>  1\n",
      "2 ====>  3\n",
      "1 ====>  2\n",
      "1 ====>  3\n",
      "2 ====>  3\n"
     ]
    }
   ],
   "source": [
    "# текст программы\n",
    "def move(n, start, end, empty):\n",
    "    if n>0:\n",
    "        move(n-1, start, empty, end)\n",
    "        print(f'{start} ====>  {end}')\n",
    "        move(n-1, empty, end, start)\n",
    "move(4, start=1, end=3, empty=2)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "584542046090.6263"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(2**64-1)/3600/24/365.25"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://staticlb.rmr.rocks/uploads/pics/02/55/818.jpg\" align=\"left\" width=\"140\">\n",
    "\n",
    "**Идея использована в рассказе:**\n",
    "Артур Кларк. [Девять миллиардов имён Бога.](https://librebook.me/the_nine_billion_names_of_god/vol1/1) \n",
    " \n",
    "***Чтобы хорошо сдать экзамен, лучше бы прочитать!***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Визуализация рекурсии (для самостоятельной работы)\n",
    "\n",
    "Источник: http://aliev.me/runestone/Recursion/intro-VisualizingRecursion.html\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Черепашья графика**.\n",
    "\n",
    "http://acm.mipt.ru/twiki/bin/view/Cintro/PythonTurtleInfo (кратко, на русском)\n",
    "\n",
    "https://docs.python.org/3/library/turtle.html (подробная документация, на английском)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# программа откроется в отдельном окне\n",
    "from turtle import *\n",
    "color('red', 'yellow')\n",
    "begin_fill()\n",
    "while True:\n",
    "    forward(200)\n",
    "    left(170)\n",
    "    if abs(pos()) < 1:\n",
    "        break\n",
    "end_fill()\n",
    "done()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Подключение (упрощенной) черепашьей графики для jupyter notebook**\n",
    "\n",
    "Выполнить в командной строке (Windows PowerShell)\n",
    "```\n",
    "pip install ipyturtle\n",
    "jupyter nbextension enable --py --sys-prefix ipyturtle\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Основные команды управления черепашкой:\n",
    "\n",
    "```python\n",
    "back(length)\n",
    "forward(length)\n",
    "left(degree)\n",
    "right(degree)\n",
    "pendown()\n",
    "penup()\n",
    "pencolor(r, g, b)\n",
    "showturtle()\n",
    "hideturtle()\n",
    "reset()\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Инициализация окна рисования**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ipyturtle import Turtle\n",
    "my_t = Turtle(fixed=False)\n",
    "my_t"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sys.setrecursionlimit(1000)\n",
    "# рекурсивное рисование спирали\n",
    "def draw_spiral(t, line_len):\n",
    "        if line_len > 0:\n",
    "            t.forward(line_len)\n",
    "            t.right(90)\n",
    "            draw_spiral(t,line_len-3)\n",
    "    \n",
    "# холст в исходном положении\n",
    "my_t.reset()\n",
    "# опускаем черепашку в левый нижний угол\n",
    "my_t.penup()\n",
    "my_t.back(150)\n",
    "my_t.left(90)\n",
    "my_t.forward(150)\n",
    "my_t.right(90)\n",
    "my_t.pendown()\n",
    "# рисуем спираль\n",
    "draw_spiral(my_t,300)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# рекурсивное рисование дерева\n",
    "def tree(branch_len,t):\n",
    "    t.pencolor(r=4*branch_len, g=255-3*branch_len, b = 2*branch_len)\n",
    "    t.pendown()\n",
    "    if branch_len > 5:\n",
    "        t.forward(branch_len)\n",
    "        t.right(20)\n",
    "        tree(branch_len-7,t)\n",
    "        t.left(40)\n",
    "        tree(branch_len-7,t)\n",
    "        t.right(20)\n",
    "        t.penup()\n",
    "        t.back(branch_len)\n",
    "\n",
    "# холст в исходном положении\n",
    "my_t.reset()\n",
    "# опускаем черепашку вниз\n",
    "my_t.penup()\n",
    "my_t.back(100)\n",
    "my_t.pendown()\n",
    "# рисуем дерево\n",
    "tree(50,my_t)\n",
    "my_t.hideturtle()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Вместо послесловия\n",
    "\n",
    "\n",
    ">**12.1.1 Why Functions?**\n",
    ">\n",
    ">There is a long and disreputable tradition of writing very long functions – hundreds of lines long. I once encountered a single (handwritten) function with more than 32,768 lines of code. Writers of such functions seem to fail to appreciate one of the primary purposes of functions: to break up complicated computations into meaningful chunks and name them. We want our code to be comprehensible, because that is the ﬁrst step on the way to maintainability. The ﬁrst step to comprehensibility is to break computational tasks into comprehensible chunks (represented as functions and classes) and name those. Such functions then provide the basic vocabulary of computation, just as the types (built-in and user-deﬁned) provide the basic vocabulary of data. ... \n",
    "Next, we can compose functions representing common or specialized tasks into larger computations. \n",
    ">\n",
    ">The number of errors in code correlates strongly with the amount of code and the complexity of the code. Both problems can be addressed by using more and shorter functions. Using a function to do a speciﬁc task often saves us from writing a speciﬁc piece of code in the middle of other code; making it a function forces us to name the activity and document its dependencies. Also, function\n",
    "call and return saves us from using error-prone control structures... \n",
    ">\n",
    ">The most basic advice is to keep a function of a size so that you can look at it in total on a screen. Bugs tend to creep in when we can view only part of an algorithm at a time. For many programmers that puts a limit of about 40 lines on a function. My ideal is a much smaller size still, maybe an average of 7 lines. \n",
    ">\n",
    ">In essentially all cases, the cost of a function call is not a signiﬁcant factor ... Use functions as a structuring mechanism.\n",
    "\n",
    "<div align=right>\n",
    "    Bjarne Stroustrup. The C++ Programming Language. Fourth Edition. Addison-Wesley, 2013."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.14.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
