web-dev-qa-db-ja.com

Python-ループ内の前後の値

このようなことをPythonで行うにはどうすればよいですか?

foo = somevalue
previous = next = 0

for (i=1; i<objects.length(); i++) {
    if (objects[i]==foo){
        previous = objects[i-1]
        next = objects[i+1]
    }
}
64
dir01

これでうまくいくはずです。

foo = somevalue
previous = next_ = None
l = len(objects)
for index, obj in enumerate(objects):
    if obj == foo:
        if index > 0:
            previous = objects[index - 1]
        if index < (l - 1):
            next_ = objects[index + 1]

enumerate 関数のドキュメントを次に示します。

81
Hank Gay

これまでのソリューションはリストのみを扱い、ほとんどはリストをコピーしています。私の経験では、それは不可能です。

また、リスト内で要素を繰り返し使用できるという事実を扱っていません。

質問のタイトルには「ループ内の前の値と次の値」と書かれていますが、ここでほとんどの回答をループ内で実行すると、最終的には各要素でリスト全体をもう一度繰り返して検索します。

だから私はちょうどその関数を作成しました。 itertools モジュールを使用して、反復可能オブジェクトを分割およびスライスし、前の要素と次の要素を一緒に含むタプルを生成します。あなたのコードが正確に何をするのかはわかりませんが、問題を解決できる可能性があるため、一見する価値があります。

from itertools import tee, islice, chain, izip

def previous_and_next(some_iterable):
    prevs, items, nexts = tee(some_iterable, 3)
    prevs = chain([None], prevs)
    nexts = chain(islice(nexts, 1, None), [None])
    return izip(prevs, items, nexts)

次に、ループで使用すると、前のアイテムと次のアイテムが含まれます。

mylist = ['banana', 'orange', 'Apple', 'kiwi', 'tomato']

for previous, item, nxt in previous_and_next(mylist):
    print "Item is now", item, "next is", nxt, "previous is", previous

結果:

Item is now banana next is orange previous is None
Item is now orange next is Apple previous is banana
Item is now Apple next is kiwi previous is orange
Item is now kiwi next is tomato previous is Apple
Item is now tomato next is None previous is kiwi

任意のサイズのリスト(リストをコピーしないため)および反復可能なもの(ファイル、セットなど)で動作します。この方法で、シーケンスを繰り返し処理し、ループ内で前のアイテムと次のアイテムを使用できます。シーケンス内のアイテムを再度検索する必要はありません。

コードの簡単な説明:

  • teeは、入力シーケンスに対して3つの独立した反復子を効率的に作成するために使用されます
  • chainは、2つのシーケンスを1つにリンクします。ここでは、単一要素のシーケンス[None]prevsに追加するために使用されます
  • isliceは、最初の要素を除くすべての要素のシーケンスを作成するために使用され、chainは、最後にNoneを追加するために使用されます
  • 次のようなsome_iterableに基づく3つの独立したシーケンスがあります。
    • prevsNone, A, B, C, D, E
    • itemsA, B, C, D, E
    • nextsB, C, D, E, None
  • 最後に、izipを使用して、3つのシーケンスを1つのトリプレットのシーケンスに変更します。

izipは入力シーケンスがなくなると停止するため、prevsの最後の要素は無視されることに注意してください。これは正しいです。最後の要素がprevprevsから最後の要素を取り除こうとすることもできますが、izipの動作により冗長になります

teeizipislice、およびchainitertoolsモジュールに由来することにも注意してください。入力シーケンスをオンザフライで(怠iに)操作するため、効率的になり、いつでも一度にシーケンス全体をメモリに保持する必要がなくなります。

python 3では、izipのインポート中にエラーが表示されます。Zipの代わりにizipを使用できます。 Zipをインポートする必要はありません。python 3で事前定義されています- source

125
nosklo

リスト内包表記を使用して、現在の要素、前の要素、および次の要素を持つ3タプルを返します。

three_Tuple = [(current, 
                my_list[idx - 1] if idx >= 1 else None, 
                my_list[idx + 1] if idx < len(my_list) - 1 else None) for idx, current in enumerate(my_list)]
6
RYS

python> = 2.5の簡潔さのために条件式を使用する

def prenext(l,v) : 
   i=l.index(v)
   return l[i-1] if i>0 else None,l[i+1] if i<len(l)-1 else None


# example
x=range(10)
prenext(x,3)
>>> (2,4)
prenext(x,0)
>>> (None,2)
prenext(x,9)
>>> (8,None)
2
makapuf

以下は、境界エラーのないジェネレーターを使用したバージョンです。

def trios(input):
    input = iter(input) # make sure input is an iterator
    try:
        prev, current = input.next(), input.next()
    except StopIteration:
        return
    for next in input:
        yield prev, current, next
        prev, current = current, next

def find_prev_next(objects, foo):
    prev, next = 0, 0
    for temp_prev, current, temp_next in trios(objects):
        if current == foo:
            prev, next = temp_prev, temp_next
    return prev, next

print find_prev_next(range(10), 1)
print find_prev_next(range(10), 0)
print find_prev_next(range(10), 10)
print find_prev_next(range(0), 10)
print find_prev_next(range(1), 10)
print find_prev_next(range(2), 10)

境界の動作は、コードとは異なり、最初または最後の要素で「foo」を探すことはないことに注意してください。繰り返しますが、境界セマンティクスは奇妙です...そしてあなたのコードから推測するのは難しいです:)

2
moshez

組み込み関数のみを使用し、他のオフセットに簡単に拡張できるため、これがどのようにまだ現れていないのかわかりません:

values = [1, 2, 3, 4]
offsets = [None] + values[:-1], values, values[1:] + [None]
for value in list(Zip(*offsets)):
    print(value) # (previous, current, next)

(None, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, None)
1
Eric Czech

これに対する解決策を探している人でも、要素を循環させたい場合は、以下がうまくいくかもしれません-

from collections import deque  

foo = ['A', 'B', 'C', 'D']

def prev_and_next(input_list):
    CURRENT = input_list
    PREV = deque(input_list)
    PREV.rotate(-1)
    PREV = list(PREV)
    NEXT = deque(input_list)
    NEXT.rotate(1)
    NEXT = list(NEXT)
    return Zip(PREV, CURRENT, NEXT)

for previous_, current_, next_ in prev_and_next(foo):
    print(previous_, current_, next)
1
skr47ch

ジェネレーターを使用すると、非常に簡単です。

signal = ['→Signal value←']
def pniter( iter, signal=signal ):
    iA = iB = signal
    for iC in iter:
        if iB is signal:
            iB = iC
            continue
        else:
            yield iA, iB, iC
        iA = iB
        iB = iC
    iC = signal
    yield iA, iB, iC

if __== '__main__':
    print('test 1:')
    for a, b, c in pniter( range( 10 )):
        print( a, b, c )
    print('\ntest 2:')
    for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 3:')
    cam = { 1: 30, 2: 40, 10: 9, -5: 36 }
    for a, b, c in pniter( cam ):
        print( a, b, c )
    for a, b, c in pniter( cam ):
        print( a, a if a is signal else cam[ a ], b, b if b is signal else cam[ b ], c, c if c is signal else cam[ c ])
    print('\ntest 4:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 5:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']):
        print( a, b, c )
    print('\ntest 6:')
    for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ):
        print( a, b, c )

シグナル値のチェックでは「is」を使用し、シグナルはPythonはインターンしない値です。ただし、任意のシングルトンマーカー値を信号として使用できます。これにより、状況によってはユーザーコードが簡素化される場合があります。

0
Victoria

リストでindexを使用してsomevalueの場所を見つけ、必要に応じて前と次を取得できます。


def find_prev_next(elem, elements):
    previous, next = None, None
    index = elements.index(elem)
    if index > 0:
        previous = elements[index -1]
    if index < (len(elements)-1):
        next = elements[index +1]
    return previous, next


foo = 'three'
list = ['one','two','three', 'four', 'five']

previous, next = find_prev_next(foo, list)

print previous # should print 'two'
print next # should print 'four'

0
John Montgomery

私の知る限り、これはかなり速いはずですが、私はそれをテストしませんでした:

def iterate_prv_nxt(my_list):
    prv, cur, nxt = None, iter(my_list), iter(my_list)
    next(nxt, None)

    while True:
        try:
            if prv:
                yield next(prv), next(cur), next(nxt, None)
            else:
                yield None, next(cur), next(nxt, None)
                prv = iter(my_list)
        except StopIteration:
            break

使用例:

>>> my_list = ['a', 'b', 'c']
>>> for prv, cur, nxt in iterate_prv_nxt(my_list):
...    print prv, cur, nxt
... 
None a b
a b c
b c None
0
Sfisol