リストの中で条件にあう要素をリストの末尾から調べて最初に見つかったもののインデックスを得る修行
あるリストの中で特定の条件にあう要素のインデックスを知りたい。しかも、リストの末尾から調べて最初に見つかるものの。たとえば
>>> a = range(5,94,13) >>> a [5, 18, 31, 44, 57, 70, 83]
として、3 の倍数になっている要素は 1 番目 (最初の要素が 0 番目) の 18 と 4 番目の 57 だが、この 4 という値が欲しい。
enumerate() でインデックスをつけたイテレータを反転させようとしたのだが、reversed() はイテレータではなくて sequece を引数にとる模様。
>>> [(k,c) for k,c in reversed(enumerate(a))] Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: argument to reversed() must be a sequence
どうせリストの末尾までなめなければならないから、と、イテレータの反転はあきらめ、とりあえず次の泥臭い方法を採用している。
>>> k = -1 >>> for (j, v) in enumerate(a): ... if v % 3 == 0: k = j ... >>> k 4
少し考えて次のような方法にしてみた。
>>> [j for (j,v) in filter(lambda x:x[1]%3==0, enumerate(a))][-1] 4
これだと条件にマッチする要素が見つからなかったときは IndexError になる。
条件にマッチする要素が見つからなかったときには -1 が返るくらいの方が嬉しい。
>>> max([j for (j,v) in filter(lambda x:x[1]%3==0, enumerate(a))]) 4
これも条件にマッチする要素が見つからなかったときには ValueError。条件にマッチするインデックスのリストを一旦作っているけれど、それに -1 を入れられれば万事解決なのだが。
追記
reduce() + max() を使って一応できた。
>>> reduce(lambda u,v: max(u,v), [j for (j,v) in filter(lambda x:x[1]%3==0, enumerate(a))], -1) 4
max() じゃなくて三項演算子ならまだよさそうだけど、ちょっと遅い気がするなあ。そういうときは計れ、と。
>>> timeit.Timer('reduce(lambda u,v: max(u,v), [j for (j,v) in filter(lambda x:x[1]%3==0, enumerate(a))], -1)', 'a = range(5,94,13)').timeit() 6.8371469974517822 >>> timeit.Timer("""k0=1 ... for (k,v) in enumerate(a): ... if v % 3 == 0: k0 = 0 ... """, 'a = range(5,94,13)').timeit() 1.7457489967346191
やっぱり、泥臭い方法の方が速かった。