5

次のようにシミュレートするタプルジェネレーターがあるとしましょう。

g = (x for x in (1,2,3,97,98,99))

この特定のジェネレーターについて、次を出力する関数を書きたいと思います。

(1,2,3)
(2,3,97)
(3,97,98)
(97,98,99)
(98,99)
(99)

そのため、最後に近づいたときを除いて、一度に 3 つの連続した項目を反復して印刷しています。

関数の最初の行は次のようになります。

t = tuple(g)

言い換えれば、タプルを直接操作するのが最善なのか、それともジェネレーターを操作するのが有益なのかということです。両方の方法を使用してこの問題にアプローチできる場合は、両方のアプローチの利点と欠点を述べてください。また、ジェネレーター アプローチを使用することが賢明である場合、そのようなソリューションはどのように見えるでしょうか?

これが私が現在行っていることです:

def f(data, l):
    t = tuple(data)
    for j in range(len(t)):
        print(t[j:j+l])

data = (x for x in (1,2,3,4,5))
f(data,3)

更新

ウィンドウの長さを指定する 2 番目の引数を取るように関数を更新したことに注意してください。

4

7 に答える 7

3

実際、itertoolsモジュールにはこれのための関数があります - tee()izip_longest() :

>>> from itertools import izip_longest, tee
>>> g = (x for x in (1,2,3,97,98,99))
>>> a, b, c = tee(g, 3)
>>> next(b, None)
>>> next(c, None)
>>> next(c, None)
>>> [[x for x in l if x is not None] for l in izip_longest(a, b, c)]
[(1, 2, 3), (2, 3, 97), (3, 97, 98), (97, 98, 99), (98, 99), (99)]

ドキュメントから:

Return n independent iterators from a single iterable. Equivalent to:

def tee(iterable, n=2):
    it = iter(iterable)
    deques = [collections.deque() for i in range(n)]
    def gen(mydeque):
        while True:
            if not mydeque:             # when the local deque is empty
                newval = next(it)       # fetch a new value and
                for d in deques:        # load it to all the deques
                    d.append(newval)
            yield mydeque.popleft()
    return tuple(gen(d) for d in deques)
于 2013-09-20T10:45:07.980 に答える
3

3 つのアイテムを返す具体的な例は次のとおりです。

def yield3(gen):
    b, c = gen.next(), gen.next()
    try:
        while True:
            a, b, c = b, c, gen.next()
            yield (a, b, c)
    except StopIteration:
        yield (b, c)
        yield (c,)


g = (x for x in (1,2,3,97,98,99))
for l in yield3(g):
    print l
于 2013-09-20T10:38:28.737 に答える
2

一度に 3 つ以上の要素を取得する必要があり、ジェネレーター全体をメモリにロードしたくない場合は、標準ライブラリdequeのモジュールの a を使用して、現在のアイテムのセットを格納することをお勧めします。collectionsA deque(「デッキ」と発音し、「両端キュー」を意味する) は、両端から効率的に値をプッシュおよびポップできます。

from collections import deque
from itertools import islice

def get_tuples(gen, n):
    q = deque(islice(gen, n))   # pre-load the queue with `n` values
    while q:                    # run until the queue is empty
        yield tuple(q)          # yield a tuple copied from the current queue
        q.popleft()             # remove the oldest value from the queue
        try:
            q.append(next(gen)) # try to add a new value from the generator
        except StopIteration:
            pass                # but we don't care if there are none left
于 2013-09-20T10:55:34.800 に答える
1

実際には依存します。

ジェネレーターは、必要な結果を得るためにすべてをメモリに格納する必要がない非常に大きなコレクションの場合に役立つ場合があります。一方、印刷する必要があるのは、コレクションが膨大ではないことを推測しても問題ないように思われるため、違いはありません。

しかし、これはあなたが探していたものを達成するジェネレータです

def part(gen, size):
    t = tuple()
    try:
        while True:
        l = gen.next()
        if len(t) < size:
            t = t + (l,)
            if len(t) == size:
                yield t
            continue
        if len(t) == size:
            t = t[1:] + (l,)
            yield t
            continue
    except StopIteration:
        while len(t) > 1:
        t = t[1:]
        yield t

>>> a = (x for x in range(10))
>>> list(part(a, 3))
[(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7), (6, 7, 8), (7, 8, 9), (8, 9), (9,)]
>>> a = (x for x in range(10))
>>> list(part(a, 5))
[(0, 1, 2, 3, 4), (1, 2, 3, 4, 5), (2, 3, 4, 5, 6), (3, 4, 5, 6, 7), (4, 5, 6, 7, 8), (5, 6, 7, 8, 9), (6, 7, 8, 9), (7, 8, 9), (8, 9), (9,)]
>>> 

注: コードは実際にはあまりエレガントではありませんが、たとえば 5 つに分割する必要がある場合にも機能します。

于 2013-09-20T10:39:29.210 に答える
1

すべてをメモリに保持する必要がないため、ジェネレーターを使用するのが間違いなく最善です。

両端キューを使用すると、非常に簡単に実行できます。

from collections import deque
from itertools import islice

def overlapping_chunks(size, iterable, *, head=False, tail=False):
    """
    Get overlapping subsections of an iterable of a specified size.

        print(*overlapping_chunks(3, (1,2,3,97,98,99)))
        #>>> [1, 2, 3] [2, 3, 97] [3, 97, 98] [97, 98, 99]

    If head is given, the "warm up" before the specified maximum
    number of items is included.

        print(*overlapping_chunks(3, (1,2,3,97,98,99), head=True))
        #>>> [1] [1, 2] [1, 2, 3] [2, 3, 97] [3, 97, 98] [97, 98, 99]

    If head is truthy, the "warm up" before the specified maximum
    number of items is included.

        print(*overlapping_chunks(3, (1,2,3,97,98,99), head=True))
        #>>> [1] [1, 2] [1, 2, 3] [2, 3, 97] [3, 97, 98] [97, 98, 99]

    If tail is truthy, the "cool down" after the iterable is exhausted
    is included.

        print(*overlapping_chunks(3, (1,2,3,97,98,99), tail=True))
        #>>> [1, 2, 3] [2, 3, 97] [3, 97, 98] [97, 98, 99] [98, 99] [99]
    """

    chunker = deque(maxlen=size)
    iterator = iter(iterable)

    for item in islice(iterator, size-1):
        chunker.append(item)

        if head:
            yield list(chunker)

    for item in iterator:
        chunker.append(item)
        yield list(chunker)

    if tail:
        while len(chunker) > 1:
            chunker.popleft()
            yield list(chunker) 
于 2013-09-20T10:46:11.353 に答える
0

Python 2.7.17 と 3.8.1 の両方で動作するジェネレータを次に示します。内部的には可能な限りイテレータとジェネレータを使用するため、比較的メモリ効率が良いはずです。

try:
    from itertools import izip, izip_longest, takewhile
except ImportError:  # Python 3
    izip = zip
    from itertools import zip_longest as izip_longest, takewhile

def tuple_window(n, iterable):
    iterators = [iter(iterable) for _ in range(n)]
    for n, iterator in enumerate(iterators):
        for _ in range(n):
            next(iterator)
    _NULL = object()  # Unique singleton object.
    for t in izip_longest(*iterators, fillvalue=_NULL):
        yield tuple(takewhile(lambda v: v is not _NULL, t))

if __name__ == '__main__':
    data = (1, 2, 3, 97, 98, 99)
    for t in tuple_window(3, data):
        print(t)

出力:

(1, 2, 3)
(2, 3, 97)
(3, 97, 98)
(97, 98, 99)
(98, 99)
(99,)
于 2013-09-20T15:34:54.620 に答える
0

あなたが現在行っていることは、上記のどれよりもずっと簡単に思えると思います。特に複雑にする必要がなければ、シンプルに保つのが私の意見です。つまり、タプルを直接操作するのが最善です。

于 2013-09-20T10:55:13.407 に答える