2013-11-27 2 views
1

Я пытаюсь создать бесконечный поток результатов с заданной функцией f и начальным значением x , поэтому первый вызов должен дать начальное значение, второй вызов должен дать f (x), третий вызов F (x2) при х2 предыдущий результат Р (х) и так далее ..Генератор бесконечного потока python без использования результата

то, что я придумал:

def generate(f, x): 
    return itertools.repeat(lambda x: f(x)) 

, который, кажется, не работает. есть идеи? (я не могу использовать выход в своем коде). также я не могу использовать более 1 строки кода для этой проблемы. любая помощь будет оценена по достоинству.

также обратите внимание, что в предыдущем примере. Меня попросили использовать доходность. без проблем:

while True: 
    yield x 
    x = f(x) 

это работает нормально. но теперь .. нет подсказки, как это сделать без

+5

Что с этими ограничениями? Кажется довольно искусственным? –

+6

Вау ... тот, кто дал вам это задание, должен подумать об этом ... Насколько я вижу, это упражнение в обучении людей делать задачу * неправильным образом * – mgilson

+0

@NPE И это тоже в одной строке , :( – thefourtheye

ответ

4

В Python 3.3, вы можете использовать itertools.accumulate:

import itertools 

def generate(f, x): 
    return itertools.accumulate(itertools.repeat(x), lambda v,_:f(v)) 

for i, val in enumerate(generate(lambda x: 2*x, 3)): 
    print(val) 
    if i == 10: 
    break 
+0

Я думаю, что аргумент функции пришел только в 3.3. – DSM

+1

@ DSM: Правильно, спасибо! (Мне очень понравилось повествование в вашем ответе BTW.) – NPE

+0

это прекрасно работает! также благодарим всех, кто помогал! – David

0

Я предполагаю, что это какая-то домашняя работа или задание? Таким образом, я бы сказал, что вы должны взглянуть на generator expressions. Хотя я согласен с другими комментаторами, что это кажется упражнение сомнительной ценности ...

+0

Да, я тоже думал об этом, но единственное, что кажется трюком, - это imap? – David

+0

Я неправильно понял вопрос и думал о 'count'. Это не сработает для того, что вы хотите, хотя вы можете что-то уговорить что-то от 'itertools' ... –

4

Я думаю, что это работает:

import itertools as it 
def g(f, x): 
    return it.chain([x],(setattr(g, 'x', f(getattr(g, 'x', x))) or getattr(g, 'x') for _ in it.count())) 

def f(x): 
    return x + 1 

gen = g(f, 1) 
print next(gen) 
print next(gen) 
print next(gen) 
print next(gen) 

Конечно, он полагается на какое-то отрывочное поведение, когда я фактически добавляю атрибут самой функции для сохранения состояния. В принципе, эта функция будет работать только при первом вызове. После этого все ставки отключены.

Если мы хотим смягчить это ограничение, мы можем использовать временное пространство имен. Проблема в том, что для получения временного пространства имен нам нужен уникальный экземпляр класса (или класс, но экземпляр более чистый и требует только 1 дополнительный набор скобок). Для того, чтобы это произошло в одной строке, мы должны создать новую функцию инлайн и использовать это в качестве аргумента по умолчанию:

import itertools as it 
def g(f, x): 
    return (lambda f, x, ns=type('foo', (object,), {})(): \ 
     it.chain([x], 
       (setattr(ns, 'x', f(getattr(ns, 'x', x))) or getattr(ns, 'x') 
          for _ in it.count())) 
      )(f, x) 

def f(x): 
    return x + 1 

gen = g(f, 1) 
print next(gen) == 1 
print next(gen) == 2 
print next(gen) == 3 
print next(gen) == 4 

print "first worked?" 
gen2 = g(f, 2) 
print next(gen2) == 2 
print next(gen2) == 3 
print next(gen2) == 4 

Я разбил его на несколько строк, для удобства чтения, но это 1-лайнер в глубине души.

версия без импорта

(и самый надежный один все же я верю).

def g(f, x): 
    return iter(lambda f=f, x=x, ns=type('foo', (object,), {'x':x}): ((getattr(ns, 'x'),setattr(ns, 'x', f(getattr(ns, 'x'))))[0]), object()) 

Один трюк здесь такой же, как раньше. Мы создаем лямбда-функцию с изменяемым аргументом по умолчанию, чтобы сохранить состояние. Внутри функции мы создаем кортеж. Первый элемент - это то, что мы действительно хотим, второй элемент - это возвращаемое значение функции setattr, которая используется для обновления состояния. Чтобы избавиться от itertools.chain, мы устанавливаем начальное значение в пространстве имен на значение x, поэтому класс уже инициализирован, чтобы иметь начальное состояние. Второй трюк заключается в том, что мы используем две формы аргумента iter, чтобы избавиться от it.count(), которая использовалась только для создания бесконечного итерационного. iter продолжает вызывать функцию, которую вы даете ей в качестве первого аргумента, пока возвращаемое значение не будет равно второму аргументу.Однако, поскольку мой второй аргумент является экземпляром object, ничто из нашей функции никогда не будет равным ему, поэтому мы создали бесконечный итерируемый без itertools или yield! Подумайте об этом, я считаю, что эта последняя версия является самой надежной. Предыдущие версии имели ошибку, в которой они полагались на правдивость возвращаемого значения f. Я думаю, что они, возможно, потерпели неудачу, если f вернули 0. Эта последняя версия исправляет эту ошибку.

+0

Я думал о переменчивом состоянии аргумента состояния по умолчанию. – DSM

+0

@ DSM - Да, это тоже сработает. Я действительно избавился от этой проблемы с состоянием, возвращая возвращаемое значение лямбда-функции с изменчивым состоянием :) – mgilson

+0

FWIW, я думаю, что это берет его для самого запутанного кода на языке питона, который я когда-либо писал ... Спасибо, профессор. – mgilson

Смежные вопросы