Функциональный питон - почему только один из этих генераторов требует 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 без использования каких-либо списков. Здорово.

Другие вопросы по тегам