Решение наборов задач из курса CS50 “Введение в программирование на Python” – по одной: Набор задач 4


Читайте оригинальную статью в блоге здесь.


Пришло время для очередного набора задач, и на этот раз мы погружаемся в очень вкусную тему: библиотеки.

Когда я говорю “вкусная”, я имею в виду, что у нас есть множество качественных вариантов, когда речь идет о модулях и сторонних библиотеках в Python. Не говоря уже о сторонних библиотеках, сам Python поставляется с кучей встроенных модулей, которые довольно полезны для решения проблем, которые вы пытаетесь решить. С менеджером пакетов также, в наши дни есть библиотека почти для всего, и я имею в виду, для чего угодно.

Ну, может быть, вы не можете импортировать антигравитацию и летать – пока что – но вы, по крайней мере, можете найти много полезного в Python Package Index. (И, кстати, модуль antigravity – это пасхальное яйцо. Так что вы действительно можете импортировать его).

Прежде чем мы начнем, вы можете найти сообщения о предыдущих наборах задач здесь, в архиве. И я должен сделать обычный отказ от ответственности, я не предоставляю здесь полных решений, так что если вы прокручиваете страницу вниз, пытаясь найти блок кода, который заставит вас пройти тесты, вы тратите свое время, и это немного печально, мой друг. Это гораздо более сильный прилив дофамина, когда вы сами исследуете и находите решения. Я здесь для того, чтобы рассказать о наборах задач и быть проводником на этом пути.

Со всеми этими разговорами давайте посмотрим на проблемы, которые нам нужно решить на этой неделе.

Emojize

В каждом посте я предполагаю, что вы уже прочитали объяснения задач. Если да, то эта задача очень проста, поскольку вы уже знаете, как импортировать библиотеки и использовать их. И, объяснение намекает на библиотеку, которую вы собираетесь использовать, а именно emoji. В нем приведено несколько примеров того, как ее использовать. Так что в данном случае вам не нужно искать конкретное применение, оно буквально перед вами. Главное, о чем здесь не стоит забывать, – это указывать псевдонимы.

Письма Фрэнка, Яна и Глена

Как мы увидим в других задачах этой недели, четкая документация – очень важная вещь, когда речь идет об использовании сторонних библиотек. Без подсказок, которые дает объяснение проблемы, вам придется искать использование библиотеки pyfiglet, читая описание проекта. (Кстати, я думаю, что причина, по которой CS50 поместил ссылку на более старую версию пакета на PyPI, заключается в том, что описание проекта представлено там. Если вы нажмете на более новую версию, описание не будет дано, но вы можете найти его здесь, на GitHub).

Нам нужно преобразовать введенный текст в крутую версию ASCII-арта. Опять же, на странице объяснения дается много подсказок, но давайте посмотрим.

Прежде всего, нам нужно сделать что-то, только если у нас есть ноль или два аргумента командной строки. Поскольку имя программы также является аргументом, по умолчанию у нас 1 аргумент командной строки. Например:

$ python something.py # len(sys.argv) == 1
Войти в полноэкранный режим Выйти из полноэкранного режима
$ python something.py --someflag # len(sys.argv) == 2
Войти в полноэкранный режим Выйти из полноэкранного режима

В случае 1 аргумента – который является просто именем файла – нам нужно сделать случайный выбор, чтобы получить шрифт из всех предоставленных шрифтов. Получить все шрифты очень просто, поскольку нам дается подсказка:

f = Figlet()
fonts = f.getFonts() 
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь fonts – это список, из которого мы можем сделать случайный выбор. Что касается выбора, то я думаю, что уже дал достаточно подсказок. Если вы не поняли, помните, что всегда есть документация, в которой можно найти нужные сведения.

После того, как у нас есть случайный шрифт и входной текст, нам остается только создать Figlet, установить его шрифт и вывести текст (который вы можете просто print, чтобы увидеть его в терминале):

f = Figlet()
f.setFont(font=random_font)
print(f.renderText(text))
Войти в полноэкранный режим Выйти из полноэкранного режима

Это было сделано для случайного шрифта, если пользователь не предоставит шрифт сам. Если он предоставит, нам нужно выполнить эти шаги с заданным шрифтом вместо random_font здесь (который вы можете получить с помощью чего-то вроде sys.argv[2]). Но мы делаем это только если длина аргументов равна 3 (помните, что имя файла тоже считается), и sys.argv[1] на самом деле либо -f, либо --font; также при условии, что sys.argv[2] находится в fonts. Вы можете сделать это одной строкой условного кода, используя операторы and и or. Я не могу дать больше подсказок, не раскрыв ответ. После того, как все это сделано (если аргументы командной строки не укладываются в эти две условные ветви), мы просто выходим из программы с помощью sys.exit('Invalid argument').

Это было действительно весело. Теперь давайте посмотрим на следующий.

Adieu, Adieu

Не могу поверить, что я не слышал об этой библиотеке раньше. Знаете, именно такие вещи заставляют снова и снова влюбляться в Python. Спасибо CS50 за то, что познакомил меня с ней в этой задаче.

Проблемы такого рода – например, объединение всех имен и добавление , и для последнего элемента – можно решить многими способами, включая рекурсию. Но здесь, с Python, это всего лишь одна строка кода. Забудьте об антигравитации, это другой вид сверхспособностей.

Заимствуя идеи из задач прошлой недели, мы также должны продолжать получать ввод, пока пользователь не нажмет кнопку control-d. Это является следствием использования бесконечного цикла и обработки исключения EOFError. Вот, собственно, и все. Учитывая, что вы просмотрели описание проекта inflect, все необходимое для вас уже есть. Только не забудьте запустить движок программы следующим образом:

import inflect

p = inflect.engine()
Войти в полноэкранный режим Выйти из полноэкранного режима

И все, что вам нужно сделать, это соединить имена, которые вы собирали из вводимых данных. Все методы, которые вы можете использовать из библиотеки inflect, перед вами. Как всегда, научитесь любить документацию.

Игра в угадайку

Единственное, что нам нужно импортировать для решения этой задачи, это встроенный в Python модуль random. Честно говоря, я не думаю, что решение, которое я придумал, было элегантным. Элегантное или нет, но если подумать, нам нужно постоянно получать один входной сигнал для уровня, другой – для угадывания. Когда речь идет об уровне, мы должны спрашивать до тех пор, пока он не станет целым положительным числом; а когда речь идет об отгадке, мы должны спрашивать до тех пор, пока она не станет правильным числом. Решение можно реализовать разными способами, главное, что нам нужно получить случайное целое число от 1 до level – что легко решается с помощью модуля random в Python. Один из способов сделать это – использовать цикл и блок try...except, чтобы убедиться, что наши входные данные имеют правильное значение. После того как мы сгенерируем случайное целое число, мы можем использовать другой цикл, чтобы продолжать спрашивать пользователя об угаданном числе. Если угаданное число совпадает со сгенерированным, мы просто выводим Just right! и возвращаемся или выходим из цикла. Для случаев, когда угаданный результат меньше или больше числа, мы выдаем соответствующие сообщения Too small! или To large! и продолжаем спрашивать. Следует помнить, что сравнивать угаданное и случайно сгенерированное число нужно до тех пор, пока угаданное число является положительным или больше 0. И это все.
Кстати, игра в угадайку имеет глубокую связь с алгоритмом двоичного поиска, с помощью которого можно правильно угадать ответ за log(n) времени.

Маленький профессор

Я считаю, что с этого момента кривая становится более крутой, и наборы задач, которые мы будем видеть дальше, станут немного тяжелее, чем те, что мы видели до сих пор. Но давайте не будем зацикливаться на этом и посмотрим на эту задачу.
Нам нужно рассмотреть четыре спецификации. Для первой мы должны получить вход для уровня, только если он равен 1, 2 или 3. Скажем, у нас есть кортеж valid_inputs, мы можем проверить, есть ли входное значение в этой коллекции:

valid_inputs = (1, 2, 3)
level = int(input('Level: ')) # Let's say it is 4
print(level in valid_inputs) # False
Войти в полноэкранный режим Выйти из полноэкранного режима

И что нам с этим делать? После получения допустимого уровня нам нужно сгенерировать целое число с количеством цифр уровня. Например, так:

-> level = 1:
-> 0 <= integer <= 9

-> level = 2:
-> 10 <= integer <= 99

-> level = 3:
-> 100 <= integer <= 999
Войти в полноэкранный режим Выйти из полноэкранного режима

Если уровень равен 1, то генерируемое число должно быть от 0 до 9 включительно, если уровень равен 2, то число должно быть от 10 до 99, и, наконец, если уровень равен 3, то число должно быть от 100 до 999. Разумеется, все включено. Использовать условия – это заманчиво, но всегда есть другой подход. Давайте попробуем что-то другое.

Мы хотим, чтобы эти уровни соответствовали этим диапазонам. Одна из структур данных, которая приходит на ум для такого использования, – это словарь. Возможно, что-то вроде этого:

range_levels = {
    1: (0, 9),
    2: (10, 99),
    3: (100, 999)
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Все в порядке. Когда мы печатаем range_levels[1], он должен вывести (0, 9). Но что если мы хотим передать эти два значения, 0 и 9, отдельно внутри функции. Функции, которая поможет нам получить случайное целое число. Вместо того чтобы дать вам ответ, я расскажу об одной классной вещи, которую можно сделать с помощью Python, а именно об операторах распаковки. Идея заключается в том, что вы можете использовать оператор * для распаковки итерабельной таблицы, а ** – для распаковки пары ключ-значение. Например:

values = [0, 5, 2]
print(*values) # 0 5 2

# Prints 0, 2, 4 respectively
for i in range(*values):
    print(i)


houses = {
    'Gryffindor': 'courage',
    'Ravenclaw': 'intelligence',
    'Hufflepuff': 'loyalty',
    'Slytherin': 'ambition'
}

people = {
    'Harry Potter': 'Gryffindor',
    'Hermione Granger': 'Gryffindor',
    'Luna Lovegood': 'Ravenclaw'
}

print({**houses, **people}) # {'Gryffindor': 'courage', 'Ravenclaw': 'intelligence', 'Hufflepuff': 'loyalty', 'Slytherin': 'ambition', 'Harry Potter': 'Gryffindor', 'Hermione Granger': 'Gryffindor', 'Luna Lovegood': 'Ravenclaw'}
Войти в полноэкранный режим Выход из полноэкранного режима

(Как видите, мы не можем распаковать словарь в одну переменную, но мы можем использовать его для объединения словарей!)

Честно говоря, для решения этой задачи вам не нужно знать о функции распаковки. Это просто классная вещь, которую можно использовать. И посмотрите, как мы можем использовать эту функцию внутри функции range для нашего цикла for. range ожидает три аргумента: начало, конец и шаг. В этом случае, как мы видим в примере, for i in range(*values) будет то же самое, что for i in range(0, 5, 2). Потрясающе!

В задаче нам также нужно вывести EEE для неправильных ответов, и предоставить пользователю в общей сложности 3 варианта, чтобы получить правильный ответ. Но в целом нам нужно задать 10 вопросов. Поэтому здесь целесообразно использовать двойной цикл. Если внешний цикл отслеживает 10 вопросов, то внутренний цикл может отслеживать 3 раза один и тот же заданный вопрос. Если пользователь дает правильный ответ, мы можем выйти из внутреннего цикла. Если внутренний цикл полностью завершен (то есть один и тот же вопрос задан трижды), нам нужно вывести правильный ответ, прежде чем переходить к другому вопросу. Соответственно, мы также сохраняем счет очков пользователя. Достаточно просто увеличить переменную score count при получении правильного ответа. И в этом нет ничего особенного, если вы правильно реализуете get_level и generate_integer, и будете осторожны с циклами и моментом выхода из них. Примеры распаковки, которые я привожу здесь, просто интересно знать, даже если это не обязательно использовать для решения данной задачи. Теперь, наконец, перейдем к последнему.

Индекс цен на биткойн

Эта задача сначала кажется сложной, но на самом деле она очень проста. Если количество аргументов командной строки не равно 2, нам нужно выйти из программы с сообщением Missing command-line argument. Если мы не можем преобразовать второй аргумент в float, то выходим с сообщением Command-line argument is not a number. После этого, используя библиотеку requests, мы получаем ответ с URL, указанного в объяснении проблемы. Поскольку он в формате JSON, разумно будет использовать именно тот метод, который нужен. А вот и самая сложная часть: как преобразовать строку курса, что-то вроде 37,769.6060 в float? Возможно, нам поможет избавление от запятой. И как это сделать? Если считать, что удаление символа – это просто замена его на ничто, то мы на верном пути. После этого простого вычисления суммы с количеством биткоинов, которое предоставляется в качестве аргумента командной строки, и печати отформатированного результата будет достаточно, чтобы решить эту проблему. А как это сделать, буквально дается подсказка в объяснении задачи.

Наконец, подошла к концу и эта неделя о библиотеках. На этот раз мы видели несколько более тяжелые задачи, и, честно говоря, я ожидаю, что в ближайшие недели задачи будут постепенно усложняться. Но на самом деле это то, чему стоит радоваться. Я надеюсь, что на этой неделе вы узнали много нового и – если вы похожи на меня – снова полюбили Python.

Увидимся на следующей неделе для решения проблемного набора юнит-тестов. Счастливого кодинга!

Оцените статью
Procodings.ru
Добавить комментарий