{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python\n",
    "\n",
    "## Управление потоком исполнения, функции и модули\n",
    "\n",
    "### Раздел 1. Конструкции для управления потоком исполнения\n",
    "\n",
    "Мы рассмотрели большую часть конструкций для управления потоком исполнения в лекции 1. Это if-elif-else выражения, циклы while и for ... in. Единственное, что ускользнуло от нашего внимания — условные выражения. Это своего рода ответ на тернарый оператор в языках с C-подобным синтаксисом.\n",
    "\n",
    "В общем случае условное выражение выглядит так:\n",
    "\n",
    "*выражение* if *условие* else *альтернатива*\n",
    "\n",
    "Например:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n"
     ]
    }
   ],
   "source": [
    "for i in range(1, 21):\n",
    "    print(\"Нечетное\" if i & 1 else \"Четное\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Отметим отдельно инструкцию pass, которе позволяет вам использовать ее вместо любого блока кода, где он требуется. Как правило, используется при написании кода, для замены блоков в тех местах, где они еще не написаны, но будут:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(1, 10):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "unexpected EOF while parsing (<ipython-input-3-6df1f476ba84>, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-3-6df1f476ba84>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m    for i in range(1, 10):\u001b[0m\n\u001b[0m                          ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m unexpected EOF while parsing\n"
     ]
    }
   ],
   "source": [
    "for i in range(1, 10):"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для циклов также есть инструкции, нерасмотренные ранее: continue и break. Инструкция continue переходит на следующий этап выполнения цикла, а инструкция break обрывает его выполнение:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "Четное\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n"
     ]
    }
   ],
   "source": [
    "for i in range(1, 21):\n",
    "    if i & 1:\n",
    "        continue\n",
    "    print(\"Нечетное\" if i & 1 else \"Четное\")\n",
    "    \n",
    "print(\"\\n\\n\\n\")\n",
    "    \n",
    "for i in range(1, 21):\n",
    "    if i > 5:\n",
    "        break\n",
    "    print(\"Нечетное\" if i & 1 else \"Четное\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "После циклов можно писать блок с else, который будет выполнен в случае нормального завершения цикла:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Цикл завершен нормально\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n"
     ]
    }
   ],
   "source": [
    "for i in range(1, 21):\n",
    "    print(\"Нечетное\" if i & 1 else \"Четное\")\n",
    "else:\n",
    "    print(\"Цикл завершен нормально\")\n",
    "    \n",
    "print(\"\\n\\n\\n\")\n",
    "    \n",
    "for i in range(1, 21):\n",
    "    if i > 5:\n",
    "        break\n",
    "    print(\"Нечетное\" if i & 1 else \"Четное\")\n",
    "else:\n",
    "    print(\"Цикл завершен нормально\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 2. Обработка исключений\n",
    "\n",
    "Исключительные ситуации требуют... исключений! Хотя иногда вы можете столнуться с тем, что некоторые вызовы из сторонних библиотек предполагают анализ обработку ошибок в стиле C (возвращение кода ошибки, запись кода ошибки в глобальную переменную и т.д.), но это очень плохой путь. Большой плюс исключений заключается в том, что вы можете проявлять гибкость при их обработке: разным образом обрабатывать исключения разных групп, вешать один обработчик исключений на очень большие группы исключений, обрабатывать их на любом уровне исполнения и т.д.\n",
    "\n",
    "Общий синтаксис для ловли и обработки исключений:\n",
    "\n",
    "```Python\n",
    "try:\n",
    "    блок_в_котором_может_возникнуть исключение\n",
    "except группа_исключений_1 as переменная_1:\n",
    "    обработка_исключений_1\n",
    "...\n",
    "except группа_исключений_N as переменная_N:\n",
    "    обработка_исключений_N\n",
    "else:\n",
    "    блок_который_выполняется_если_исключений_не_было\n",
    "finally:\n",
    "    блок_который_выполняется_в_любом_случае\n",
    "```\n",
    "\n",
    "Блоки else и finally являются необязательными, но хотя бы один except должен присутствовать. Переменные типа `переменная_N` позволяют доступиться к объекту исключения внутри обработчика.\n",
    "\n",
    "Блок finally **всегда** выполняется в конце. **Всегда** подразумевает, что даже если у вас есть return внутри одного из блоков-обработчиков, finally выполнится. Как правило, там размещается сохранение временных результатов и очистка ресурсов (например, закрытие соединения с БД).\n",
    "\n",
    "Все блоки except применяются по очереди. В случае возникновения исключения, сначала первый блок проверяет, подходит ли ему это исключение. \"Подходит\" означает, что в данном выражении except либо указан класс этого исключения, либо класс-предок класса этого исключения. Если исключение не подходит, то идет проверка по следующему блоку. Таким образом, следующая конструкция не имеет смысла:\n",
    "\n",
    "```Python\n",
    "try:\n",
    "    какой-то_код\n",
    "except Класс-предок as e1:\n",
    "    обработка_класса-предка\n",
    "except Класс-потомок as e2:\n",
    "    обработка_класса-потомка\n",
    "```\n",
    "\n",
    "Всегда будет выполняться обработка класса-предка\n",
    "\n",
    "Для возбуждения исключения используется инструкция raise:\n",
    "\n",
    "```Python\n",
    "raise исключение(аргументы)\n",
    "raise исключение(аргументы) from исходное_исключение\n",
    "raise\n",
    "```\n",
    "\n",
    "Первая форма возбуждает исключение заданного типа (встроенного или определенного пользователем), который должен быть потомком класса Exception. Вторая и третья форма используются только внутри обработчиков исключений. Вторая форма позволяет возбуждать цепочку исключений, а третья - просто возбуждает то же исключение, которое обрабатывается на данный момент.\n",
    "\n",
    "Для определения собственных классов исключений, необходимо просто выбрать класс исключения, от который мы будем наследовать и написать инструкцию:\n",
    "\n",
    "```Python\n",
    "class новое_исключение(исключение_предок): pass\n",
    "```\n",
    "\n",
    "По сути своей это объявление класса (ООП — тема лекции номер 5), у которого нет тела (инструкция pass). После этого объявления вы сможете ловить данные исключения в обработчиках исключений."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 3. Функции, определяемые пользователем\n",
    "\n",
    "В Python есть четыре вида функций: глобальные, локальные, лямбда-функции и методы.\n",
    "\n",
    "Все функции, которые вы пишете в своем модуле, определяя их через инструкцию def — глобальные. Локальные функции пишутся таким же образом, но определяются внутри других функций (как правило, это утилитарные функции, которые не имеют смысла за пределами области видимости, в которой они определены). Лямбда-функции (анонимные функции) — функции, которые определяются без привязки к идентификатору. Как привило, лямбда-функции используются в местах, где необходимо передать объект-функцию, но вы не видите необходимости определять отдельную функцию в коде. Методы — функции-члены классов (ООП — тема следующей лекции).\n",
    "\n",
    "Мы уже видели стандартное определение функции:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Bob!\n"
     ]
    }
   ],
   "source": [
    "def greetings(name, greeting):\n",
    "    print(\"{0}, {1}!\".format(greeting, name))\n",
    "    \n",
    "greetings(\"Bob\", \"Hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Если мы хотим задать значение по умолчанию какому-либо из аргументов функции, после его имени необходимо поставить = и желаемое значение:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, John!\n"
     ]
    }
   ],
   "source": [
    "def greetings(name, greeting=\"Hello\"):\n",
    "    print(\"{0}, {1}!\".format(greeting, name))\n",
    "    \n",
    "greetings(\"John\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Нужно учитывать, что параметры со значениями по умолчанию **всегда** должны идти после параметров без значений по умолчанию. При вызове функции, можно явно указать параметры, к которым мы обращаемся, тогда их порядок не важен (только необходимо, чтобы были указаны все параметры без значений по умолчанию):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Oh hi, Mark!\n"
     ]
    }
   ],
   "source": [
    "greetings(greeting=\"Oh hi\", name=\"Mark\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "При этом данный подход можно комбинировать с классическим: сначала указываем позиционные аргументы, а затем именованные:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "What`s up, Dawg!\n"
     ]
    }
   ],
   "source": [
    "greetings(\"Dawg\", greeting=\"What`s up\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Все параметры, для которых значение по умолчанию не задано, являются обязательными, остальные — необязательными. Необходимо помнить, что если вы создаете новый объект при указании значения по умолчанию, то этот объект будет создан в момент **объявления** функции, а не в момент вызова. Классическая ошибка — задать значение по умолчанию как пустой список. Тогда этот список будет общий на все вызовы функции, т.е. если вы в него что-то запишете в первом вызове, то во втором он уже не будет пустым.\n",
    "\n",
    "Имена функций и их параметров согласно PEP 8 рекомендуется называть в camel_case. Следует избегать необщепринятых сокращений в именах функций и параметрах. Имена параметров должны отражать суть того, что в них хранится, а имена функци — суть того, что они делают.\n",
    "\n",
    "Можно задокументировать любую функцию при помощи *docstring* — строки, которая идет после строки с инструкцией def, до любой из инструкций внутри функции:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Prints greeting'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def greetings(name, greeting=\"Hello\"):\n",
    "    \"Prints greeting\"\n",
    "    print(\"{0}, {1}!\".format(greeting, name))\n",
    "    \n",
    "greetings.__doc__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Естественно, этот строковый литерал может содержать несколько строк"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Prints greeting\n"
     ]
    }
   ],
   "source": [
    "def greetings(name, greeting=\"Hello\"):\n",
    "    \"\"\"Prints \\\n",
    "greeting\"\"\"\n",
    "    print(\"{0}, {1}!\".format(greeting, name))\n",
    "    \n",
    "print(greetings.__doc__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "С операторами распаковки мы уже знакомы: \\* позволяет распаковывать коллекции, а \\*\\* — словари, например:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, World!\n",
      "Hi, Jim!\n"
     ]
    }
   ],
   "source": [
    "lst = [\"World\", \"Hello\"]\n",
    "dct = {\"greeting\" : \"Hi\", \"name\" : \"Jim\"}\n",
    "\n",
    "greetings(*lst)\n",
    "greetings(**dct)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Анонимные функции\n",
    "\n",
    "Синтаксис для описания лямбда функций выглядит следующим образом:\n",
    "\n",
    "```Python\n",
    "lambda аргументы: выражение\n",
    "```\n",
    "\n",
    "Аргументы необязательны, внутри выражения не может содержаться ветвление или циклы, а также инструкции return и yield. При этом можно использовать условные выражения. Результат выражения и является возвращаемым значением лямбда функции. Результатам лямбда-выражения является объект функции:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n",
      "Нечетное\n",
      "Четное\n"
     ]
    }
   ],
   "source": [
    "f = lambda x: \"Нечетное\" if x & 1 else \"Четное\"\n",
    "\n",
    "for i in range(1, 11):\n",
    "    print(f(i))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Утверждения\n",
    "\n",
    "Утверждения (assertions) позволяют проверять, удовлетворяют ли некоторые значения определенным условиям. Синтаксис утверждений следующий:\n",
    "\n",
    "```Python\n",
    "assert выражение, необязательное_выражение\n",
    "```\n",
    "\n",
    "Если выражение имеет булевое значение True, то программа выполняется в обычном режиме. В противном случае возбуждается исключение AssertionError. Если указано необязательное\\_выражение, то оно передается в качестве аргумента AssertionError (полезно для обозначение того, что именно пошло не так). Данные выражения часто используются в тестировании, но, в принципе, можно таким образом проверять корректность, скажем, переданных функции аргументов. Я рекомендуюю в последнем случае все-таки использовать инструкцию throw с более подходящими классами исключений.\n",
    "\n",
    "Выражения assert можно игнорировать, передав флаг -0 интерпретатору при запуске программы. Также, можно установить переменную окружения PYTHONOPTIMYZE в 0. При передаче флага -00 интерпретатору будут игнорироваться и инструкции assert, и строки документации (docstrings)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Доступ к переменным в глобальной области видимости\n",
    "\n",
    "Иногда не удается избежать наличия глобальных переменных, тогда необходимо явно указать область видимости:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, James!\n"
     ]
    }
   ],
   "source": [
    "greeting = \"Hello\"\n",
    "\n",
    "def greetings(name):\n",
    "    global greeting\n",
    "    print(\"{0}, {1}!\".format(greeting, name))\n",
    "    \n",
    "greetings(\"James\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также есть возможность указать область видимости nonlocal, тогда будет выбрано значение из ближайшей области видимости, которая находится выше текущей:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Helen\n",
      "Hello,  Natalia\n",
      "Helen\n",
      "Hello,  Maria\n",
      "Natalia\n"
     ]
    }
   ],
   "source": [
    "name = \"Helen\"\n",
    "\n",
    "def greet1():\n",
    "    name = \"Maria\"\n",
    "    def set_name(new_name):\n",
    "        nonlocal name\n",
    "        name = new_name\n",
    "    set_name(\"Natalia\")\n",
    "    print(\"Hello, \", name)\n",
    "    \n",
    "def greet2():\n",
    "    name = \"Maria\"\n",
    "    def set_name(new_name):\n",
    "        global name\n",
    "        name = new_name\n",
    "    set_name(\"Natalia\")\n",
    "    print(\"Hello, \", name)\n",
    "    \n",
    "\n",
    "print(name)\n",
    "greet1()\n",
    "print(name)\n",
    "greet2()\n",
    "print(name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как видно, в первом случае доступ был к переменной name области видимости функции greet1, а во втором — к переменной name в глобальной области видимости."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 4. Модули\n",
    "\n",
    "Говоря простым языком, модуль в Python — любой .py файл. Пакет модулей — директория, которая содержит пустой файл \\_\\_init\\_\\_.py и некоторое количество .py файлов.\n",
    "\n",
    "Импортирование имен из модулей происходит посредством инструкции import. Если после инструкции указать имя модуля, то все имена из этого модуля будут доступны через имя\\_модуля.идентификатор. Модули можно импортировать, присваивая им новый идентификатор в текущей области имен. Также можно импортировать конкретные идентификаторы из модуля. Сущностям из модуля также можно назначать свои идентификаторы. Чтобы импортировать все идентификаторы из модуля в текущее пространство имен, необходимо указать звездочку (астериск, \\*). Вот примеры всех вариантов:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import math as m\n",
    "from sys import path\n",
    "from datetime import date, time\n",
    "from cmath import sin as s\n",
    "from requests import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Все модули, которые находятся в пакете являются объектами этого пакета. Т.е. если у вас есть директория Server, внутри которой находится файл \\_\\_init\\_\\_.py и файл, скажем, Router.py, то последний может быть импортирован следующим образом:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import Server.Router as rt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Или:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from Server import Router as rt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Предположим, что вы хотите из модуля Router пакета Server обратиться к модулю Templates того же пакета. Тогда пакет будет доступен по имени . (текущая директория):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "form . import Templates as tmpl"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 5. Решение домашнего задания\n",
    "\n",
    "Рассмотрим решение [задачи](https://lambda-it.ru/post/27).\n",
    "\n",
    "Наиболее эффективное и наглядное ее решение — сведение ее к [задаче поиска наибольшей общей подстроки](https://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D0%B8%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%B0%D1%8F_%D0%BE%D0%B1%D1%89%D0%B0%D1%8F_%D0%BF%D0%BE%D0%B4%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B0).\n",
    "\n",
    "Для написания нашего решения обратимся к одному из классов разреженных матриц, которые предложены в модуле scipy.sparse. Поскольку наша матрица будет в основном содержать значение 0, то имеет смысл выбрать класс dok_matrix:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy.sparse import dok_matrix as M"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь напишем нашу основную функцию, которая принимает две последовательности, запоминает, какая из них больше (поскольку в конце мы будем использовать операцию среза, сложность которой возрастает линейно от числа элементов последовательности), создает матрицу вида, требуемого задачей, попутно запоминая местонахождение и значение максимума в ней, а затем возвращает срез от одной из исходных последовательностей:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def longest_common_subsequence(a, b):\n",
    "    n = len(a)\n",
    "    m = len(b)\n",
    "    x = 1\n",
    "    y = 1\n",
    "    maximum = 0\n",
    "    row_based = n >= m\n",
    "    \n",
    "    matrix = M((n + 1, m + 1))\n",
    "    \n",
    "    for i in range(1, n + 1):\n",
    "        for j in range(1, m + 1):\n",
    "            if a[i-1] == b[j-1]:\n",
    "                matrix[i, j] = matrix[i-1, j-1] + 1\n",
    "                tmp = matrix[i, j]\n",
    "                if tmp > maximum:\n",
    "                    maximum = tmp\n",
    "                    x = i\n",
    "                    y = j\n",
    "    maximum = int(maximum)\n",
    "    return a[x - maximum:x] if row_based else b[y - maximum: y]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "print(longest_common_subsequence([1, 2, 3, 4, 5, 6, 0], [1, 4, 5, 6, 7, 8]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 6. Домашнее задание\n",
    "\n",
    "Прочитать главы 4 и 5 Саммерфилда, выполнить упражнения после них\n",
    "\n",
    "Самостоятельно изучить раздел 5 главы, посвященный стандартной библиотеке Python.\n",
    "\n",
    "Переписать игру \"быки и коровы\", разделив ее на 3 модуля: основной, в котором описан интерфейс командной строки и логика игры, модуль, в котором содержатся функции связанные с вариантом, когда компьютер угадывает, и модуль, в котором содержатся функции, связанные с вариантом, когда угадывает игрок.\n",
    "\n",
    "Решить задачу, которая будет опубликована в субботу."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}