« ^ »

イテラブルなオブジェクトから指定した条件に一致するn番目の要素を1つだけ取得する

所要時間: 約 2分

イテラブルなオブジェクトから指定した条件に一致する先頭の要素を1つだけ取得してみます。 elementsは2〜9までの整数のリストです。

>>> elements = list(range(2, 10))

この中から最小の9の約数を取得します。

>>> next(filter(lambda n: 9 % n == 0, elements))
3

取得できました。filter()はfilter objectを返します。これはイテラブルなオブジェクトです。

>>> filter(lambda n: 9 % n == 0, elements)
<filter object at 0x10fcfab38>
>>>

next()関数はイテラブルオブジェクトの次に取得できる要素を返します。

条件に合う要素がない場合

条件に合う要素がない場合はどうでしょうか?先ほどは9を指定しましたが今度は素数である11を指定します。

>>> next(filter(lambda n: 11 % n == 0, elements))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

StopIteration例外が送出されました。

条件に合う要素がない場合に例外が発生するならtry…except句を使って例外処理しなければなりません。多くの場合は条件に合うものがなければ指定した値を返してくれると嬉しいです。next()はその値を第二引数に指定できます。以下では条件に合うものがなければNoneを返すようにしています。

>>> next(filter(lambda n: 11 % n == 0, elements), None)
>>>

先頭の要素ではなくn番目の要素を取得する

整数の問題に〜のn番目の数というものがあります。例えば7は4番目の奇数です。4番目を指定して取得することはできるでしょうか。

>>> next(filter(lambda n: 11 % n == 0, range(1, 10)), None, 4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: next expected at most 2 arguments, got 3

流石にnext()はだめなようです。イテレータの処理などはitertoolsに関数が集まっています。itertools.islice()はイテレータをスライスできるので使えそうです。

>>> import itertools
>>> next(itertools.islice(filter(lambda n: n % 2 == 1, range(1, 10)), 4, None), None)
9

取れましたね。itertools.islice()でイテレータを4番目以降でスライスして、スライス後のイテレータの先頭の要素をnext()で取得しています。