2017-04-03 15 views
3

におけるif文のメソッドをキャプチャ:リスト内包の上は、私は以下のユースケース持っているリスト内包

[(x, f(x)) for x in list_x if f(x) == cond(x)] 

を、私が思うに、F(x)は二回呼び出すのですか?どのようにf(x)が1回だけ呼び出されるように、それを回避してf(x)の値を取得するのですか?

私は、上のリストの理解をforループに変換するのは簡単だと思いますが、リストの理解を使って効率的に行うことができるのか不思議です。

答えて

6

最初のリストにfを適用することができますあなたは一度だけの関数を呼び出すために、ネストされたジェネレータ式を使用することができます。

[(x, fx) for (x, fx) in ((x, f(x)) for x in list_x) if fx == cond(x)] 

ジェネレータ式をロックステップで反復して、リストの理解のための(x, fx)タプルを生成します。

あなたが読者にこれが簡単に見つけた場合、あなたが最初に別の名前にジェネレータ式を分割することができます:再反復ここまで

mapped_x = ((x, f(x)) for x in list_x) 
filtered_x = [(x, fx) for (x, fx) in mapped_x if fx == cond(x)] 

:ジェネレータ式がゆったりと実行されます。式のforループは、for ... in mapped_xループの各ステップごとに段階的に進められます。

デモ:f(x)は一度だけ呼び出され、状態がすぐにチェックされているか

>>> list_x = range(5) 
>>> f = lambda x: print('f({!r})'.format(x)) or (x ** 2 - 1) 
>>> cond = lambda x: print('cond({!r})'.format(x)) or x % 2 == 0 
>>> mapped_x = ((x, f(x)) for x in list_x) 
>>> [(x, fx) for (x, fx) in mapped_x if fx == cond(x)] 
f(0) 
cond(0) 
f(1) 
cond(1) 
f(2) 
cond(2) 
f(3) 
cond(3) 
f(4) 
cond(4) 
[(1, 0)] 

注意。

この効率は、f(x)コールのコストに依存します。ジェネレータ式は別の関数フレームとして実行され、インタプリタは2つのフレームを切り替えます(リスト内包のためのループもフレームオブジェクトです)。

f(x)がPython関数の場合、作成された関数フレームオブジェクトの数が半分になります(それぞれf(x)呼び出しでフレームオブジェクトが作成され、これらの作成は比較的コストがかかります)。 C関数の場合、予想されるリストサイズの方が速いかどうかを確認するには、timeit moduleでいくつかの試行を作成する必要があります。

1

あなたが二回fを呼び出すにしたくない場合は、

[a for a in map(lambda x: (x, f(x)), list_x) if a[1] == cond(x)]

+0

だけでなく 'filter'を使用していないなぜあなたは' map'を使用している場合?... – Julien

+0

あなたは 'lambda'を使用する(作成と飛び出る大幅にこれを遅くすることを答え –

+0

注意のバージョンを提供するために歓迎されています関数フレームオブジェクトは、リスト長Nに対してN回)。 –

1

フィルタはこれを行うためのきれいな方法です。渡された関数を評価し、trueを返す場合は要素を保持し、そうでない場合はnoを保持します。

print (list(filter(lambda x: x[1] == cond(x[0]), [(x, f(x)) for x in list_x]))) 
関連する問題