Python.use(better)《余録》組み込み関数 filter

記事一覧 Python.use(better)《Python3.1》

《余録》組み込み関数 filter

《著》小粒ちゃん+∞《監修》小泉ひよ子とタマゴ倶楽部
第0版♪2001/03/02 ● 第1版♪2003/05/25 ● 第2版♪2004/06/01 ● 第3版♪2009/02/28

さまざまな事例

簡単な事例を使って、組み込み関数 filter の理解を深めます。

《Note》Smalltalk-80 セミナー用に作成した課題を、Python で再構成しました。この課題では「コレクションを扱う基本的なプロトコル(collect:/select:/reject:/detect:/inject:into:)を利用する」という制約を課してあるので、問題解決のための「最適解」を示すものではありません。

組み込み関数 filter

$ python3.1
Python 3.1 (r31:73578, Jun 27 2009, 21:49:46) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> print(filter.__doc__)
filter(function or None, iterable) --> filter object

Return an iterator yielding those items of iterable for which function(item)
is true. If function is None, return the items that are true.
>>> 

関数 function を、iterables の各要素に適用した結果を列挙する、イテレーターを生成します。

  • function に None を指定すると、iterables の各要素で true と評価されたものを列挙する、イテレーターを生成します。
■ 事例:リストの内包
>>> [e for e in "ABC" if ord(e)%2]
['A', 'C']

文字列 "ABC" の各要素 e(長さ1の文字列)の中から、if に続く条件式を満たす要素を列挙した、リストが得られます。リストの内包と同等の結果は、次のように、組み込み関数 filter でも得られます。

>>> filter(lambda e:ord(e)%2, "ABC")
<filter object at 0xeed10>
>>> list(_)
['A', 'C']

文字列 "ABC" の各要素 e の中から、if に続く条件式を満たす要素を列挙する、イテレーターが得られます。そこで、これをもとにリスト list を生成します。

■ 事例:第1引数に None を指定する
>>> [e for e in range(6) if e%3]
[1, 2, 4, 5]

数列 range(6) の各要素 e(0 から 5 までの整数)の中から、if に続く条件式を満たす要素を列挙した、リストが得られます。すると、次のように、

>>> filter(None, [e%3 for e in range(6)])

>>> list(_)
[1, 2, 1, 2]

第1引数に None を指定すると、数列 range(6) の各要素 e(0 から 5 までの整数)の中から、式 e%3 の値が True になる(3 の倍数でない)要素を列挙した、リストが得られます。

課題:九九の表〔filter 版〕

組み込み関数 filter を用いて、掛算の九九の表を作成します。*1

def multiplicationTable():
    for n in range(2, 10):
        for e in filter(lambda e: not e%n, range(n, n*10)):
            print("%2d"%e, end=" ")
        print()
>>> multiplicationTable()
 2  4  6  8 10 12 14 16 18
 3  6  9 12 15 18 21 24 27
 4  8 12 16 20 24 28 32 36
 5 10 15 20 25 30 35 40 45
 6 12 18 24 30 36 42 48 54
 7 14 21 28 35 42 49 56 63
 8 16 24 32 40 48 56 64 72
 9 18 27 36 45 54 63 72 81

filter を使って、任意の数 n の倍数を残してふるいに掛けます。

事例:エラトステネスのふるい(素数

任意の数n以下の素数が得られます *2。filter(フィルター:ふるい)という言葉から、すぐに思い浮かぶのが「エラトステネスのふるい」です。

def prime(n):
    return reduce(
        lambda acc, n: filter(lambda e: e==n or e%n, acc),
        range(2, int(sqrt(n))+1),
        range(2, n+1))
>>> prime(100)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

reduce を使って累積される情報は、反復処理ごとにその要素が減ってもかまいません。filter を使って n の倍数をふるいに掛けると、最後に素数だけが残ります。

課題:OCL/Sequence.select

《Note》OCL では、次のような操作が規定されています。

select(expression: OclExpression): Sequence(T)
source->select(iterator | body) = 
  source->iterate(iterator; result: Sequence(T) = Sequence{} | 
    if body then
      result->including(iterator) 
    else
      result 
    endif)


これを見ると、iterate 操作に関わる部分を除くすべてが、Sequence で規定されたプロトコル(Template パターン)に従うことが分かります。そのため、Sequence の傘下では、iterate 操作を実現するだけで(その操作を最適化したいときなどを除いて)select 操作を再定義する必要はありません。□

OCL/Sequence で規定された select 操作に準拠した機能を実現します。

class OCL_Sequence(OCL_Collection):
    def __init__(self, items):
        self.items = items
    def select(self, f):
        return [e for e in self.items if f(e)]
>>> OCL_Sequence("ABC").select(lambda e: ord(e)%2) 
['A', 'C']
>>> OCL_Sequence(range(10)).select(lambda e: e>5) 
[6, 7, 8, 9]
>>> OCL_Sequence(range(10)).select(lambda e: e%2) 
[1, 3, 5, 7, 9]

OCL_Sequence をラッパーと見なすと、Python に組み込みのシーケンスに対するメソッドとして、任意の条件処理を記述できます。Smalltalk/select: のような汎用性はありませんが、軽微な問題解決を図りたいときには重宝します。

》こちらに移動中です《
TOP


関連記事

Last updated♪2009/11/04

*1:Smalltalk 3分クッキング《問6》九九の表, 1988.

*2:Smalltalk 3分クッキング《問3》エラトステネスのふるい, 1988.