{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python\n",
    "\n",
    "## Типы данных-коллекции\n",
    "\n",
    "### Раздел 1. Типы данных-последовательности\n",
    "\n",
    "Тип данных, реализующий последовательность, это такой тип данных, для которого доступен оператор вхождения, функция получения длины (len()), получения среза и к нему можно доступаться как к итератору. Мы уже рассмотрели тип str, в будущем будут рассмотрены bytearray и bites, а на данный момент сосредоточимся на tuple и лист. Все эти типы являются встроенными.\n",
    "\n",
    "#### Кортежи\n",
    "\n",
    "Кортежем (tuple) называется упорядоченная последовательность из 0 или более ссылок на объекты. Кортежи поддерживают тот же оператор извлечения среза, что и строки. Кортежи являются неизменяемыми, так что если в ходе работы вам необходима изменяемая последовательность — обратитесь к списком, либо преобразуйте кортеж в список при помощи вызова list().\n",
    "\n",
    "Как и всегда, tuple может быть вызван как функция. Без передачи аргумента, будет возвращен пустой объект. При передачи в качестве аргумента объекта типа tuple, будет создана его поверхностная копия. Во всех остальных случаях будет совершена попытка привести аргумент к типу tuple. Данная функция принимает не более одного аргумента.\n",
    "\n",
    "Кортежи могут быть созданы заключением элементов в скобки (даже если элементов нет () — пустой кортеж) или перечислением элементов кортежа через запятую (только не при передачи таким образом кортежа в качестве аргумента функции.\n",
    "\n",
    "Для кортежей доступны всего два метода: count и index. t.count(x) возвращает число вхождений x в t, а t.index(x) возвращает индекс самого левого вхождения x в t, либо возбуждает ValueError, если x не входит в t. К тому же, для кортежей доступны операторы + (конкатенация), \\* (повторение), [] (взятие среза), in и not in (проверка на вхождение). Комбинированные версии присваивания += и \\*= могут использоваться, несмотря на то, что кортежи неизменяемы. Просто будет создан новый объект, который будет присвоен переменной (то же происходит и со строками). Для кортежей доступны операции сравнения: < <= == != >= >. Они выполняют сравнение поэлементно и рекурсивно.\n",
    "\n",
    "Кортежи позволяют выполнить один из самых известных трюков в Python:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 1\n"
     ]
    }
   ],
   "source": [
    "x = 1\n",
    "y = 2\n",
    "\n",
    "x, y = y, x\n",
    "    \n",
    "print(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также, если элементами итератора являются кортежи, то вы их сразу можете распаковывать:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Batman vs Joker\n",
      "Superman vs Lex Luthor\n",
      "Green Lantern vs Sinestro\n"
     ]
    }
   ],
   "source": [
    "for hero, villain in (('Batman', 'Joker'), ('Superman', 'Lex Luthor'), ('Green Lantern', 'Sinestro')):\n",
    "    print(hero, 'vs', villain)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Списки\n",
    "\n",
    "Список есть упорядоченная последовательность из нуля или более ссылок на объекты. Как и кортежи, для списков доступно взятие среза. Но в отличие от кортежей, списки изменяемы. Как следствие, мы можем изменять не только список целиком или его отдельные элементы, но и целые срезы в нем.\n",
    "\n",
    "Тип list может быть вызван как функция. Без аргументов list() вернет пустой список. Если в качестве аргумента был передан объект типа list, то будет создана его поверхностная копия. Если аргумент дан, но не является объектом класса list, то будет произведена попытка приведения его к типу list. Данная функция принимает не более одного аргумента.\n",
    "\n",
    "Списки могут быть созданы также перечислением элементов через запятую в квадратных скобках или при помощи генераторов списков.\n",
    "\n",
    "Для типа list доступны операции сравнения, работают аналогично случаю с кортежами.\n",
    "\n",
    "Списки могут быть вложены в друг друга, проитерированы, от них можно взять срез, — все то же, что и для кортежей. Можно использовать операторы in, not int, +, +=, *, *=, функцию len() и конструкцию del.\n",
    "\n",
    "Также есть оператор \\*, который может использоваться для получения остальной части списка в случае выбора нескольких элементов:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Batman ['Superman', 'Green Lantern', 'Flash']\n",
      "Batman ['Superman', 'Green Lantern'] Flash\n"
     ]
    }
   ],
   "source": [
    "heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash']\n",
    "\n",
    "batman, *rest = heroes\n",
    "print(batman, rest)\n",
    "\n",
    "batman, *mid, flash = heroes\n",
    "print(batman, mid, flash)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также есть оператор распаковки \\*, который используется для передачи элементов списка по отдельности в качестве аргументов функции:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Batman\n",
      "Superman\n",
      "Green Lantern\n",
      "Flash\n"
     ]
    }
   ],
   "source": [
    "heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash']\n",
    "\n",
    "def print4(a, b, c, d):\n",
    "    print(a)\n",
    "    print(b)\n",
    "    print(c)\n",
    "    print(d)\n",
    "    \n",
    "print4(*heroes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для списков доступны следующие методы:\n",
    "\n",
    "| Метод | Описание |\n",
    "| ----- | :------- |\n",
    "| L.append(x) | Добавляет элемент x в конец списка L |\n",
    "| L.count(x) | Подсчитывает число вхождений элемента x в список L |\n",
    "| L.extend(m) | Добавляет элементы m, к которому должен быть обеспечен доступ как к итератору, в конец списка L (то же, что и L += m |\n",
    "| L.index(x, start, end) | Возвращает индекс самого левого вхождения x в L. Если start и end заданы, то поиск происходит в срезе L[start:end]. Если элемент не найден, то возбуждается исключение ValueError |\n",
    "| L.insert(i, x) | Вставляет элемент x на позицию с индексом i в список L |\n",
    "| L.pop() | Удаляет последний элемент списка L и возвращает его |\n",
    "| L.pop(i) | Удаляет элемент списка L с индексом i и возвращает его |\n",
    "| L.remove(x) | Удаляет самое левое вхождение элемента x из списка L. Если элемент не найден, то возбуждается исключение ValueError |\n",
    "| L.reverse() | Разворачивает список |\n",
    "| L.sort(...) | Сортирует список. Аргумент key отвечает за функцию порядка, а reversed за способ сортировки (по невозрастанию / неубыванию) |\n",
    "\n",
    "#### Генераторы списков\n",
    "\n",
    "Для получения списка как результат какой-то операции над итератором используются генераторы списков. Есть две формымы генератора списков:\n",
    "\n",
    "[выражение for элемент in итератор]\n",
    "\n",
    "[выражение for элемент in итератор if условие]\n",
    "\n",
    "В самом простом случае выражение и есть элемент:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
      "[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]\n",
      "[0, 6, 12, 18]\n"
     ]
    }
   ],
   "source": [
    "print([x for x in range(10)])\n",
    "print([2*x for x in range(10)])\n",
    "print([2*x for x in range(10) if x % 3 == 0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Последняя форма в разы удобнее, чем запись:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 6, 12, 18]\n"
     ]
    }
   ],
   "source": [
    "lst = []\n",
    "for x in range(10):\n",
    "    if x % 3 == 0:\n",
    "        lst.append(2*x)\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 2. Типы-множества\n",
    "\n",
    "Типы данных-множества — типы, для которых доступны оператор вхождения in, функция len и доступ, как к итератору. В Python есть два типа множеств — set и frozenset. Тип frozenset является неизменяемым, поэтому для него недоступны методы для типа set, которые изменяют объект. В остальном они идентичны.\n",
    "\n",
    "Только хешируемые объекты могут быть добавлены в множество. Все встроенные неизменяемые типы являются хешируемыми.\n",
    "\n",
    "#### Множества\n",
    "\n",
    "Множество типа set есть неупорядоченная коллекция из нуля или более ссылок на хешируемые объекты. Множества изменяемы, так что в них можно добавлять и удалять элементы. Будучи неупорядоченными, понятие индекса или позиции для них не имеет смысла.\n",
    "\n",
    "Для создания множества используется либо вызов set(), который без аргументов создает пустое множество. С аргументом типа set создается поверхностная копия множества, а с другим аргументом выполняется попытка приведения к множеству. Данная функция не принимает более одного аргумента. Множество может быть также создано путем заключения элементов в фигурные скобки, но стоит учитывать, что пустое множество не может быть создано таким образом, так как {} создаст пустой словарь.\n",
    "\n",
    "Множество всегда хранит уникальные элементы:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "set('hello world') == {'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Множества поддерживают функцию len(), которое возвращает число элементов множества.\n",
    "\n",
    "Для типа set доступны следующие методы:\n",
    "\n",
    "| Метод | Описание |\n",
    "| ----- | :------- |\n",
    "| s.add(x) | Добавляет элемент x во множество s, если такого там нет |\n",
    "| s.clear() | Удаляет все элементы множества s |\n",
    "| s.copy() | Создает неглубокую копию множества s |\n",
    "| s.difference(t) | Возвращает новое множество, которое содержит все элементы множества s, которые не входят во множество t. Эквивалент s - t |\n",
    "| s.differenct_update(t) | Удаляет все элементы t из множества s. Эквивалент s -= t |\n",
    "| s.discard(x) | Удаляет элемент x из множества s |\n",
    "| s.intersection(t) | Возвращает новое множество, которое содержит элементы множества s, которые в то же время являются элементами множества t. Эквивалент s & t |\n",
    "| s.intersection_update(t) | Удаляет из множества s все элементы, которые не входят в t. Эквивалент s &= t |\n",
    "| s.isdisjoint(t) | Возвращает True, если у множеств s и t нет общих элементов |\n",
    "| s.issubset(t) | Возвращает True, если s есть подмножество (несобственное) t. Эквивалент s <= t |\n",
    "| s.issuperset(t) | Возвращает True, если t есть подмножество (несобственное) s. Эквивалент t <= s |\n",
    "| s.pop() | Удаляет случайный элемент s и возвращает его. Возбуждает исключение KeyError, если множесво пустое |\n",
    "| s.remove(x) | Удаляет элемент x из множества s. Возбуждает исключение KeyError, если x не входит в s |\n",
    "| s.symmetric_difference(t) | Возвращает новое множество, которое содержит элементы, которые либо содержатся в s и не содержатся в t, либо содержатся в t и не содержатся в s. Эквивалент s ^ t |\n",
    "| s.symmetric_difference_update(t) | Записывает в s симметрическую разность s и t. Эквивалент s ^= t |\n",
    "| s.union(t) | Возвращает новое множество, которое содержит все элементы множеств s и t |\n",
    "| s.update(t) | Добавляет все элементы множества t во множество s |\n",
    "\n",
    "Последние два метода эквивалентны s | t и s |= t соответственно.\n",
    "\n",
    "#### Генераторы множеств\n",
    "\n",
    "В целом то же, что и для списков, но сам генератор обозначается фигурными скобками:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n",
      "{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}\n",
      "{0, 18, 12, 6}\n"
     ]
    }
   ],
   "source": [
    "print({x for x in range(10)})\n",
    "print({2*x for x in range(10)})\n",
    "print({2*x for x in range(10) if x % 3 == 0})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 3. Типы-отображения\n",
    "\n",
    "Типы отображения — наборы пар \"ключ - значение\". Единственным встроенным типом-отображением является dictionary, который отображает хешируемые объекты на ссылки на другие объекты.\n",
    "\n",
    "#### Словари\n",
    "\n",
    "Тип dict (словарь) это упорядоченная коллекция пар \"ключ - значение\". Раньше dict был неупорядоченным типом, поэтому во избежания ошибок при исполнении кода более старыми интерпретаторами, советую вам придерживаться отношения к словорям как к неупорядоченным последовательностям.\n",
    "\n",
    "Словари поддерживают оператор вхождения in, функцию len(), и доступ как к итератору. Среди операций сравнения поддерживаются только == и !=.\n",
    "\n",
    "Как и обычно, тип dict может быть вызван как функция. Если ей не переданы аргументы, то создается пустой словарь. Если передан аргумент с типом-отображением, то создается словарь на основе этого аргумента. Например, если аргумент типа dict, то будет создана его поверхностная копия. Можно также передать аргумент-последовательность, каждый элемент в котором — последовательность из двух элементов: ключа и значения. Также пустой словарь можно создать при помощи конструкции {}, а словарь с элементами — перечислив пары ключ - значение в фигурных скобках через запятую, отделяя ключи от значений двоеточием:\n",
    "\n",
    "{ключ1 : значение1, ключ2 : значение2}\n",
    "\n",
    "К тому же доступны генераторы словарей.\n",
    "\n",
    "Мы можем доступаться к элементам словаря, используя ключ: d[ключ] вернет значение элемента с ключем *ключ*.\n",
    "\n",
    "Для добавления новых элементов используется оператор присваивания: d[новый\\_ключ] = новое\\_значение.\n",
    "\n",
    "Для типа dict доступны следующие методы:\n",
    "\n",
    "| Метод | Описание |\n",
    "| ----- | :------- |\n",
    "| d.clear() | Удаляет все элементы словаря d |\n",
    "| d.copy() | Возвращает поверхностную копию словаря d |\n",
    "| d.fromkeys(s, v) | Возвращает словарь, где ключами являются элементы последователности s, а значения равны v или None, если v не указано |\n",
    "| d.get(k) | Возвращает значение, которое соответствует ключу k или None, если такого нет |\n",
    "| d.get(k, v) | Возвращает значение, которое соответствует ключу k или v, если такого нет |\n",
    "| d.items() | Возвращает view (read-only итератор), содержащий пары (ключ, значение) словаря d |\n",
    "| d.keys() | Возвращает view, содержащий ключи словаря d |\n",
    "| d.pop(k) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возбуждается исключение KeyError |\n",
    "| d.pop(k, v) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возвращается значение v |\n",
    "| d.popitem() | Удаляет из словаря d и возвращает некоторую пару (ключ, значение). Возбуждает исключение KeyError, если d пуст |\n",
    "| d.setdefault(k, v) | ТТо же, что и d.get(k), но если такого элемента нет, то создается новый со значением None или v, если указано |\n",
    "| d.values() | Возвращает view всех значений словаря d |\n",
    "\n",
    "#### Генераторы словарей\n",
    "\n",
    "Генераторы словарей имеют две уже знакомые нам формы:\n",
    "\n",
    "[выражение\\_ключа : выражение\\_значения for ключ, значение in итератор]\n",
    "\n",
    "[выражение\\_ключа : выражение\\_значения for ключ, значение in итератор if условие]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 4. Итераторы. Функции и операци для работы с итераторами\n",
    "\n",
    "Тип данных называется итерируемым, если он может возвращать свои элементы по одному. Технически, любой объект, реализующий метод \\_\\_iter\\_\\_() является итерируемым и может предоставить итератор. Итератор — объект, реализуюзий метод \\_\\_next()\\_\\_, возвращающий следующее значение и возбуждающий исключение StopIteration, если элементов больше нет.\n",
    "\n",
    "Для итерируемых объектов доступны следующие функции и операторы:\n",
    "\n",
    "| Функция или оператор | Описание |\n",
    "| -------------------- | :------- |\n",
    "| s + t | Возвращает конкатенацию последовательностей s и t |\n",
    "| s * n | Возвращает последовательность, которая является результатом повторения последовательности s n раз |\n",
    "| x in t | Проверяет, есть ли в последовательности i элемент x |\n",
    "| all(i) | Возвращает True, если все элементы i при приведении к типу bool имеют значение True |\n",
    "| any(i) | Возвращает True, если один из элементов i при приведении к типу bool имеет значение True |\n",
    "| enumerate(i, start) | Возвращает последовательность элементов (индекс, значение) для итератора i, начиная с индекса 0 или start, если данный аргумент задан |\n",
    "| len(x) | Возвращает число элементов в x |\n",
    "| max(i, key) | Возвращает максимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key максимален |\n",
    "| min(i, key) | Возвращает минимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key минимален |\n",
    "| range(start, stop, step) | Возвращает итератор, содержащий целые числа от start до stop с шагом step. Аргумент start включен в последовательность, а stop — нет |\n",
    "| reversed(i) | Возвращает итератор, в котором элементы идут в обратном порядке |\n",
    "| sorted(i, key, reverse) | Возвращает отсортированный по неубыванию итератор. Если задан аргумент key, то сортировка идет по значением key(x) для каждого элемента x. Если reversre указан как True, то сортировка идет по невозрастанию |\n",
    "| sum(i, start) | Возвращает сумму элементов в итераторе i плюс start. Если start не задан, то принимается равным 0 |\n",
    "| zip(i1, ..., iN) | Возвращает итератор из кортежей, состоящий из элементов i1, ..., iN. Количество кортежей определяется минимальной длиной i1, ..., iN |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 5. Копирование коллекций\n",
    "\n",
    "При присваивании объектов будет выполнено присваивание по ссылке:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Alfred', 'Robin', 'Catwoman']\n"
     ]
    }
   ],
   "source": [
    "x = ['Nightwing', 'Robin', 'Catwoman']\n",
    "y = x\n",
    "x[0] = 'Alfred'\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для создания поверхностной копии можно использовать оператор среза:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['Nightwing', 'Robin', 'Catwoman']\n"
     ]
    }
   ],
   "source": [
    "x = ['Nightwing', 'Robin', 'Catwoman']\n",
    "y = x[:]\n",
    "x[0] = 'Alfred'\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Но поверхностная копия имеет ограничения, связанные с тем, что при ее создании создаются копии лишь самих элементов. Если они в свою очередь являются ссылками, то мы снова столкнемся с проблемой:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[['Lex Luther', 'General Zod'], ['Joker', 'Pinguin']]\n"
     ]
    }
   ],
   "source": [
    "dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]\n",
    "villains = dc_villains[:]\n",
    "\n",
    "dc_villains[1][1] = 'Pinguin'\n",
    "print(villains)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Чтобы избежать этого, необходимо использовать функцию deepcopy() модуля copy, которая создает глубокую копию объекта:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]\n"
     ]
    }
   ],
   "source": [
    "from copy import deepcopy\n",
    "\n",
    "dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]\n",
    "villains = deepcopy(dc_villains)\n",
    "\n",
    "dc_villains[1][1] = 'Pinguin'\n",
    "print(villains)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 6. Практика: vedis и matplotlib\n",
    "\n",
    "Есть достаточно интересный модуль vedis, который предоставляет доступ к key-value store типа Redis на диске, но при этом API этого доступа совпадает с доступом к словарю.\n",
    "\n",
    "Решим с использованием модуля vedis [задачу о гипотезе Колатца](https://lambda-it.ru/post/26) и сделаем визуализацию при помощи matplotlib.\n",
    "\n",
    "Для начала напишем функцию, которая из текущего числа получает следующее в последовательности:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def next_kolatz(x):\n",
    "    if x & 1:\n",
    "        return 3*x + 1\n",
    "    return x >> 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Если число x является нечетным (последний разряд в двоичной системе равен единице), то мы возвращаем 3x + 1. В противном случае, мы делим число на 2 (соответствует сдвигу вправо на один разряд в двоичной системе счисления). Теперь импортируем модуль vedis. При помощи него мы будем хранить длину цепочки для чисел, которые мы уже встречали. Также напишем функцию, которая вычисляет и возвращает длину цепочки для заданного числа x, записывает ее в хранилище, а также записывает в хранилище длины цепочек для всех промежуточных чисел:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_kolatz_length(x, storage):\n",
    "    cur = x\n",
    "    try:\n",
    "        return int(storage.get(cur))\n",
    "    except KeyError as e:\n",
    "        pass\n",
    "    stack = [cur]\n",
    "    while True:\n",
    "        cur = next_kolatz(cur)\n",
    "        try:\n",
    "            length = int(storage.get(cur))\n",
    "            break\n",
    "        except KeyError as e:\n",
    "            pass\n",
    "        stack.append(cur)\n",
    "    for item in stack[::-1]:\n",
    "        length += 1\n",
    "        storage.set(item, length)\n",
    "    return int(storage.get(x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь напишем функцию, которая возвращает число, меньшее заданного, для которого данная цепочка максимальна и длину цепочки:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_max_kolatz_length(n, storage):\n",
    "    num = max(range(1, n+1), key=lambda x: get_kolatz_length(x, storage))\n",
    "    return num, get_kolatz_length(num, storage)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь остается только инициализировать хранилище и испытать наши функции:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(77031, 351)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vedis import Vedis\n",
    "\n",
    "db = Vedis('storage.db')\n",
    "db.set(1, 1)\n",
    "\n",
    "print(get_max_kolatz_length(100000, db))\n",
    "db.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь построим обычный scatter plot при помощи matplotlib:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    " \n",
    "db = Vedis('storage.db')\n",
    "\n",
    "X = range(1, 101)\n",
    "Y = [get_kolatz_length(x, db) for x in X]\n",
    " \n",
    "db.close()\n",
    "\n",
    "plt.scatter(X, Y)\n",
    "plt.title('Визуализация длин цепочек Колатца')\n",
    "plt.xlabel('Число')\n",
    "plt.ylabel('Длина цепочки')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Раздел 7. Домашнее задание\n",
    "\n",
    "Решить задачи после третьей главы Саммерфилда\n",
    "\n",
    "Загуглить гипотезу Гольдбаха\n",
    "\n",
    "Загуглить динамическое программирование\n",
    "\n",
    "Написать функцию, которая принимает в качестве аргумента множество и функцию и при помощи matplotlib изображает график заданной функции на множестве значений. Добавьте несколько дополнительных аргументов: например, возможность сделать одну из осей логарифмической, выбрать разные цветовые схемы, разные типы графиков и т.д.\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
}