Ерунда на itertools и генераторах
Я люблю itertools и генераторы в Пайтоне, но как-то по работе пока не удаётся их применить, поэтому, чтобы не завять, приходится время от времени писать всякую ерунду.
Прочитал тут на «Хабре», что дробь 1/998001 даёт последовательность из чисел от нуля до 997. Вот я и написал код с использованием генераторов и itertools, который берёт эту дробь и выводит из неё все числа последовательно, пока не попадутся числа с разницей не равной единице.
from decimal import Decimal, getcontext
from itertools import groupby, islice, izip, takewhile, chain, imap
getcontext().prec = 3000
n = (
groupby(
enumerate(
islice(str(1 / Decimal(998001)), 2, None)
),
lambda n: n[0] // 3)
)
n = (int(''.join(n[1] for n in n[1])) for n in n)
n = chain(*takewhile(lambda n: n[1] - n[0] == 1, izip(n, n)))
print ', '.join(imap(str, n))
Кстати, для вычисления дроби с точностью до трёхтысячного знака, используется модуль decimal.
Подумалось, что многим, наверное, было интересно сравнить как выглядит эквивалентный код на Пайтоне без всех этих страшных (на взгляд большинства) ленивых выражений. А то некоторые ребята по моей вине уже думают, что Пайтон какой-то адский язык. Ничего подобного. Вот, посмотрите, более традиционное решение:
from decimal import Decimal, getcontext
getcontext().prec = 3000
number, prev, out = str(1 / Decimal(998001)), -1, []
for i in xrange(2, len(number), 3):
curr = int(number[i:i+3])
if curr - prev != 1: break
prev = curr
out.append(str(curr))
print ', '.join(out)
Очень, кстати, интересно, что есть 10 типов людей. Те, которые считают, что «there should be one-- and preferably only one --obvious way to do it» говорит о первом подходе (с итертулз, лямбдами и прочими радостями), и те, которые так же думают о «традиционном» решении (-:
А я теперь определиться не могу как мне больше нравится, а это значит что PEP20 всё врёт )-:
А есть какой-то способ посмотреть на машинный код этих программ? Интересно, насколько он будет запутан.
Комментарий для profiles.google.com/jankkhvej/:
Насколько я знаю, компиляторов Пайтона не существует и вряд ли может существовать. Это слишком динамический язык, чтобы его можно было компилировать.
После двух листингов появился вопрос:
А зачем лаконичный код 2 листинга превращать в «не такой очевидный» код 1 листинга.
К тому же нет выигрыша ни по кол-ву подключенных модулей и ни по скорости выполнения(наверняка, но могу ошибаться). Разве что по количеству строк. Или это и было целью? -))
Комментарий для SowingSadness:
Первый абзац отвечает на этот вопрос же.
Есть выигрыш по скорости и простоте модификации, кстати, но для этого нужно уметь это читать, конечно.
Комментарий для Евгения Степанищева:
Ну вот какому-то реальному процессору, типа i386 или ARM, приходится же исполнять это. Вот и хотелось посмотреть на то, что он в итоге исполняет, и сделать вывод — существует ли смысл в таких конструкциях, умеют ли существующие процессоры эффективно работать с такими языками…
Perl-то, кстати, довольно эффективен в плане перевода его конструкций на реальные устройства.
А вот про Python тут кто-то может сказать, кто-то может уже смотрел в исходники?
Комментарий для Евгения Степанищева:
Кстати, а почему у вас в блоге время не в моём часовом поясе, и это никак не обозначено?
Комментарий для profiles.google.com/jankkhvej/:
Процессоры — нет. Процессоры-то тут причём? Это всё равно раскрывается в обычные циклы с условиями, процессорам всё равно какие там конструкции.
Я даже как-то теряюсь перед вашим вопросом. Наверное потому что это мой блог?
Комментарий для Евгения Степанищева:
Мне сложно читать первый пример из-за названий переменных, например:
Почему тут все n? :) В остальных строках кода, кстати, тоже. Во втором примере они хоть и сокращенные, но можно предположить по названиям, что они означают.
Комментарий для fulc.ru:
Ну, сначала там не «n» были, это уже для запутывания :)
Комментарий для Евгения Степанищева:
Я как то ожидал, что если я его читаю :), то время должно отображается в они часовом поясе. Тем более, что везде практически так и происходит.
Насчёт того, что неважно, какой процессор — ну как же ж?! А кто будет исполнять код-то?! Мерлин? Пушкин? ;)
Комментарий для profiles.google.com/jankkhvej/:
Где, например?
Процессор. Я же написал — эти конструкции всё одно будут развёрнуты в циклы и условия. Всё равно процессору что там за конструкции. Вопрос, наверное, в том насколько эффективно будут развёрнуты. Но тут вопрос реализации, сами по себе конструкции каких-то ограничений в этом смысле не несут.
Комментарий для Евгения Степанищева:
Кстати, использование ленивых выражений вовсе не обязывает к написанию диких однострочников. Вполне можно писать что-то наподобие:
Ну и так далее. В сочетании с несокращенными названиями переменных получается, на мой взгляд, гораздо понятнее.
Комментарий для fulc.ru:
Как будто цель в понятности. Первый абзац же раскрывает тему :)
Ну и использование промежуточных переменных всё ломает, так как, попытайся я встроить что-то в цепочку, мне придётся переименовывать переменные и ещё убедиться, что ниже они нигде не используются.
Комментарий для Евгения Степанищева:
Не очень понял. Если вставить что-то в цепочку в твоем примере, то придется (в общем случае) все равно переписывать то, что идет выше уровнем, потому что данные просто другие будут. В моем случае — в тех строчках, где данные другие, ты все равно будешь менять правую часть, ну и заодно переменные переименуешь. А с того момента, когда данные становятся такими же, все останется прежним.
Это уже другая задача, я опять не вижу связи с ленивыми выражениями. Вот ты не боишься, что у тебя во втором коде «prev» где-то ниже будет использоваться? Вообще, если название переменной описывает ее содержание (у меня не совсем так, «grouped_digits» используется два раза, но мне было лень думать, как назвать дополнительную переменную), то вероятность двойного использования переменной снизится. Также снизится и от разделения кода на независимые функции.
Комментарий для Евгения Степанищева:
Я про это и спрашивал. Наверное, как-то не очевидно. Ну да ладно, погуглю ещё сам, спасибо за интересную тему.
Комментарий для fulc.ru:
Ты, Володь, рушишь всю идею, вот я и сопротивляюсь :) Написать какую-то понятную ерунду — скучно, написать что-то непонятное — намного веселее, это же не продакшн, тут мне весело должно быть :)