map()とfilter()

map()とfilter()

map()

 map() はリストやタプルのすべての要素に同じ演算を適用する関数です。
 正確には [関数] と [イテラブルオブジェクト] を受け取って、[イテラブルオブジェクト] の各要素を関数に渡して各要素の戻り値を要素にもつイテレータを生成します。

map(関数, イテラブル)

 map() は Python の組み込み高階関数なので、モジュールをインポートせずにいつでも使えます。

map()の基本的な使い方

 map() を使って、1 から 9 までの数の平方数 (2 乗した数) のリストを作成してみましょう。

# PYTHON_MAP_01

# 数値を2乗する関数を定義
def square(x):
    return x ** 2

# map()を使って平方数のイテレータを作成
square_numbers = map(square, range(1, 10))

# イテレータをリストに変換
print(list(square_numbers))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

 map() の第 2 引数には Rangeオブジェクトを渡していますが、代わりに [1, 2, 3, 4, 5, 6, 7, 8, 9] というリストを渡しても同じ結果が得られます。map() の戻り値はイテレータなので、要素を一括して取得したい場合は、サンプルコードにあるようにリストに変換します。
 

lambda式とmap()

 lambda式 を使えば関数を定義する手間が省けます。
 コード MAP_01 を lambda式を使って書き換えると次のようになります。

# PYTHON_MAP_02

# map()を使って平方数のイテレータを作成
square_numbers = map(lambda x : x**2, range(1, 10))

# イテレータをリストに変換
print(list(square_numbers))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

 一般項がわかっている数列は map() で実装できます。
 たとえば、an = 2n - 5n + 1 で表される数列は以下のコードで生成できます。

# PYTHON_MAP_03

# 数列anを定義
an = lambda n : 2**n - 5*n + 1

# 数列anの初項から第9項までを格納したイテレータ
seq = map(an, range(1, 10))

# イテレータをリストに変換
print(list(seq))
[-2, -5, -6, -3, 8, 35, 94, 217, 468]

 引数に一般項の形 (関数オブジェクト) と項数を受け取って数列を生成する関数を定義してみます。

# PYTHON_MAP_04-1

# 数列関数
def sequence(an, k):
    seq = map(an, range(1, k+1))
    return list(seq)

 一般項が an = 5n - 2 で表される数列を初項から第 5 項まで並べてみましょう。

# PYTHON_MAP_04-2

# 数列anを定義
an = lambda n : 5*n - 2

# 初項から第5項までanを生成
seq = sequence(an, 5)

print(seq)
[3, 8, 13, 18, 23]

 

map()で数値と文字列を結合する

 数値と文字列が収められたリストの各要素を連結するときに、map()を使うと便利です。

# PYTHON_MAP_05

# 数値と文字列を要素にもつリスト
my_date = [2018, "年", 11, "月", 9, "日"]

# map()を使ってすべての要素を文字列に変換
my_date = map(str, my_date)

# 要素を結合
my_date = ''.join(my_date)

print(my_date)
2018年11月9日

 このコードでは map() に str() とリストを渡して、リストの要素をすべて文字列に変換してから、join()メソッドで各要素をつなげています。
 

アルゴリズム図鑑 絵で見てわかる26のアルゴリズム

中古価格
¥2,011から
(2019/7/30 20:26時点)

filter()

 filter() は関数とイテラブルオブジェクトを受け取って、イテラブルの要素のうち関数が True を返す要素を抜き出してイテレータを構築します。

filter(関数, イテラブル)

 たとえば、あるリストから 50 以上の数値だけを取り出してイテレータを生成するには次のようなコードを記述します。

# PYTHON_FILTER_01-1

# リストを作成
my_list = [12, 67, 45, 96, 58]

# リストの中から50以上の数字を抜き出してイテレータを生成
f = filter(lambda x : x > 50, my_list)

# イテレータから最初の数字を取得
print(next(f))
67

 同じようなことはリスト内包表記でも可能です。
 上のコードを内包表記で書き直すと次のようになります。

# FILTER_01-2

# リストを作成
my_list = [12, 67, 45, 96, 58]

# リストの中から50以上の数字を抜き出してイテレータを生成
new_list = [x for x in my_list if x > 50]

print(new_list)
[67, 96, 58]

 しかし、filter() はイテレータを、内包表記はリストを生成するという点が異なっています。大きなデータを扱う場合、イテレータのほうがメモリを節約できます。
 

filter()のサンプルコード

 filter() の応用例をいくつか載せておきます。

# PYTHON_FILTER_02-1

# 1~100の自然数の中から、12または19で割り切れる数を見つける

# 1~100までの数字のリストを作成
my_list = list(range(1, 101))

# 1~100のうち、12または19で割り切れる数を返すイテレータを作成
f = filter(lambda x : x % 12 == 0 or x % 19 == 0, my_list)

# イテレータからすべての要素を取り出す
print(list(f))
[12, 19, 24, 36, 38, 48, 57, 60, 72, 76, 84, 95, 96]

 

# FILTER_02-2
# リストから"S"を含む文字列を抽出

my_list = ["SML", "Haslkell", "Erlang", "Scheme", "Scala"]

# リストの中からSを含む要素を抜き出してイテレータを作成
f = filter(lambda x : x.find("S") != -1, my_list)

# イテレータからすべての要素を取り出す
print(list(f))
['SML', 'Scheme', 'Scala']

 str.find() は、指定した文字列の場所を返すメソッドですが、見つからない場合は -1 を返してきます。x.find("S") != -1 は「"S" が -1 でない」、すなわち「"S" が文字列のどこかに含まれる」を意味しています。