Я думаю, что это работает:
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
. Эта последняя версия исправляет эту ошибку.
Что с этими ограничениями? Кажется довольно искусственным? –
Вау ... тот, кто дал вам это задание, должен подумать об этом ... Насколько я вижу, это упражнение в обучении людей делать задачу * неправильным образом * – mgilson
@NPE И это тоже в одной строке , :( – thefourtheye