1

Pythonで関数型プログラミングを試しているときに、同じ結果になるはずの2つの式の違いに気づきました。

特に、私がしたいのは、他のイテラブルで構成されている(または、利回りと言うべきですか?)イテラブルを持つことです。私がやりたいことの簡単な例は次のとおりです。

import itertools as itr
itr.repeat(itr.repeat(1,5),3)

これは 3 つの iterable から構成される iterable であり、それ自体が 1 の 5 回のオカレンスで構成されています。しかし、これは起こることではありません。代わりに(リストに変換されて)得られるものは次のとおりです。

[[1,1,1,1,1],[],[]]

つまり、最も内側の iterable はコピーされません (そのように見えます) 代わりに、同じ iterable が何度も使用され、要素が不足します。

マップを使用して機能するこれのバージョンは次のとおりです。

import itertools as itr
map(lambda x: itr.repeat(1,5), range(3))

これにより、私が期待する結果が得られます。

[[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]]

繰り返しのみを使用する方法では機能しないのに、なぜこれが機能するのかわかりません。たぶん、マップ バージョンでは iterable がrepeatlambda でラップされているという事実と関係がありますが、違いはあるのでしょうか? 私が見る限り、 と の唯一の違いlambda x: itr.repeat(1,5)itr.repeat(1,5)、最初のものは引数を取り (その後破棄します)、もう 1 つは引数を取りません。

4

5 に答える 5

3

ご指摘のとおり、itertools.repeat()繰り返されるアイテムをコピーするのではなく、毎回同じ iterable が使用されます。

map()の各要素に対してラムダ関数が個別に呼び出されるため、このメソッドは機能しrange(3)ます。そのため、ネストされたコンテンツを生成するために 3 つの個別itertools.repeat(1, 5)の iterable を取得します。

これを完全に itertools で行うには、次を使用しますitertools.tee

import itertools as itr
itr.tee(itr.repeat(1, 5), 3)

結果をリストとして表示する例を次に示します。

>>> [list(x) for x in itr.tee(itr.repeat(1, 5), 3)]
[[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]
于 2013-09-23T19:42:14.137 に答える
2

違いは、最初の引数としてオブジェクトitertools.repeatを取り、反復すると同じオブジェクトが複数回生成されることです。この場合、そのオブジェクトは使い果たされる前に 1 回しか反復できないため、表示される結果になります。

map呼び出し可能なオブジェクトを最初の引数として取り、そのオブジェクトを複数回呼び出し、そのたびに結果を生成します。

したがって、最初のコード スニペットでは、15 回生成されるオブジェクトは 1 つだけです。2 番目のスニペットにはラムダ オブジェクトが 1 つありますが、呼び出されるたびに新しいジェネレータ オブジェクトが作成され、15 回生成されます。

必要なものを取得するには、通常、次のいずれかを記述します。

(itr.repeat(1,5) for _ in range(3))

複数の15 回ジェネレーターを取得するには、または:

itr.repeat(tuple(itr.repeat(1,5)),3)

からの戻りとは異なり、タプルはitr.repeat繰り返し反復できるためです。

もちろん、この例は小さいので、ジェネレーターのことは忘れて、次のように書くこともできます。

((1,)*5,)*3

これは簡潔ですが、少しあいまいです。

あなたの問題は、次の違いに似ています。

# there is only one inner list
foo = [[]] * 3
foo[0].append(0)
foo
# [[0], [0], [0]]

# there are three separate inner lists
bar = [[] for _ in range(3)]
bar[0].append(0)
bar
# [[0], [], []]
于 2013-09-23T20:13:19.037 に答える
2

イテレータを 3 回繰り返しています。1 回目で使い果たされたので、2 回目と 3 回目の繰り返しでは何もしません。もう終わりです。

(ラムダの呼び出しを介して) 3 つの個別の反復子オブジェクトをmap()作成するため、これは発生しません。

于 2013-09-23T19:42:14.457 に答える
0

あなたの直感は正しいです。問題はrepeat、オブジェクトのコピーではなく、同じオブジェクトを生成し続けるジェネレーターを提供することです。Generator オブジェクトは 1 回だけ反復できます。繰り返しの次のアイテムが生成されるたびに、ジェネレーターから永久に破棄されます。

lambda x: itr.repeat(1,5)との違いは、 codedataitr.repeat(1,5)の違いです。「生の」呼び出しを渡すと、既に実行されてジェネレーター オブジェクトが返されており、渡されるのはジェネレーター オブジェクトです。ラムダを渡すと、まだ実行されていない関数内のコードであり、渡されるのは関数です。ラムダが呼び出されると、呼び出しが評価されてジェネレーターが返されます。これは、ラムダが呼び出されるたびに発生するため、毎回新しいジェネレーターを取得します。repeatitr.repeat(1,5)repeat

mapオブジェクトを取得するために一度呼び出してからそのオブジェクトを毎回使用するのではなく、コレクションの各要素に対してその引数関数を呼び出すため、個別の独立したジェネレータ オブジェクトが取得されます。repeat最初に与えたオブジェクトを繰り返し生成するだけなので、単一のジェネレーターオブジェクトへの複数の参照を取得します。

これは基本的に、これら 2 つのスニペットの違いと同じです。

a = itr.repeat(1, 5)
b = itr.repeat(1, 5)

a = itr.repeat(1, 5)
b = a

一度呼び出しrepeatてから結果のオブジェクトを渡すと、ジェネレーターは 1 つしかなく、渡した場所のいずれかからそれを消費し、それらすべての場所から表示されます。repeat複数回呼び出すと、複数の独立したジェネレーターができます。

于 2013-09-24T05:34:58.160 に答える