このようなことを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]
}
}
これでうまくいくはずです。
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
関数のドキュメントを次に示します。
これまでのソリューションはリストのみを扱い、ほとんどはリストをコピーしています。私の経験では、それは不可能です。
また、リスト内で要素を繰り返し使用できるという事実を扱っていません。
質問のタイトルには「ループ内の前の値と次の値」と書かれていますが、ここでほとんどの回答をループ内で実行すると、最終的には各要素でリスト全体をもう一度繰り返して検索します。
だから私はちょうどその関数を作成しました。 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つの独立したシーケンスがあります。prevs
:None, A, B, C, D, E
items
:A, B, C, D, E
nexts
:B, C, D, E, None
izip
を使用して、3つのシーケンスを1つのトリプレットのシーケンスに変更します。izip
は入力シーケンスがなくなると停止するため、prevs
の最後の要素は無視されることに注意してください。これは正しいです。最後の要素がprev
。 prevs
から最後の要素を取り除こうとすることもできますが、izip
の動作により冗長になります
tee
、izip
、islice
、およびchain
はitertools
モジュールに由来することにも注意してください。入力シーケンスをオンザフライで(怠iに)操作するため、効率的になり、いつでも一度にシーケンス全体をメモリに保持する必要がなくなります。
python 3
では、izip
のインポート中にエラーが表示されます。Zip
の代わりにizip
を使用できます。 Zip
をインポートする必要はありません。python 3
で事前定義されています- source
リスト内包表記を使用して、現在の要素、前の要素、および次の要素を持つ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)]
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)
以下は、境界エラーのないジェネレーターを使用したバージョンです。
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」を探すことはないことに注意してください。繰り返しますが、境界セマンティクスは奇妙です...そしてあなたのコードから推測するのは難しいです:)
組み込み関数のみを使用し、他のオフセットに簡単に拡張できるため、これがどのようにまだ現れていないのかわかりません:
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)
これに対する解決策を探している人でも、要素を循環させたい場合は、以下がうまくいくかもしれません-
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)
ジェネレーターを使用すると、非常に簡単です。
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はインターンしない値です。ただし、任意のシングルトンマーカー値を信号として使用できます。これにより、状況によってはユーザーコードが簡素化される場合があります。
リストで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'
私の知る限り、これはかなり速いはずですが、私はそれをテストしませんでした:
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