5++ способов в одну строку на Python решить первую задачу Проекта Эйлера
Однажды меня посетила мысль, а что если попробовать решить первую задачу Проекта Эйлера всевозможными способами, но с условием, что решение должно быть в одну строку. В итоге получилось более пяти однострочных решений с применением Filter, Map, Reduce, Generator Expression и т.д. В этой статье я покажу то, к чему я пришёл.
Это моя первая статья. Стоит отнестись к ней настороженно. Уникальные решения будут оформлены в отдельные пункты. Менее уникальные — в подпункты.
Условие задачи
Если выписать все натуральные числа меньше 10, кратные 3 или 5, то получим 3, 5, 6 и 9. Сумма этих чисел равна 23.
Найдите сумму всех чисел меньше 1000, кратных 3 или 5.
00 — Базовое решение
Прежде чем перейти непосредственно к однострочным решениям, разумно было бы упомянуть сначала стандартное, классическое решение:
result = 0 for i in range(1, 1000): if i%3 == 0 or i%5 == 0: result += i print(result) # => 233168
Перебираем последовательность чисел от 1 до 999. Если перебираемое число делится на 3 или на 5 без остатка от деления, то прибавляем каждое такое число в заранее объявленную переменную result .
01 — Generator Expression. Выражение-генератор
print(sum(i for i in range(1, 1000) if i%3 == 0 or i%5 == 0)) # => 233168
Числа из последовательности от 1 до 999, делящиеся на 3 или на 5 без остатка от деления, собираются в генератор. Затем функция sum() складывает содержимое генератора.
01.a — List Comprehension. Выражение на основе списка
print(sum([i for i in range(1, 1000) if i%3 == 0 or i%5 == 0])) # => 233168
В отличии от предыдущего, здесь выражение дополнительно помещается в список. Стоило упомянуть этот вариант, так как он довольно часто встречается в различных статьях.
01.b — Set Comprehension. Выражение на основе множества
print(sum()) # => 233168
Тоже, что и в предыдущем, но вместо списка здесь множество.
02 — Filter
print(sum(filter(lambda i: i%3 == 0 or i%5 == 0, range(1, 1000)))) # => 233168
Функция filter схожа по принципу работы с выражением-генератором. Функция лямбда применяется к каждому элементу последовательности чисел от 1 до 999. Все числа последовательности, делящиеся на 3 или на 5 без остатка от деления, возвращаются, затем суммируются функцией sum() .
03 — Map
print(sum(map(lambda i: i if i%3 == 0 or i%5 == 0 else 0, range(1, 1000)))) # => 233168
Перебираемые числа последовательности от 1 до 999, делящиеся на 3 или 5 без остатка от деления, остаются без изменений, все остальные числа заменяются на ноль. Полученная последовательность суммируется функцией sum() .
04 — Reduce
from functools import reduce print(reduce(lambda x, y: x+y if y%3 == 0 or y%5 == 0 else x, range(1, 1000), 0)) # => 233168
Из всей подборки, этот вариант «очень не очень». Как по степени реализации, так и по времени выполнения(но об этом попозже).
Если в reduce указан инициализатор(в нашем случае ноль), то он становится накопителем. К нему по очереди прибавляются только те числа из последовательности от 1 до 999, которые делятся на 3 или на 5 без остатка от деления. Если из функции reduce убрать инициализатор ноль, то инициализатором станет крайний левый элемент последовательности.
05 — Однострочное решение на основе множества
print(sum(set(range(3, 1000, 3)) | set(range(5, 1000, 5)))) # => 233168
Самое элегантное решение, как по красоте написания, так и по времени выполнения.
Последовательность чисел от 1 до 999, кратную трём, помещаем во множество и объединяем со множеством, содержащим в себе последовательность чисел от 1 до 999, кратную пяти. Содержимое, полученного множества суммируем функцией sum() .
05.a — Ещё одно однострочное решение на основе множества
print(sum( <*range(3, 1000, 3)>| <*range(5, 1000, 5)>)) # => 233168
Похожий вариант на предыдущий, но, если использовать фигурные скобки, то последовательность чисел от 1 до 999, кратную трём и последовательность чисел от 1 до 999, кратную пяти, нужно распаковывать.
05.b — И ещё одно однострочное решение на основе множества
print(sum(set(range(3, 1000, 3)).union(range(5, 1000, 5)))) # => 233168
Создаём множество, с последовательностью чисел от 1 до 999, кратную трём и присоединяем к нему последовательность чисел от 1 до 999, кратную пяти. Затем функцией sum() суммируем.
05.c И последнее однострочное решение на основе множества
print(sum(set([*range(3, 1000, 3)] + [*range(5, 1000, 5)]))) # => 233168
По аналогии с предыдущими. Распаковываем последовательности чисел в списки. Складываем списки. Оборачиваем во множество. Затем суммируем функцией sum() .
Смотрим на скорость выполнения каждого однострочного решения
Если проверить скорость выполнения каждого однострочного решения в командной строке, при помощи timeit, получим следующие значения в микросекундах:
sum(i for i in range(1, 1000) if i%3 == 0 or i%5 == 0) # 203 usec sum([i for i in range(1, 1000) if i%3 == 0 or i%5 == 0]) # 181 usec sum() # 189 usec sum(filter(lambda i: i%3 == 0 or i%5 == 0, range(1, 1000))) # 306 usec sum(map(lambda i: i if i%3 == 0 or i%5 == 0 else 0, range(1, 1000))) # 313 usec reduce(lambda x, y: x+y if y%3 == 0 or y%5 == 0 else x, range(1, 1000), 0)# 324 usec sum(set(range(3, 1000, 3)) | set(range(5, 1000, 5))) # 47 usec sum( <*range(3, 1000, 3)>| <*range(5, 1000, 5)>) # 47 usec sum(set(range(3, 1000, 3)).union(range(5, 1000, 5))) # 41 usec sum(set([*range(3, 1000, 3)] + [*range(5, 1000, 5)])) # 43 usec
Методика расчёта: python -m timeit «выражение»
Быстрее всего справились с задачей последние четыре варианта.
Заключение
Всего получилось 5 уникальных + 5 не уникальных решений. Благодаря этой задаче у меня появилось более устойчивое понимание работы функций Filter, Map, Reduce. И если раньше я недоумевал, почему функцию Reduce убрали из основного модуля, то теперь я не сомневаюсь в правильности этого решения.
В статье я старался отойти от популярного шаблона повествования «точность на грани бесполезности». Где предложения набиты под завязку «тяжёлыми» терминами, а из знакомого там только союзы и предлоги. Не уверен, что у меня получилось.
Как вывести цикл в одну строку питон
1) Функция print() имеет параметр end , указывающий какой символ или какую строку выводить после вывода каждого значения (по дефолту переходом на новую строку, т.е. строкой ‘\n’ ). Укажите необходимый разделитель (например пробельной строкой ‘ ‘ ):
for x in range(0, 5): print(x, end=' ') # => 0 1 2 3 4
2) Перед выводом данные можно агрегировать в массив, который затем методом join() соединяем и выводим одной строкой:
array = [] for x in range(0, 5): array.append(str(x)) # => [0, 1, 2, 3, 4] print(' '.join(array)) # => 0 1 2 3 4
При этом все элементы должны быть строковыми объектами, поэтому в цикле мы приводили числа к строкам str(x) .
Как сделать цикл for и условие if in в одну строку?
вопрос следующего типа.
цикл for с последующим условием if in будет использоваться более 1000 раз.
Можно ли как то сократить в 1 строку?
- Вопрос задан 02 апр.
- 487 просмотров
Комментировать
Решения вопроса 1
цикл for с последующим условием if in будет использоваться более 1000 раз.
Можно ли как то сократить в 1 строку?
Можешь пояснить, какая вообще связь между длиной кода и числом использований?
В принципе ты можешь использовать регулярные выражения под эти цели. Они более гибкие, чем просто набор подстрок.
Сейчас выглядит как будто ты пишешь этакого чат-бота для терминала. Если я прав, то длинная цепочка из if-elif-elif тут не подойдёт.
Но если прямо невтерпёж, то
if any(word in text for word in ["привет", "хай"]):
Ответ написан 02 апр.
Нравится 2 5 комментариев
RoMoN @RoMoN777 Автор вопроса
от души эта строка то что мне нужно.
да делаю чат бота телеграмм.
RoMoN, лучше используй существующие средства для выбора команды. У большинства библиотек что-то да есть.
А как в таком однострочнике расположить break ?
hulitolku, никак, да и не надо. Как ты это себе вообще представляешь?
Vindicar, Ну допустим список [«привет», «хай»] состоит из десятки тысяч элементов, и чтобы не тратить время, пробегая по всему списку, если например слово найдется в самом начале, то брейк будет прерывать цикл.
Ответы на вопрос 1
Вроде человек. Вроде учусь. Вроде пайтону
походу, никак
C:\Users\User>py Python 3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> text = '123' >>> print('hello') if command in text for command in ["привет", "хай"] File "", line 1 print('hello') if command in text for command in ["привет", "хай"] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SyntaxError: expected 'else' after 'if' expression >>> print('hello') if command in text else pass for command in ["привет", "хай"] File "", line 1 print('hello') if command in text else pass for command in ["привет", "хай"] ^^^^ SyntaxError: invalid syntax >>> print('hello') if command in text else . for command in ["привет", "хай"] File "", line 1 print('hello') if command in text else . for command in ["привет", "хай"] ^^^ SyntaxError: invalid syntax >>> print('hello') if command in text else print('',end='') for command in ["привет", "хай"] File "", line 1 print('hello') if command in text else print('',end='') for command in ["привет", "хай"] ^^^ SyntaxError: invalid syntax >>> command print('hello') if command in text else print('',end='') for command in ["привет", "хай"] File "", line 1 command print('hello') if command in text else print('',end='') for command in ["привет", "хай"] ^^^^^ SyntaxError: invalid syntax >>> print('hello') if command in text else print('',end='') command for command in ["привет", "хай"] File "", line 1 print('hello') if command in text else print('',end='') command for command in ["привет", "хай"] ^^^^^^^ SyntaxError: invalid syntax >>> print('hello') if command in text else print('',end='') for command in ["привет", "хай"] File "", line 1 print('hello') if command in text else print('',end='') for command in ["привет", "хай"] ^^^ SyntaxError: invalid syntax
Цикл for в одну строку
Как и большинство программистов, вы знаете, что после создания массива, вам нужно написать цикл для его обработки. С этим нет никаких проблем, но иногда нам не нужно использовать несколько строк для написания полного цикла for для одной простой задачи. К частью, Python это понимает и предоставляет замечательный инструмент для использования в таких ситуациях. Этот инструмент называется генератор списка (list comprehensions, списковое включение).
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Telegram Чат & Канал
Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Что это за зверь?
Списковое включение (List comprehensions) – это списки, которые генерируются с циклом for внутри. Они очень распространены в Python и выглядят примерно следующим образом:
[ thing for thing in list_of_things ]
Возможно, вы еще сильнее запутались, так что сделаем шаг назад. Список содержит в себе множество вещей, но определяется между квадратными скобками. Скажем, мне нужно получить функцию, которая удваивает значения всех чисел в списке. Для начала, мне нужно создать список чисел.
my_list = [ 21 , 2 , 93 ]
Теперь создадим функцию. Назовем ее list_doubler , так как это именно то, что она делает, и она будет принимать аргумент, который является списком, который мы будем удваивать.
def list_doubler ( lst ) :
for num in lst :
doubled . append ( num * 2 )
return doubled
Вызов этой функции даст нам новый список с удвоенными элементами.
my_doubled_list = list_doubler ( lst )
my_doubled_list теперь содержит значения 42 , 4 и 186 . Эта функция простая и делает то, что нам нужно простым способом, но это пять строк, учитывая определяющую строку. Также есть переменная, с которой мы ничего не делаем, кроме как добавляем и в конце возвращаем её.
Единственная часть функции, которая по-настоящему работает – это цикл for. Цикл for тоже мало что делает, просто умножает число на 2. Это идеальный кандидат для превращения в списковое включение.
Создание спискового включения
Давайте сохраним его как функцию, которую мы будем вызывать. Нам нужно только упростить тело функции. Так как списковое включение создает списки, а списки могут быть назначены к переменным, примем это во внимание и расположим списковое включение справа от doubled и продолжим.
doubled = [ thing for thing in list_of_things ]
Хорошо, теперь нам нужно заполнить правую сторону. Как и с нормальным циклом for, а правая часть списка выглядит именно так, нам нужно назвать элементы в нашем цикле. Сначала, назовем каждый объект, и мы также будем использовать переменную списка, которая будет передана.
doubled = [ thing for num in lst ]
Это не может работать в полной мере, так как элемент не является… элементом. В нашей изначальной функции мы выполнили num * 2 , так что давайте сделаем это еще раз.
doubled = [ num * 2 for num in lst ]
Все что находится перед циклом for точно внесено в список. Наконец, нам нужно вернуть наш новый список.
def list_doubler ( lst ) :
doubled = [ num * 2 for num in lst ]
return doubled
Запускаем нашу новую функцию.
my_doubled_list = list_doubler ( [ 12 , 4 , 202 ] )
И да, my_doubled_list содержит ожидаемые значения 24 , 8 и 404 . Отлично, все работает! Но так как мы создаем и моментально возвращаем переменную, давайте просто применим списковое включение напрямую.
def list_doubler ( lst ) :
return [ num * 2 for num in lst ]
Хорошо, отлично, но зачем мне это нужно?
Списковые включения (генератор списка, list comprehensions) отлично подходят для случаев, когда нам нужно сохранить немного места в коде. Они также удобны в случаях, когда вам просто нужно быстро обработать списки, чтобы сэкономить время над рутинной работой с этим списком.
Они также очень полезны, если вы хотите больше узнать о функциональном программировании, но эту тему мы обсудим в будущем.
Применяем условие if в список
Давайте сделаем новую функцию, которая будет давать только длинные слова из списка. Скажем, каждое слово, которое состоит более чем из 5 букв, будет считаться длинным. Для начала пропишем их вручную.
def long_words ( lst ) :
for word in lst :
if len ( word ) > 5 :
words . append ( word )
return words
Мы создали переменную для хранения наших слов, применяем цикл над всеми словами в нашем списке, и проверяем длинну каждого слова. Если оно длиннее 5 букв, мы вносим слово в список, и затем, наконец, мы отсылаем список назад. Давайте попробуем.
long_words([‘blog’, ‘Treehouse’, ‘Python’, ‘hi’]) возвращает [‘Treehouse’, ‘Python’] . Это как раз то, чего мы и ожидали.
Хорошо, давайте перепишем это в списковое включение. Для начала, построим то, что мы и так знаем.
def long_words ( lst ) :
return [ word for word in lst ]
Это возвращает нам все слова, не только те, которые длиннее 5 букв. Мы вносим условный оператор в конец цикла for.
def long_words ( lst ) :
return [ word for word in lst if len ( word ) > 5 ]
Итак, мы использовали всё то же условие if, но поместили его в конец спискового включения. Оно использует то же наименование переменной, которое мы используем для элементов в списке.
Хорошо, давайте опробуем эту версию long_words([‘list’, ‘comprehension’, ‘Treehouse’, ‘Ken’]) возвращает [‘comprehension’, ‘Treehouse’] .
Примеры
1. Возводим в квадрат все числа от 1 до 9. Применяем функцию range.
[ x * * 2 for x in range ( 10 ) ]
[ 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 ]
2. Все цифры которые делятся на 5 без остатка, в диапазоне от 0 до 100.
[ x for x in range ( 100 ) if x % 5 == 0 ]
[ 0 , 5 , 10 , 15 , 20 , 25 , 30 , 35 , 40 , 45 , 50 , 55 , 60 , 65 , 70 , 75 , 80 , 85 , 90 , 95 ]
3. Все цифры которые делятся на 3 и 6 без остатка, в диапазоне от 0 до 50.
[ x for x in range ( 50 ) if x % 3 == 0 and x % 6 != 0 ]
[ 3 , 9 , 15 , 21 , 27 , 33 , 39 , 45 ]
4. Первая буква из каждого слова предложения.
phrase = «Тестовое сообщение из мира Python для сообщества.»
print ( [ w [ 0 ] for w in phrase . split ( ) ] )
[ ‘Т’ , ‘с’ , ‘и’ , ‘м’ , ‘P’ , ‘д’ , ‘с’ ]
5. Заменяем букву А в каждом слове на # .
phrase = «АБАЖУР, АБАЗИНСКИЙ, АБАЗИНЫ, АББАТ, АББАТИСА, АББАТСТВО»
print ( » . join ( [ letter if letter != ‘А’ else ‘#’ for letter in phrase ] ) )
#Б#ЖУР, #Б#ЗИНСКИЙ, #Б#ЗИНЫ, #ББ#Т, #ББ#ТИС#, #ББ#ТСТВО
Итоги
Надеюсь это руководство помогло вам понять простой способ экономии кода , который вам нужно написать для получения конкретной готовой работы с вашими списками.
Старайтесь сохранять ваши списковые включения короткими, а условия if – простыми. Несложно разглядеть решение многих ваших проблем в списковых включениях и превратить их в огромный беспорядок.
Если это только распалило ваш аппетит, посмотрим, сможете ли вы разобраться со словарными включениями самостоятельно. Они используют конструкторы dict, , но они довольно похожи. Вы также можете проработать установочные включения. Также ознакомьтесь с функциональным программированием в Python, если считаете себя готовым.
Больше примеров
- https://www.python.org/dev/peps/pep-0289/
- https://legacy.python.org/dev/peps/pep-0274/
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»