Ерунда на 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)
28 января 2012 17:14

Alexey Burlakov (profiles.google.com/gaius.julius)
29 января 2012, 00:24

Очень, кстати, интересно, что есть 10 типов людей. Те, которые считают, что "there should be one-- and preferably only one --obvious way to do it" говорит о первом подходе (с итертулз, лямбдами и прочими радостями), и те, которые так же думают о "традиционном" решении (-:

А я теперь определиться не могу как мне больше нравится, а это значит что PEP20 всё врёт )-:

Sergey Solyanik (profiles.google.com/jankkhvej/)
29 января 2012, 02:29

А есть какой-то способ посмотреть на машинный код этих программ? Интересно, насколько он будет запутан.

bolk (bolknote.ru)
29 января 2012, 09:35, ответ предназначен profiles.google.com/jankkhvej/:

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

SowingSadness (инкогнито)
29 января 2012, 12:53

После двух листингов появился вопрос:
А зачем лаконичный код 2 листинга превращать в "не такой очевидный" код 1 листинга.
К тому же нет выигрыша ни по кол-ву подключенных модулей и ни по скорости выполнения(наверняка, но могу ошибаться). Разве что по количеству строк. Или это и было целью? -))

bolk (bolknote.ru)
29 января 2012, 15:13, ответ предназначен SowingSadness

Первый абзац отвечает на этот вопрос же.

Есть выигрыш по скорости и простоте модификации, кстати, но для этого нужно уметь это читать, конечно.

Sergey Solyanik (profiles.google.com/jankkhvej/)
29 января 2012, 17:59, ответ предназначен bolk (bolknote.ru):

Ну вот какому-то реальному процессору, типа i386 или ARM, приходится же исполнять это. Вот и хотелось посмотреть на то, что он в итоге исполняет, и сделать вывод - существует ли смысл в таких конструкциях, умеют ли существующие процессоры эффективно работать с такими языками…
Perl-то, кстати, довольно эффективен в плане перевода его конструкций на реальные устройства.
А вот про Python тут кто-то может сказать, кто-то может уже смотрел в исходники?

Sergey Solyanik (profiles.google.com/jankkhvej/)
29 января 2012, 18:00, ответ предназначен bolk (bolknote.ru):

Кстати, а почему у вас в блоге время не в моём часовом поясе, и это никак не обозначено?

bolk (bolknote.ru)
29 января 2012, 19:07, ответ предназначен profiles.google.com/jankkhvej/:

умеют ли существующие процессоры эффективно работать с такими языками
Процессоры — нет. Процессоры-то тут причём? Это всё равно раскрывается в обычные циклы с условиями, процессорам всё равно какие там конструкции.
Кстати, а почему у вас в блоге время не в моём часовом поясе, и это никак не обозначено?
Я даже как-то теряюсь перед вашим вопросом. Наверное потому что это *мой* блог?

Fulcrum (fulc.ru)
29 января 2012, 19:14, ответ предназначен bolk (bolknote.ru):

Мне сложно читать первый пример из-за названий переменных, например:
n = (int(''.join(n[1] for n in n[1])) for n in n)
Почему тут все n? :) В остальных строках кода, кстати, тоже. Во втором примере они хоть и сокращенные, но можно предположить по названиям, что они означают.

bolk (bolknote.ru)
29 января 2012, 19:53, ответ предназначен Fulcrum (fulc.ru):

Почему тут все n? :)
Ну, сначала там не «n» были, это уже для запутывания :)

Sergey Solyanik (profiles.google.com/jankkhvej/)
29 января 2012, 21:06, ответ предназначен bolk (bolknote.ru):

Я как то ожидал, что если я его читаю :), то время должно отображается в они часовом поясе. Тем более, что везде практически так и происходит.

Насчёт того, что неважно, какой процессор – ну как же ж?! А кто будет исполнять код-то?! Мерлин? Пушкин? ;)

bolk (bolknote.ru)
29 января 2012, 21:45, ответ предназначен profiles.google.com/jankkhvej/:

Тем более, что везде практически так и происходит.
Где, например?
Насчёт того, что неважно, какой процессор – ну как же ж?! А кто будет исполнять код-то?! Мерлин? Пушкин? ;)
Процессор. Я же написал — эти конструкции всё одно будут развёрнуты в циклы и условия. Всё равно процессору что там за конструкции. Вопрос, наверное, в том насколько эффективно будут развёрнуты. Но тут вопрос реализации, сами по себе конструкции каких-то ограничений в этом смысле не несут.

Fulcrum (fulc.ru)
30 января 2012, 01:50, ответ предназначен bolk (bolknote.ru):

Кстати, использование ленивых выражений вовсе не обязывает к написанию диких однострочников. Вполне можно писать что-то наподобие:
string = str(1 / Decimal(99800))
digits = islice(str(string, 2, None)
grouped_digits = groupby(enumerate(digits), lambda index, digit: index // 3)
grouped_digits = (data for grouper, data in grouped_digits)
numbers = (''.join(digit for index, digit in group) for group in grouped_digits)
Ну и так далее. В сочетании с несокращенными названиями переменных получается, на мой взгляд, гораздо понятнее.

bolk (bolknote.ru)
30 января 2012, 09:25, ответ предназначен Fulcrum (fulc.ru):

Как будто цель в понятности. Первый абзац же раскрывает тему :)

Ну и использование промежуточных переменных всё ломает, так как, попытайся я встроить что-то в цепочку, мне придётся переименовывать переменные и ещё убедиться, что ниже они нигде не используются.

Fulcrum (fulc.ru)
30 января 2012, 10:56, ответ предназначен bolk (bolknote.ru):

Ну и использование промежуточных переменных всё ломает, так как, попытайся я встроить что-то в цепочку, мне придётся переименовывать переменные
Не очень понял. Если вставить что-то в цепочку в твоем примере, то придется (в общем случае) все равно переписывать то, что идет выше уровнем, потому что данные просто другие будут. В моем случае - в тех строчках, где данные другие, ты все равно будешь менять правую часть, ну и заодно переменные переименуешь. А с того момента, когда данные становятся такими же, все останется прежним.
и ещё убедиться, что ниже они нигде не используются.
Это уже другая задача, я опять не вижу связи с ленивыми выражениями. Вот ты не боишься, что у тебя во втором коде "prev" где-то ниже будет использоваться? Вообще, если название переменной описывает ее содержание (у меня не совсем так, "grouped_digits" используется два раза, но мне было лень думать, как назвать дополнительную переменную), то вероятность двойного использования переменной снизится. Также снизится и от разделения кода на независимые функции.

Sergey Solyanik (profiles.google.com/jankkhvej/)
30 января 2012, 11:11, ответ предназначен bolk (bolknote.ru):

Всё равно процессору что там за конструкции. Вопрос, наверное, в том насколько эффективно будут развёрнуты.
Я про это и спрашивал. Наверное, как-то не очевидно. Ну да ладно, погуглю ещё сам, спасибо за интересную тему.

bolk (bolknote.ru)
1 февраля 2012, 11:35, ответ предназначен Fulcrum (fulc.ru):

Ты, Володь, рушишь всю идею, вот я и сопротивляюсь :) Написать какую-то понятную ерунду — скучно, написать что-то непонятное — намного веселее, это же не продакшн, тут мне весело должно быть :)

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)