В нашем курсе предусмотрены задачи на дом. Их автоматически генерирует и проверяет Цифровой Ассистент Преподавателя (ЦАП), расположенный на странице http://kispython.ru
Студентам необходимо зарегистрироваться на http://kispython.ru/register, следуя подсказкам интерфейса: авторизоваться через ЕСИА РТУ МИРЭА или зарегистрироваться с помощью почты (студенты отправляют сообщения на почту со своей почты
Требуется решить все домашние задачи в течение семестра. Рекомендуется не затягивать с решениями, поскольку к дате последнего семинара в семестре прием задач в ЦАП будет отключен, а система будет переведена в тренировочный режим перед зачетом.
Студентам необходимо зарегистрироваться на http://kispython.ru/register, следуя подсказкам интерфейса: авторизоваться через ЕСИА РТУ МИРЭА или зарегистрироваться с помощью почты (студенты отправляют сообщения на почту со своей почты
@edu.mirea.ru
, а робот ее время от времени проверяет и подтверждает учетные записи) и выбрать свою группу. Требуется решить все домашние задачи в течение семестра. Рекомендуется не затягивать с решениями, поскольку к дате последнего семинара в семестре прием задач в ЦАП будет отключен, а система будет переведена в тренировочный режим перед зачетом.
Цифровой ассистент преподавателя не только проверяет код на наборах тестов, но и анализирует тексты программ алгоритмом классификации на основе искусственной нейронной сети.
Для задач разных типов алгоритмом кластеризации были выявлены наиболее распространённые в прошлом году подходы к решению. На полученных данных была обучена искусственная нейронная сеть. В этом году решение задачи ЦАП одного и того же типа известными нейронной сети способами позволяет зарабатывать учебные достижения, изучая Python!
На картинке показан один из способов решения 2-й задачи ЦАП при помощи словаря
Для задач разных типов алгоритмом кластеризации были выявлены наиболее распространённые в прошлом году подходы к решению. На полученных данных была обучена искусственная нейронная сеть. В этом году решение задачи ЦАП одного и того же типа известными нейронной сети способами позволяет зарабатывать учебные достижения, изучая Python!
На картинке показан один из способов решения 2-й задачи ЦАП при помощи словаря
dict[bool, float]
, в котором ключом является True
или False
, а значением — вычисленное по одной из формул число. В следующих задачах ЦАП необходимо будет достигать бóльшего разнообразия в синтаксической структуре решений.Современные алгоритмы из области искусственного интеллекта и компьютерной графики описываются в научных статьях. Статьи, в свою очередь, часто используют математические обозначения. Эти символы универсальны — они понятны специалистам вне зависимости от языка программирования.
Первый блок из 6 задач ЦАП помогает увидеть связь между математическими формулами и кодом на Python. С умением переводить математические формулы в код вы сможете использовать передовые научные идеи в своих проектах.
Первый блок из 6 задач ЦАП помогает увидеть связь между математическими формулами и кодом на Python. С умением переводить математические формулы в код вы сможете использовать передовые научные идеи в своих проектах.
Задача №6 ЦАП имеет отношение к операциям над множествами. Давайте попрактикуемся работать с этим типом данных.
Предположим, что требуется найти множество всех подмножеств (такое множество еще называется power set) для заданного множества. Может ли эта задача возникнуть на практике? Вполне. Например, в машинном обучении есть специальный вариант классификации под названием Label Powerset.
Вот простая реализация алгоритма нахождения power set:
На каждой итерации мы добавляем к текущему
А вот еще одна реализация, с использованием библиотеки
Здесь
Разумеется, существуют и другие, более быстродействующие алгоритмы получения power set. Попробуйте самостоятельно придумать, реализовать и оценить один из таких алгоритмов!
Предположим, что требуется найти множество всех подмножеств (такое множество еще называется power set) для заданного множества. Может ли эта задача возникнуть на практике? Вполне. Например, в машинном обучении есть специальный вариант классификации под названием Label Powerset.
Вот простая реализация алгоритма нахождения power set:
def get_powerset1(sequence):
powerset = {frozenset()}
for elem in sequence:
powerset |= {subset | {elem} for subset in powerset}
return powerset
На каждой итерации мы добавляем к текущему
powerset
его варианты с очередным элементом elem
. Попробуйте разобраться, зачем понадобилось вводить frozenset
.А вот еще одна реализация, с использованием библиотеки
itertools
:from itertools import combinations
def get_powerset2(sequence):
return {frozenset(comb)
for size in range(len(sequence) + 1)
for comb in combinations(sequence, size)}
Здесь
combinations
выдает все комбинации заданной длины из элементов последовательности.Разумеется, существуют и другие, более быстродействующие алгоритмы получения power set. Попробуйте самостоятельно придумать, реализовать и оценить один из таких алгоритмов!
Продолжаем публикацию вспомогательных материалов для задач на перевод математической нотации формы записи множества в код. Приведённые примеры кода не являются единственно возможным способом реализации указанных формул, при решении задач полезно обращаться к стандарту PEP 289, модулю functools, лекциям курса.
Успешное выполнение творческих проектов позволяет не только решить 0 задач на зачёте, но и опубликовать статью или получить опыт участия в проекте с открытым кодом.
📖 Павел Саломатин написал статью «JIT-компилятор Python в 300 строк». Его статья получила больше голосов на Habr, чем статьи многих профессиональных разработчиков.
📚 Научная статья Кирилла Павлова «Библиотека llvm2py для анализа промежуточного представления LLVM и её применение в оценке степени распараллеливания линейных участков кода» опубликована в журнале, входящем в Перечень ВАК. Кроме того, полученные результаты были представлены на международной научной конференции.
А вот несколько примеров выполненных доработок ЦАП.
💻 Сергей Дмитриев по собственной инициативе подключил к ЦАП удобный редактор кода, а также добавил в ЦАП поддержку входа через систему аутентификации РТУ МИРЭА.
🔍 Владислав Кнышов добавил возможность просмотра списка отправленных решений задач ЦАП, а Олег Цветков добавил поддержку просмотра решений, отправленных в ЦАП в режиме зачёта.
📊 Дмитрий Луковников разработал рейтинг студентов по числу открытых способов решения.
🎮 Олег Токарев реализовал панель управления зачётом ЦАП, а Арсений Коротков улучшил постраничную навигацию по спискам решений в кабинетах студента и преподавателя.
📈 Софья Быкова нарисовала логотип ЦАП, а Ярослав Сизиков и Михаил Гальцев улучшили покрытие кода тестами, добавив автоматические тесты для преподавательской и страницы входа.
Благодаря усилиям этих разработчиков сегодняшние студенты нашего курса могут пользоваться ЦАП с большим удобством, чем прежде.
📖 Павел Саломатин написал статью «JIT-компилятор Python в 300 строк». Его статья получила больше голосов на Habr, чем статьи многих профессиональных разработчиков.
📚 Научная статья Кирилла Павлова «Библиотека llvm2py для анализа промежуточного представления LLVM и её применение в оценке степени распараллеливания линейных участков кода» опубликована в журнале, входящем в Перечень ВАК. Кроме того, полученные результаты были представлены на международной научной конференции.
А вот несколько примеров выполненных доработок ЦАП.
💻 Сергей Дмитриев по собственной инициативе подключил к ЦАП удобный редактор кода, а также добавил в ЦАП поддержку входа через систему аутентификации РТУ МИРЭА.
🔍 Владислав Кнышов добавил возможность просмотра списка отправленных решений задач ЦАП, а Олег Цветков добавил поддержку просмотра решений, отправленных в ЦАП в режиме зачёта.
📊 Дмитрий Луковников разработал рейтинг студентов по числу открытых способов решения.
🎮 Олег Токарев реализовал панель управления зачётом ЦАП, а Арсений Коротков улучшил постраничную навигацию по спискам решений в кабинетах студента и преподавателя.
📈 Софья Быкова нарисовала логотип ЦАП, а Ярослав Сизиков и Михаил Гальцев улучшили покрытие кода тестами, добавив автоматические тесты для преподавательской и страницы входа.
Благодаря усилиям этих разработчиков сегодняшние студенты нашего курса могут пользоваться ЦАП с большим удобством, чем прежде.
📚 В ЦАП есть автоматическая проверка кода на соответствие стандарту PEP8 и на когнитивную сложность. Остановимся подробнее на последней.
Когнитивную сложность не следует путать со сложностью вычислительной, ведь речь идет о сложности восприятия кода читателем. Чем проще код, тем лучше, тем меньше возможностей для ошибок спрятаться в нем.
Как мы измеряем читаемость кода? В основе лежит несколько простых правил.
1. Штраф +1 за каждую конструкцию, которая содержит ветвления (controlflowbreak).
2. Добавляем +1 к штрафу за каждый уровень вложенности конструкций, которые содержат ветвления (nesting).
3. Штраф +1 за переиспользование имени переменной в новом контексте (redefine).
В реальности когнитивная сложность в ЦАП оценивается несколько сложнее. Подробности можно узнать в исходном коде, а также в статье.
Когнитивную сложность не следует путать со сложностью вычислительной, ведь речь идет о сложности восприятия кода читателем. Чем проще код, тем лучше, тем меньше возможностей для ошибок спрятаться в нем.
Как мы измеряем читаемость кода? В основе лежит несколько простых правил.
1. Штраф +1 за каждую конструкцию, которая содержит ветвления (controlflowbreak).
2. Добавляем +1 к штрафу за каждый уровень вложенности конструкций, которые содержат ветвления (nesting).
3. Штраф +1 за переиспользование имени переменной в новом контексте (redefine).
В реальности когнитивная сложность в ЦАП оценивается несколько сложнее. Подробности можно узнать в исходном коде, а также в статье.
🎉 В честь Дня рождения Института информационных технологий 25 апреля на платформе ЦАП будут проводиться соревнования по программированию на языке Python. Для участия в мероприятии необходимо зарегистрироваться, доступно 200 мест:
https://t.me/vika_iit/16/444
🥇 20 победителей смогут посетить экскурсии в офисы «Яндекс» или «ВКонтакте», которые проведут выпускники Института ИТ!
🏅 Кроме того, студенты нашего курса, успешно справившиеся со всеми задачами на соревновании, получат дополнительные баллы активности на семинарах. А каждый из 20 победителей получит до +10 баллов активности.
https://t.me/vika_iit/16/444
🥇 20 победителей смогут посетить экскурсии в офисы «Яндекс» или «ВКонтакте», которые проведут выпускники Института ИТ!
🏅 Кроме того, студенты нашего курса, успешно справившиеся со всеми задачами на соревновании, получат дополнительные баллы активности на семинарах. А каждый из 20 победителей получит до +10 баллов активности.
ЦАП анализирует тексты программ, автоматически определяя способы решения задач. На практике, как правило, предпочтение отдаётся простым и коротким способам, использующим функции из стандартной библиотеки.
Например, для транспонирования матрицы, заданной в построчной форме, с помощью кортежей, можно воспользоваться функцией
Функция
Начиная с версии Python 3.6 для удаления дубликатов строк из таблицы можно воспользоваться словарём:
Порядок ключей в словаре соответствует порядку их вставки, а при добавлении дубликата ключа обновляется только соответствующее ему значение. Метод
Для сортировки строк таблицы можно воспользоваться функцией
В качестве ключа в
Например, для транспонирования матрицы, заданной в построчной форме, с помощью кортежей, можно воспользоваться функцией
zip
:table = tuple(zip(*table))
Функция
zip
объединяет i-е элементы поданных на вход последовательностей, возвращая генератор. Функция tuple
преобразует генератор в кортеж.Начиная с версии Python 3.6 для удаления дубликатов строк из таблицы можно воспользоваться словарём:
table = tuple(dict.fromkeys(table))
Порядок ключей в словаре соответствует порядку их вставки, а при добавлении дубликата ключа обновляется только соответствующее ему значение. Метод
fromkeys
создаёт словарь с ключами из поданной на вход последовательности и со значениями None
.Для сортировки строк таблицы можно воспользоваться функцией
sorted
:table = sorted(table, key=
lambda row: row[0])
В качестве ключа в
sorted
указана анонимная функция, возвращающая значение, по которому следует отсортировать элементы последовательности.This media is not supported in your browser
VIEW IN TELEGRAM
📚 В заключительной 11-й задаче ЦАП предлагается реализовать конечный автомат Мили или Мура в виде класса.
В автомате Мура реакция на входное слово (на вызов метода объекта) зависит только от текущего состояния автомата (значения поля объекта). В автомате Мили реакция на входное слово зависит и от текущего состояния автомата, и от входного слова.
🔄 Для автомата Мура в метках вершин графа указывается "состояние / выходной сигнал". Для автомата Мили в метках дуг указывается "входной сигнал / выходной сигнал".
После реализации конечного автомата в виде класса необходимо написать для него тесты в функции
📊 Для выявления непокрытых тестами строк в программе полезно генерировать отчёты о покрытии кода в формате HTML при помощи
Для запуска тестов в
✅ В результате будет создана директория
Подробности об инструментах
В автомате Мура реакция на входное слово (на вызов метода объекта) зависит только от текущего состояния автомата (значения поля объекта). В автомате Мили реакция на входное слово зависит и от текущего состояния автомата, и от входного слова.
🔄 Для автомата Мура в метках вершин графа указывается "состояние / выходной сигнал". Для автомата Мили в метках дуг указывается "входной сигнал / выходной сигнал".
После реализации конечного автомата в виде класса необходимо написать для него тесты в функции
test
, при этом требуемая степень покрытия кода тестами (branch coverage) — 100%.📊 Для выявления непокрытых тестами строк в программе полезно генерировать отчёты о покрытии кода в формате HTML при помощи
coverage
и pytest
:pip install coverage pytest
Для запуска тестов в
program.py
и генерации отчёта о покрытии кода тестами достаточно выполнить:coverage run --branch -m pytest program.py
coverage report
coverage html
✅ В результате будет создана директория
htmlcov
с файлом index.html
, содержащим отчёт о покрытии кода тестами, который можно открыть в веб-браузере (см. видеозапись).Подробности об инструментах
coverage
и pytest
можно найти в конспекте 5-й лекции нашего курса.Вопросы и ответы по 11-й задаче
🤔: Что вообще от нас хотят?
🎓: Реализовать конечный автомат по графу: состояния — вершины, переходы — дуги. Подробнее см., например, в книге Автоматное программирование.
🤔: Что за автомат Мура или Мили?
🎓: Мура: выход по состоянию (
🤔: Как узнать начальное состояние?
🎓: Начальное состояние указано на графе стрелкой без начала.
🤔: Эта стрелка является настоящей входной дугой?
🎓: Нет, не является.
🤔: Начальное состояние является посещенным?
🎓: Да, автомат стартует в начальном состоянии, считая его посещенным.
🤔: А если из состояния есть несколько одинаковых переходов?
🎓: Используется переменная, заданная, например, методом
🤔: Каким должно быть значение этих переменных по умолчанию?
🎓: Выберите любое разумное значение (например, 0).
🤔: Как перехватить обращение к несуществующему методу?
🎓: Придется переопределить
🤔: Как тестировать исключения?
🎓: Перехватывайте их в тестах или напишите аналог pytest.raises.
🤔: Тестировать надо только класс или весь модуль?
🎓: Весь модуль, включая main, см. подробнее здесь.
🤔: Что вообще от нас хотят?
🎓: Реализовать конечный автомат по графу: состояния — вершины, переходы — дуги. Подробнее см., например, в книге Автоматное программирование.
🤔: Что за автомат Мура или Мили?
🎓: Мура: выход по состоянию (
get_output
). Мили: выход по переходу (возвращается методом перехода).🤔: Как узнать начальное состояние?
🎓: Начальное состояние указано на графе стрелкой без начала.
🤔: Эта стрелка является настоящей входной дугой?
🎓: Нет, не является.
🤔: Начальное состояние является посещенным?
🎓: Да, автомат стартует в начальном состоянии, считая его посещенным.
🤔: А если из состояния есть несколько одинаковых переходов?
🎓: Используется переменная, заданная, например, методом
set_var(имя, значение)
, для выбора перехода.🤔: Каким должно быть значение этих переменных по умолчанию?
🎓: Выберите любое разумное значение (например, 0).
🤔: Как перехватить обращение к несуществующему методу?
🎓: Придется переопределить
__getattr__
.🤔: Как тестировать исключения?
🎓: Перехватывайте их в тестах или напишите аналог pytest.raises.
🤔: Тестировать надо только класс или весь модуль?
🎓: Весь модуль, включая main, см. подробнее здесь.
🚀 Среди тестировщиков 11-й задачи был ваш сверстник. Его зовут Кирилл, он является core-разработчиком проекта CPython — официальной реализации языка, который вы сейчас изучаете.
👨💻 Подробнее об опыте работы Кирилла в CPython можно почитать в его статье «Как я стал core-разработчиком Python в 19 лет».
👨💻 Подробнее об опыте работы Кирилла в CPython можно почитать в его статье «Как я стал core-разработчиком Python в 19 лет».
😱 До конца семестра осталось совсем немного времени!
❗️ Напоминаем, что решение всех 11 задач ЦАП обязательно для допуска на зачёт. Просим старост уведомить об этом одногруппников.
🎁 Тем временем, специально для подписчиков нашего канала мы подготовили цикл публикаций шпаргалок по решению задач ЦАП!
❗️ Напоминаем, что решение всех 11 задач ЦАП обязательно для допуска на зачёт. Просим старост уведомить об этом одногруппников.
🎁 Тем временем, специально для подписчиков нашего канала мы подготовили цикл публикаций шпаргалок по решению задач ЦАП!
Во 2-й задаче ЦАП предлагается реализовать кусочную функцию. В формуле справа указаны условия, при которых должны вычисляться выражения, указанные слева:
Другие способы решения включают решение при помощи
⚙️ Обратите внимание, что в коде отсутствует
from math import cos, log
def main(z):
if z < 46:
return 89 * z ** 3 - 57 * z ** 4
if 46 <= z < 82:
return cos(z) ** 2 - 1 - \
log(92 * z ** 3, 10) ** 6
if 82 <= z < 180:
return 49 * (32 * z ** 3) ** 7 + \
71 * z ** 4 + z ** 2
return z ** 3
Другие способы решения включают решение при помощи
match
или словаря. По способам решения 1-й задачи есть информация в СДО.⚙️ Обратите внимание, что в коде отсутствует
print
, а функция называется main
— так ЦАП сможет вашу функцию обнаружить и проверить.В 3-й задаче ЦАП предлагается реализовать итерационную функцию, о математических обозначениях которых мы писали ранее.
3 следующих друг за другом символа Σ в формуле соответствуют 3 вложенным циклам, вложенные циклы в Python легко описать при помощи отступов:
Вычислительная сложность функции
Другой способ решения этой задачи — воспользоваться выражениями-генераторами из PEP 289:
Вычислительная сложность функции
3 следующих друг за другом символа Σ в формуле соответствуют 3 вложенным циклам, вложенные циклы в Python легко описать при помощи отступов:
from math import sin
def main(b, n, a):
total = 0
for c in range(1, a + 1):
for i in range(1, n + 1):
for j in range(1, b + 1):
x = sin(c ** 2)
y = i ** 3
z = 33 * j ** 5
total += x + y + z
return total
Вычислительная сложность функции
main
равна O(a*n*b)
— тело последнего цикла выполнится a*n*b
раз. Другой способ решения этой задачи — воспользоваться выражениями-генераторами из PEP 289:
from math import sin
def main(b, n, a):
return sum(
sin(c**2) + i**3 + 33*j**5
for c in range(1, a + 1)
for i in range(1, n + 1)
for j in range(1, b + 1))
Вычислительная сложность функции
main
от этого не изменится, но код сократится.В 4-й задаче ЦАП предлагается реализовать функцию по рекуррентной формуле. Простейший способ решения задачи — реализовать формулу в виде рекурсивной функции:
Временная сложность этой функции равна
От рекурсивных вызовов легко избавиться, запоминая вычисленные значения функции в списке:
Легко заметить, что запоминать все вычисленные значения в списке совсем не обязательно, достаточно запомнить последнее вычисленное значение:
Вычислительная сложность функции по-прежнему равна
from math import sin
def main(n):
if n == 0:
return 0.37
p = main(n - 1)
return p**3 - p**2 - sin(p)
Временная сложность этой функции равна
O(n)
. Пространственная сложность также равна O(n)
, так как при каждом рекурсивном вызове main
на стек вызовов добавляется новый кадр.От рекурсивных вызовов легко избавиться, запоминая вычисленные значения функции в списке:
from math import sin
def main(n):
vals = [0.37]
for _ in range(n):
p = vals[-1]
vals.append(p**3-p**2-sin(p))
return vals[-1]
Легко заметить, что запоминать все вычисленные значения в списке совсем не обязательно, достаточно запомнить последнее вычисленное значение:
from math import sin
def main(n):
p = 0.37
for _ in range(n):
p = p**3 - p**2 - sin(p)
return p
Вычислительная сложность функции по-прежнему равна
O(n)
, а пространственная сложность функции снизилась и теперь равна O(1)
.Работе с векторами посвящена 5-я задача ЦАП. В ней предложено реализовать функцию, обрабатывающую упорядоченные наборы чисел.
Основная сложность этой задачи в том, что компоненты вектора принято нумеровать начиная с единицы, а нумерация индексов элементов списка в Python начинается с 0.
Привести списки в соответствие с векторами можно, добавив в начало каждого списка элемент-заглушку. Вот пример такого решения с помощью метода
С точки зрения программной инженерии такой код имеет существенный недостаток — реализованная функция незаметным для пользователя образом меняет входные данные. Это может привести к трудно уловимым ошибкам.
Вот исправленное решение:
Этот вариант решения отличается использованием дополнительной памяти для копий списков.
В окончательном варианте решения дополнительная память не используется и пользовательские данные не модифицируются. Вместо этого индексы аккуратно пересчитаны:
Другие способы решения задачи включают использование низкоуровневого цикла
Основная сложность этой задачи в том, что компоненты вектора принято нумеровать начиная с единицы, а нумерация индексов элементов списка в Python начинается с 0.
Привести списки в соответствие с векторами можно, добавив в начало каждого списка элемент-заглушку. Вот пример такого решения с помощью метода
insert
:import math
def main(z, x, y):
n = len(z)
z.insert(0, 0)
x.insert(0, 0)
y.insert(0, 0)
s = 0
for i in range(1, n + 1):
a = x[n + 1 - math.ceil(i / 4)]
b = 43 * z[n + 1 - i] ** 3
c = y[i] ** 2
s += math.tan(a + b + c)
return s
С точки зрения программной инженерии такой код имеет существенный недостаток — реализованная функция незаметным для пользователя образом меняет входные данные. Это может привести к трудно уловимым ошибкам.
Вот исправленное решение:
import math
def main(z, x, y):
n = len(z)
z = [0] + z
x = [0] + x
y = [0] + y
s = 0
for i in range(1, n + 1):
a = x[n + 1 - math.ceil(i / 4)]
b = 43 * z[n + 1 - i] ** 3
c = y[i] ** 2
s += math.tan(a + b + c)
return s
Этот вариант решения отличается использованием дополнительной памяти для копий списков.
В окончательном варианте решения дополнительная память не используется и пользовательские данные не модифицируются. Вместо этого индексы аккуратно пересчитаны:
import math
def main(z, x, y):
n = len(z)
s = 0
for i in range(0, n):
a = x[n - math.ceil((i + 1) / 4)]
b = 43 * z[n - 1 - i] ** 3
c = y[i] ** 2
s += math.tan(a + b + c)
return s
Другие способы решения задачи включают использование низкоуровневого цикла
while
, выражений-генераторов из PEP 289, списковых включений.6-я задача ЦАП посвящена работе с множествами. Перечень общепринятых математических обозначений, которые можно встретить в постановке этой задачи, мы уже публиковали ранее.
Для решения задачи полезно воспользоваться set comprehensions, синтаксис которых близок к математической форме записи множества.
Пригодятся также агрегирующие функции, такие как
Внимательный читатель заметит, что ЦАП отклонит код выше, поскольку одна из переменных называется O и программист может спутать такое имя с нулём 0 (правило E741). Исправить код легко — достаточно изменить имя переменной.
Эту задачу можно решить и без set comprehensions, заполняя множества элементами в обычном цикле. В этом случае код, вычисляющий множества, придётся разделить на отдельные функции, чтобы снизить когнитивную сложность функции
Для решения задачи полезно воспользоваться set comprehensions, синтаксис которых близок к математической форме записи множества.
Пригодятся также агрегирующие функции, такие как
len
, sum
, math.prod
, any
, all
:from math import floor
def main(Ω):
E = {floor(w / 7) - abs(w)
for w in Ω
if not 26 < w < 76}
O = {w ** 2 - 7 * w
for w in Ω
if not -8 <= w <= 52}
Λ = {floor(e / 9)
for e in E
if not -43 <= e < 48}
H = {3 * o
for o in O
if any(λ < o for λ in Λ)}
Δ = {7 * λ
for λ in Λ
if all(η >= λ for η in H)}
return len(H | Δ) - sum(Δ)
Внимательный читатель заметит, что ЦАП отклонит код выше, поскольку одна из переменных называется O и программист может спутать такое имя с нулём 0 (правило E741). Исправить код легко — достаточно изменить имя переменной.
Эту задачу можно решить и без set comprehensions, заполняя множества элементами в обычном цикле. В этом случае код, вычисляющий множества, придётся разделить на отдельные функции, чтобы снизить когнитивную сложность функции
main
.Сегодня разберём 7-ю задачу. В этой задаче предлагается реализовать дерево решений. При выборе пути по дереву от корня к листьям необходимо сравнивать значения в метках вершин с метками ребер.
Однако, при наивной реализации дерева решений в виде функции с большим числом условных операторов когнитивная сложность такой функции оказывается непозволительно высокой.
Для упрощения функции полезно преобразовать код, вычислять поддеревья в отдельных функциях:
При таком подходе функции легко переиспользовать, если поддерево встречается в дереве несколько раз (см.
В приведённом коде условный оператор может быть заменён оператором match. В других способах решения деревья описываются вложенными словарями, списками, иерархиями объектов.
Однако, при наивной реализации дерева решений в виде функции с большим числом условных операторов когнитивная сложность такой функции оказывается непозволительно высокой.
Для упрощения функции полезно преобразовать код, вычислять поддеревья в отдельных функциях:
def f2(x, left, middle, right):
if x[2] == 1998:
return left
if x[2] == 2018:
return middle
return right
def f1(x, left, right):
if x[1] == "BISON":
return left
return right
def f0(x):
if x[0] == 2006:
return 1
return 2
def f3(x):
if x[3] == 2001:
return f1(x, f0(x), f2(x, 3, 4, 5))
if x[3] == 1959:
return 6
return f2(x, 7, 8, f1(x, 9, 10))
def main(x):
if x[4] == 1981:
return f3(x)
if x[4] == 1987:
return 11
return 12
При таком подходе функции легко переиспользовать, если поддерево встречается в дереве несколько раз (см.
f1
и f2
). Преобразования кода такого рода называются рефакторингом.В приведённом коде условный оператор может быть заменён оператором match. В других способах решения деревья описываются вложенными словарями, списками, иерархиями объектов.