[Python] Можно ли клонировать или перезапускать генератор?
От: Кодт Россия  
Дата: 23.08.07 13:50
Оценка:
def dump(xs) :
    print 'One:',
    for x in xs : print x,
    print
    print 'Two:',
    for x in xs : print x,
    print

def series(n) :
    for x in range(n) : yield x

print 'Range:'
dump(range(5))

print 'Series:'
dump(series(5))


Получаем
Range:
One: 0 1 2 3 4
Two: 0 1 2 3 4
Series:
One: 0 1 2 3 4
Two:


А хотелось бы, чтоб генератор был неотличим от контейнера. При том, что алгоритм генератора может быть довольно кудрявым.
... << RSDN@Home 1.2.0 alpha rev. 655>>

11.12.08 22:46: Перенесено модератором из 'Декларативное программирование' — der Igel
Перекуём баги на фичи!
python
Re: [Python] Можно ли клонировать или перезапускать генерато
От: Кодт Россия  
Дата: 23.08.07 14:48
Оценка: 6 (1)
Придумал обходной манёвр.

Ленивый генератор
def dump(xsf) : # means 'x series function'
    print 'One:',
    for x in xsf() : print x,
    print
    print 'Two:',
    for x in xsf() : print x,
    print

dump(lambda : range(5))
dump(lambda : series(5))
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: [Python] Можно ли клонировать или перезапускать генерато
От: FR  
Дата: 24.08.07 04:51
Оценка:
Здравствуйте, Кодт, Вы писали:


К>А хотелось бы, чтоб генератор был неотличим от контейнера. При том, что алгоритм генератора может быть довольно кудрявым.


Ну можно просто превратить в контейнер dump(list(series(5))) или использовать itertools.tee (но он тоже использует дополнительую память), так что твой вариант с лямбдами может быть и лучше.
Re[2]: [Python] Можно ли клонировать или перезапускать генер
От: Tonal- Россия www.promsoft.ru
Дата: 25.08.07 04:58
Оценка:
Здравствуйте, FR, Вы писали:
К>>А хотелось бы, чтоб генератор был неотличим от контейнера. При том, что алгоритм генератора может быть довольно кудрявым.
FR>Ну можно просто превратить в контейнер dump(list(series(5))) или использовать itertools.tee (но он тоже использует дополнительую память), так что твой вариант с лямбдами может быть и лучше.
Не зная природы генератора без дополнительной памяти не обойтись...
Вот простой пример
def rnd_gen():
  yield random( )
... << RSDN@Home 1.2.0 alpha rev. 721>>
Re: [Python] Можно ли клонировать или перезапускать генерато
От: eugals Россия  
Дата: 26.08.07 19:40
Оценка: 88 (2)
Здравствуйте, Кодт, Вы писали:

К>А хотелось бы, чтоб генератор был неотличим от контейнера.

Да странно. Вообще-то это могли бы и в самом языке поддержать.
Достаточно, чтобы при вызове генераторной функции возвращался не сам итератор, а нечто с методом __iter__. (Тут можно вспомнить C#-овской разделение IEnumerable-IEnumerator).

Собственно, это можно и самому реализовать:
# -*- coding: windows-1251 -*-

class RewindableGenerator(object):

    def __init__(self, func, args, kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def __iter__(self):
        return self.func(*self.args, **self.kwargs)


def real_generator(func):
    def wrapper(*args, **kwargs):
        return RewindableGenerator(func, args, kwargs)
    return wrapper


@real_generator
def foo():
    yield 42


gen = foo()
print list(gen)
print list(gen)


правда, ценой введения спецдекоратора
... << RSDN@Home 1.2.0 alpha rev. 679>>
Re[2]: [Python] Можно ли клонировать или перезапускать генер
От: Кодт Россия  
Дата: 27.08.07 08:36
Оценка:
Здравствуйте, eugals, Вы писали:

E>Да странно. Вообще-то это могли бы и в самом языке поддержать.

E>Достаточно, чтобы при вызове генераторной функции возвращался не сам итератор, а нечто с методом __iter__. (Тут можно вспомнить C#-овской разделение IEnumerable-IEnumerator).

На самом деле, это ещё вопрос о глубине копирования (или о чистоте рук).
Если какой-то рядовой генератор является параметром замыкания, то все экземпляры "правильного" генератора его расшарят между собой, что приведёт к неадекватностям.
def squares() :
    for x in range(10) :
        yield x*x

@real_generator
def roots(xs) :
    for x in xs :
        yield sqrt(x)
        if x==1 : break # чуть более затейливая логика, чтоб веселее было

s = squares
r = roots(s)
print list(r) # [0.0, 1.0]
print list(r) # [2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]


Кстати, а что это за супер-синтаксис? Где почитать о нём? (В мануале к 2.4 с разбегу не нашёл)
E>@real_generator
E>def foo():
E>    yield 42
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[3]: [Python] Можно ли клонировать или перезапускать генер
От: eugals Россия  
Дата: 27.08.07 08:58
Оценка: 56 (1)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, eugals, Вы писали:


К>На самом деле, это ещё вопрос о глубине копирования (или о чистоте рук).

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

К>Кстати, а что это за супер-синтаксис? Где почитать о нём? (В мануале к 2.4 с разбегу не нашёл)

К>
E>>@real_generator
E>>def foo():
E>>    yield 42
К>

здесь
... << RSDN@Home 1.2.0 alpha rev. 679>>
Re[4]: [Python] Можно ли клонировать или перезапускать генер
От: eugals Россия  
Дата: 27.08.07 09:47
Оценка:
Здравствуйте, eugals, Вы писали:

E>Понятно, что если уж использовать эту технику, то повсеместно, т.е. все без исключения генераторые функции должны быть "чистыми"


Правда, тогда пришлось бы отказаться от generator expression-ов — у них та же проблема.
>>> x = (i for i in (1, 2, 3))
>>> x
<generator object at 0x00C48440>
>>> list(x)
[1, 2, 3]
>>> list(x)
[]

Тут уж никакой декоратор не спасет
... << RSDN@Home 1.2.0 alpha rev. 679>>
Re[5]: [Python] Можно ли клонировать или перезапускать генер
От: Кодт Россия  
Дата: 28.08.07 12:47
Оценка:
Здравствуйте, eugals, Вы писали:

E>Правда, тогда пришлось бы отказаться от generator expression-ов — у них та же проблема.

А это как раз и лечится лямбдами
>>> x = lambda : (i*i for i in (1,2,3))
>>> x
<function <lambda> at 0x00B877B0>
>>> x()
<generator object at 0x00B86AD0>
>>> list(x)
[1, 2, 3]
>>> list(x)
[1, 2, 3]
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[6]: [Python] Можно ли клонировать или перезапускать генер
От: Кодт Россия  
Дата: 28.08.07 13:27
Оценка:
Скобочки забыл. Конечно же,
К>
>>>> x = lambda : (i*i for i in (1,2,3))
>>>> list(x())
[1, 4, 9]
К>
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[7]: [Python] Можно ли клонировать или перезапускать генер
От: eugals Россия  
Дата: 28.08.07 13:51
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Скобочки забыл. Конечно же

Угу.

Вообще, в том что касается работы итераторами/генераторами мне больше нравится подход ruby (это вообще, видимо, самое лучшее, что есть в том языке):
x = [1, 2, 3].map { |i| i * i }

x.map { |i| puts(i) } # puts - аналогог питоновского print

x.map { |i| puts(i) }


Он, имхо, нагляднее.
... << RSDN@Home 1.2.0 alpha rev. 679>>
Re[8]: [Python] Можно ли клонировать или перезапускать генер
От: eugals Россия  
Дата: 28.08.07 14:09
Оценка:
Здравствуйте, eugals, Вы писали:

E>Вообще, в том что касается работы итераторами/генераторами мне больше нравится подход ruby (это вообще, видимо, самое лучшее, что есть в том языке):

Вернее так:
x = [1, 2, 3].map { |i| i * i }

puts x # puts - аналог питоновского print
puts x


А вот так через генератор:
def over_squares
    for i in (1..3)
        yield i * i
    end
end

over_squares { |i| puts i }
over_squares { |i| puts i }
... << RSDN@Home 1.2.0 alpha rev. 679>>
Re[8]: [Python] Можно ли клонировать или перезапускать генер
От: Кодт Россия  
Дата: 28.08.07 14:46
Оценка:
Здравствуйте, eugals, Вы писали:

E>Вообще, в том что касается работы итераторами/генераторами мне больше нравится подход ruby (это вообще, видимо, самое лучшее, что есть в том языке):


Что именно больше нравится?
— Морфизмы как методы объекта-списка?
— Или то, что получается выражение с использованем HOF, вместо того, чтоб написать legacy for?
(В питоне с этим трудности — лямбда-выражение не может содержать инструкции).

Кстати, а лепо ли, что map, помимо побочных эффектов, ещё и список (из nil'ов) возвращает?
Правильнее было бы использовать each либо (для извращенцев) inject.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[9]: [Python] Можно ли клонировать или перезапускать генер
От: eugals Россия  
Дата: 28.08.07 16:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Что именно больше нравится?

К>- Морфизмы как методы объекта-списка?
Не совсем. Само по себе это достаточно спорное достоинство, всего лишь синтаксический сахар. Вместо свободных функций в руби принято всё подряд в класс подмешивать, благо язык позволяет сделать это безопасно.
>— Или то, что получается выражение с использованем HOF, вместо того, чтоб написать legacy for?
К>(В питоне с этим трудности — лямбда-выражение не может содержать инструкции).
Тоже не совсем. Особых проблем с этим и в питоне нет.
Кстати, в 3.0 Гвидо вообще хотел от лямбды в пользу вложенных функций отказаться (правда сейчас, вроде, остыл)

Мне нравятся не первое и не второе по отдельности, а то, что получилось в результате их синтеза.
А в результате синтеза, итерирование чисто визаульно выглядит не как разрушающие преобразования некоего объекта "итератора".
И не как серия последовательных вызовов композиции функций над неким списком, типа такого:
print map(sqrt, map(lambda x: x * x, xrange(10)))


А как поток, с расставленными вдоль его течения фильтрами, форсунками и коллекторами:
(1..9)
    .map {|x| x * x }        
            .map { |x| sqrt(x) }

Есть в этом какая-то внутренняя естественность и прозрачность для человеческого глаза. Мне так кажется.

К>Кстати, а лепо ли, что map, помимо побочных эффектов, ещё и список (из nil'ов) возвращает?

К>Правильнее было бы использовать each либо (для извращенцев) inject.
Да. Это я уже переписал в соседней ветке.
... << RSDN@Home 1.2.0 alpha rev. 679>>
Re[8]: [Python] Можно ли клонировать или перезапускать генер
От: BulatZiganshin  
Дата: 28.08.07 16:26
Оценка: +1
E>Вообще, в том что касается работы итераторами/генераторами мне больше нравится подход ruby (это вообще, видимо, самое лучшее, что есть в том языке):

вот только клонирования/перещапуска там вроде нет. чистые клозуры
Люди, я люблю вас! Будьте бдительны!!!
Re[10]: [Python] Можно ли клонировать или перезапускать гене
От: Кодт Россия  
Дата: 28.08.07 16:57
Оценка: 12 (1)
Здравствуйте, eugals, Вы писали:

E>Мне нравятся не первое и не второе по отдельности, а то, что получилось в результате их синтеза.

E>А в результате синтеза, итерирование чисто визаульно выглядит не как разрушающие преобразования некоего объекта "итератора".
E>И не как серия последовательных вызовов композиции функций над неким списком
E>А как поток, с расставленными вдоль его течения фильтрами, форсунками и коллекторами:
E>
E>(1..9)
E>    .map {|x| x * x }        
E>            .map { |x| sqrt(x) }
E>

E>Есть в этом какая-то внутренняя естественность и прозрачность для человеческого глаза. Мне так кажется.

А в хаскеле можно любую двухместную функцию записать как инфиксный оператор Которым, безусловно, является вызов метода (слева — объект, справа — кортеж аргументов).
map_ = flip map
xs = [1..9] `map_` (\x -> x*x) `map_` sqrt
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[11]: [Python] Можно ли клонировать или перезапускать гене
От: BulatZiganshin  
Дата: 28.08.07 17:07
Оценка: 56 (1)
К>А в хаскеле можно любую двухместную функцию записать как инфиксный оператор Которым, безусловно, является вызов метода (слева — объект, справа — кортеж аргументов).
К>
К>map_ = flip map
К>xs = [1..9] `map_` (\x -> x*x) `map_` sqrt
К>


я делаю по-другому:

(.$) = flip (.)

-- Подготовить список замен к использованию в lookup
prepareSubsts x = x
    -- Удалить пустые строки, пробелы и комментарии
    .$ map (filter (not.isSpace) . fst . split2 ';') .$ filter (not.null)
    -- Заменить каждую строку с символом # на 9 строк, где # пробегает значения от 1 до 9
    .$ concatMap (\s -> map (\d->replace '#' d s) ['2'..'9'])
    -- Преобразовать список строк вида "a=b" в список для lookup
    .$ map (split2 '=')
Люди, я люблю вас! Будьте бдительны!!!
Re: [Python] Можно ли клонировать или перезапускать генерато
От: c-smile Канада http://terrainformatica.com
Дата: 29.08.07 03:59
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А хотелось бы, чтоб генератор был неотличим от контейнера. При том, что алгоритм генератора может быть довольно кудрявым.


Лучше итераторы использовать. Да и памяти едят меньше ибо для запуска генератора нужен свой стек.
По сути генератор это dormant thread который достаточно легко трансформируется в просто thread.

По идее генератор можно презапустить м помощью send(v)

Вот интерфейс генератора из SpiderMonkey:

class Generator.<O, I, E> {
  public function send(i: I) : O,
  public function next() : O;
  public function throw(e : E) : O,
  public function close() : void
};


Насколько я помню Гвидо в Python тоже send() делал.
Re: [Python] Можно ли клонировать или перезапускать генерато
От: FR  
Дата: 29.11.07 19:11
Оценка: 27 (1)
Здравствуйте, Кодт, Вы писали:

К>А хотелось бы, чтоб генератор был неотличим от контейнера. При том, что алгоритм генератора может быть довольно кудрявым.


Наткнулся на библиотечку http://www.fiber-space.de/generator_tools/doc/generator_tools.html которая умеет копировать генераторы:
from generator_tools import copy_generator

def gen(n):
    while n > 0:
        yield n
        n -= 1

g1 = gen(10)
g2 = copy_generator(g1)
    
print zip(g1, g2)
Re[2]: [Python] Можно ли клонировать или перезапускать генер
От: Кодт Россия  
Дата: 30.11.07 12:00
Оценка:
Здравствуйте, FR, Вы писали:

FR>Наткнулся на библиотечку http://www.fiber-space.de/generator_tools/doc/generator_tools.html которая умеет копировать генераторы:


Я так понял, они дизассемблируют на лету.

Отсюда ограничения: функция, использующая внутри себя произвольные генераторы, клонируется неадекватно. Потому что генератор внутри хранится как объект по ссылке, и будет разделяться между всеми копиями.
Клонировать можно только функции с известными типами генераторов (for_iter), или вообще без них.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.