Функциональный питон - почему только один из этих генераторов требует list() для работы?
При вычислении китайской теоремы об остатках из вектора кортежей (вычет, модуль) следующий код не выполняется:
c = ((1,5),(3,7),(11,13),(19,23))
def crt(c):
residues, moduli = zip(*c)
N = product(moduli)
complements = (N/ni for ni in moduli)
scaled_residues = (product(pair) for pair in zip(residues,complements))
inverses = (modular_inverse(*pair) for pair in zip(complements,moduli))
si = (product(u) for u in zip(scaled_residues,inverses))
result = sum(si) % N
return result
Давать результат как 0 (я думаю, сгенерированные итерируемые пустые). Тем не менее, следующий код работает отлично:
def crt(c):
residues, moduli = zip(*c)
N = product(moduli)
complements = list((N/ni for ni in moduli)) # <-- listed
scaled_residues = (product(pair) for pair in zip(residues,complements))
inverses = (modular_inverse(*pair) for pair in zip(complements,moduli))
si = (product(u) for u in zip(scaled_residues,inverses))
result = sum(si) % N
return result
Что дает (а) правильный результат 8851.
Почему я должен list(
один из первых генераторов? Добавление list
к любому последующему генератору не изменяет результат сбоя (0). Только перечисление этого первого генератора дает правильный результат. Что здесь происходит?
2 ответа
Вы повторяете дважды complements
, Вы можете выполнить итерацию только один раз над выражением генератора.
Если вы используете Python 2.x, zip(residues,complements)
будет потреблять complements
и ничего не осталось для zip(complements,moduli)
, На Python 3.x zip
сам генератор, и проблема появляется позже в коде, когда sum()
на самом деле работает генераторы. Было бы вытащить два предмета из complements
за каждую итерацию.
Для справки, основываясь на предложениях в ответе, я переформулировал код вопроса следующим образом:
def complements(moduli,N):
return (N/ni for ni in moduli)
def scaled_residues(residues,complements):
return (product(pair) for pair in zip(residues,complements))
def inverses(complements,moduli):
return (modular_inverse(*pair) for pair in zip(complements,moduli))
def crt_residue_terms(scaled_residues,inverses):
return (product(u) for u in zip(scaled_residues,inverses))
def crt(c):
residues, moduli = zip(*c)
N = product(moduli)
return sum(
crt_residue_terms(
scaled_residues(residues,complements(moduli,N)),
inverses(complements(moduli,N),moduli)
)) % N
Теперь он дает правильный результат 8851 без использования каких-либо списков. Здорово.