2017-12-13 21 views
2

「評価時間の差異」の下で、今日の落ち着きが「this list」から抜け出しており、苦労しています。発電機の評価時間の差異を回避する

は私の問題の短いデモンストレーションとして、私はn[2..5]から行くと、すべてのn番目の番号をスキップ無限発電機を作る:

from itertools import count 

skip_lists = [] 
for idx in range(2, 5): 
    # skip every 2nd, 3rd, 4th.. number 
    skip_lists.append(x for x in count() if (x % idx) != 0) 

# print first 10 numbers of every skip_list 
for skip_list in skip_lists: 
    for _, num in zip(range(10), skip_list): 
     print("{}, ".format(num), end="") 
    print() 

予想される出力:

1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 
1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

実際の出力:

1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
私はその大きな特徴を思い出したら

、私はskip_listの一部となる定数にif句の変数を結合することによって、それを「解決」してみました:

from itertools import count 

skip_lists = [] 
for idx in range(2, 5): 
    # bind the skip distance 
    skip_lists.append([idx]) 
    # same as in the first try, but use bound value instead of 'idx' 
    skip_lists[-1].append(x for x in count() if (x % skip_lists[-1][0]) != 0) 

# print first 10 numbers of every skip_list 
for skip_list in (entry[1] for entry in skip_lists): 
    for _, num in zip(range(10), skip_list): 
     print("{}, ".format(num), end="") 
    print() 

しかし、再び:

1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

実際の解決策は別として、なぜ私のハックがうまくいかなかったのかを知りたいです。

答えて

3

idxの値は、最新のiteratee値であるidx = 4がモジュールスコープ内に存在する点で、ジェネレータの反復を開始するまで(発電機が遅延して評価されるまで)ルックアップされることはありません。 を関数に渡し、各ジェネレータの評価時間で関数スコープから値を読み取ることによって、各追加発電機をステートフルにすることができます。idxこれはジェネレータ式の反復可能なソースがジェネレータで評価されるという事実を利用しています。 EXPの作成時間、関数は、ループの各反復で呼び出され、idx安全にですので、関数スコープに離れて保存された:GENでジェネレータ式の反復可能なソース評価の

from itertools import count 

skip_lists = [] 

def skip_count(skip): 
    return (x for x in count() if (x % skip) != 0) 


for idx in range(2, 5): 
    # skip every 2nd, 3rd, 4th.. number 
    skip_lists.append(skip_count(idx)) 

イラスト。 EXPの作成:除外が実際に世代EXPの作成時に評価されていないフィルタで行われているので

>>> (i for i in 5) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'int' object is not iterable 

あなたの場合は少しトリッキーです:

>>> (i for i in range(2) if i in 5) 
<generator object <genexpr> at 0x109a0da50> 

より理由ループとフィルタはすべて、idxを格納するスコープに移動する必要があります。フィルタだけではありません。別のノートで


、あなたはジェネレータ式のスライス印刷に使用している非効率的なロジックの代わりにitertools.isliceを使用することができます。

from itertools import islice 

for skip_list in skip_lists: 
    for num in islice(skip_list, 10): 
     print("{}, ".format(num), end="") 
    print() 
+0

グレート答えは、私のコードは=動作するようになりました)しかし、私は 'skip_lists.append([idx])'に 'idx'を格納し、' idx'の代わりにそれにアクセスするのはなぜうまくいかないのかまだ分かりません。この値は最後の反復に変わらず、 'print(skip_list)'は結局、2番目の例では[[2、]、[3、]、[4、]]を返します。 – Arne

+0

ジェネレータ式のコードオブジェクトは、最初にループが完了した後に、評価しているときに 'idx'という名前から読み込みます。より多くの場合、そのループの後に 'idx'を表示してください。その時点での名前 'idx'はint 4を参照します。 –

+0

これは恐ろしいことです。 'skip_lists.append([lambda x idx:x])'はローカルスコープの参照を作成することさえできません。私にこれを解決する方法を教えてくれてありがとう。 – Arne

関連する問題