1

今日は発電機です。今日、ループやインポートを使用せずにリストを再帰的にフラット化する方法を見つけたいという質問を見ました。tobias_kは次のコードで回答しました。

def flatten(test_list):
    if isinstance(test_list, list):
        if len(test_list) == 0:
            return []
        first, rest = test_list[0], test_list[1:]
        return flatten(first) + flatten(rest)
    else:
        return [test_list]

ジェネレーターを作成する方法はありますか (ルールを守って: インポートなし、ループなし)?

注: 純粋に教育用です。私はそれが最善のアイデアではないことを知っていますが、それを行う方法を理解できませんでした.

4

3 に答える 3

4

Python 3.3(開発バージョン)では、yield from構造を使用して明示的なループを回避できます。

def flatten(x):
    if isinstance(x, list):
        if x:
            first, rest = x[0], x[1:]
            yield from flatten(first)
            yield from flatten(rest)
    else:
        yield x

現在のバージョンでは、を使用しないソリューションは考えられませんitertools.chain

于 2012-09-18T14:16:59.887 に答える
4

ジェネレーター関数は、少なくとも 1 つのステートメントを含み、yieldreturnを取るステートメントを含まない関数です。ジェネレーター関数が呼び出されると、ジェネレーター イテレーターが返されます。これは反復されると (たとえば、forループによって、または で明示的にnext) 関数の本体を実行し、その状態を凍結して、yieldステートメントごとに呼び出し元に制御を返します (Python 3.3 では、yield fromステートメント)。

Python 関数内のフロー制御は常に順方向です。現在のフレームを設定するようなハックなしf_linenoで ((エイプリルフールの)gotoステートメントで有名なように)、制御が以前のポイントに到達する唯一の方法は、ループ (forまたはwhile) を使用することです。したがって、ループまたは がなけれyield fromば、ジェネレータ イテレータを呼び出すことができる最大回数はyield、ジェネレータ関数内のステートメントの数によって制限されます。

flattenイテレータを返すa を書くのは簡単です。元の解決策を取り、書くreturn iter(flatten(first) + flatten(rest))ことで十分です。しかし、それはジェネレータイテレータではなく、関数はジェネレータ関数ではありません。

これは、悪用f_linenoしてループのない反復を行う実装です。残念ながら、それは使用する必要がありますimport sys:

def current_frame():
    i = None
    def gen():
        yield i.gi_frame.f_back
    i = gen()
    return next(i).f_back

class Loop(object):
    jump = False
    def __call__(self, frame, event, arg):
        if self.jump:
            frame.f_lineno = self.lineno
            self.jump = False
        return None if event == 'call' else self
    def __enter__(self):
        import sys
        sys.settrace(self)
        current_frame().f_back.f_trace = self
        self.lineno = current_frame().f_back.f_lineno
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        if exc_type is None:
            self.jump = True
        else:
            import sys
            sys.settrace(None)
            current_frame().f_back.f_trace = None
            return exc_type is StopIteration

def flatten(x):
    if isinstance(x, list):
        if x:
            first, rest = flatten(x[0]), flatten(x[1:])
            with Loop():
                yield next(first)
            with Loop():
                yield next(rest)
            pass
    else:
        yield x
于 2012-09-18T14:47:19.850 に答える
-1

1行のリスト内包表記で完了:

def flatten (test_list):
    return [element for temp in test_list for element in flatten(temp)] if isinstance(test_list, list) else [test_list]


print(flatten([1, [2, 1, [3, 6, 7]], [1, 2, [3, 2, 3], 4, [1, 2, 3, 4, 5]]]))
#[1, 2, 1, 3, 6, 7, 1, 2, 3, 2, 3, 4, 1, 2, 3, 4, 5]
于 2012-09-18T14:46:54.177 に答える