Python の lambda の使い方

# はじめに
lambda と聞くと AWS の lambda を思い付くかと思いますが、
今回お話しするのは Python の無名関数を意味する lambda についてです。

# Python の lambda とは
ざっくり一言で表すと、def 文を使用せずに関数を定義できるものです。

def 文では以下のような構成になっているかと思います。

```.py
def function(引数):
    return 式
```

lambda では以下のように記述することができます。

```.py
function = lambda 引数: 式
```

# 使い方
この lambda の使い方について記述します。


## def 文
まずは lambda を使用せずに def 文でプログラムを作成したいと思います。
内容としては小文字、大文字のアルファベットが入ったリストを用意して、
`capitalize`関数を用いて、全て大文字に修正するといったものです。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

def change_words(words, func):
    for word in words:
        print(func(word))

def sample_func(word):
    return word.capitalize()

change_words(l, sample_func)
```

`change_words`関数では
引数には words と function を渡す func を設定しています。
中の処理では words を for ループで回してあげてその値を func() の中に入れています。
funcの定義にしたがって、 word を変更するといった内容になります。

`sample_func`関数では
word を引数にしていて、中では capitalize() という文字列の先頭を
大文字にするメソッドを使用して返すといった処理になっています。

実際に実行してみると全て大文字になって出力されることが確認できます。

```
A
B
C
D
E
F
G
```

## lambda
上記のプログラムを lambda を使用して記述してみます。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

def change_words(words, func):
    for word in words:
        print(func(word))

#def sample_func(word):
#    return word.capitalize()
sample_func = lambda word: word.capitalize()

change_words(l, sample_func)
```

このように一行で記述することが可能になります。
実行してみると同様の結果が出力されると思います。

## lambda式には名前を付けない方がいい

今回はわかりやすいように lambda の式に
`sample_func`と名前をつけましたが、

Python のコーディング規約PEP8では
名前を付けて関数を定義する場合はdefを使うべきと推奨されています。

そこで名前を付けずに、直接記述して関数として渡すことが可能です。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

def change_words(words, func):
    for word in words:
        print(func(word))

# def sample_func(word):
#    return word.capitalize()
# sample_func = lambda word: word.capitalize()

change_words(l, lambda word: word.capitalize())
```

この記述だと関数をいちいち定義せずに使用でき、
キレイなプログラムになると思います。

# より効果的に

より lambda の効果を実感するために
今度はアルファベットを小文字に修正する関数を追加してみます。

## def 文
まずは def 文で記述してみます。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

def change_words(words, func):
    for word in words:
        print(func(word))


def sample_func(word):
    return word.capitalize()

def sample_func2(word):
    return word.lower()

change_words(l, sample_func)

change_words(l, sample_func2)
```

`sample_func2`という名前で新たに関数を増やしました。

実際に実行してみると小文字のアルファベットも出力されます。

```
A
B
C
D
E
F
G
a
b
c
d
e
f
g
```

`sample_func2`関数の内容としては
`capitalize()`が`lower()`に変更しただけです。

しかし、いちいち関数を定義してから記述しなければならないのは面倒に感じます。

## lambda
そこで lambdaを使用して１行で記述した方が楽になります。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

def change_words(words, func):
    for word in words:
        print(func(word))

change_words(l, lambda word: word.capitalize())

change_words(l, lambda word: word.lower())
```

こちらを実行しても同じ結果が出力されます。

function を引数にするものは lambda を使用して()内で定義すれば
あえて def で関数を定義しなくてもよくなるため
とても効率のよいプログラムを記述することが可能になります。


# 追記
以下、コメントいただいた内容を記事に反映いたしました。

## map、filterと組み合わせよう
上記で説明した lambda を組み込み関数の map 、filter と合わせて
使うことでより効率的になります。

ここでは map 関数を使用してみます。

実際に map と組み合わせると
funcの定義にしたがって、 word を変更する内容の
`change_words`関数も def で定義する必要がなくなりました。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

change_words = map(lambda c: c.capitalize(), l)

print(list(change_words)
```

## なるべくリスト内包表記を使おう
ここまで lambda や map の扱い方を説明しましたが、
[Effective Python](https://www.oreilly.co.jp/books/9784873117560/)という本では、これらの代わりにリスト内包表記の使用を推奨しています。
（※15 ページの項目 7 に詳しく記載されています。）

以下がリスト内包表記を使用した場合のプログラムです。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

change_words = [c.capitalize() for c in l]

print(change_words)
```

著書の中ではリスト内包表記を薦める理由として、大きく二つ挙げられています。

### 一つ目は可読性です。

リスト内包表記では組み込み関数の map や filter に比べて 余分なlambda式を
必要としないため明確的であると記述されています。

確かに上記のプログラムを見てもリスト内包表記の方が
すっきりとして見えますし、記述しやすいかと思います。

### 二つ目にリスト内包表記の方が、入力リストから要素を抜き出すことが容易なことです。

例えばリストの中から大文字の`B`だけを出力させたい場合があるとします。

リスト内包表記ではループの後に条件文を付け加えるだけで可能になります。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

change_words = [c.capitalize() for c in l if c == 'b']

print(change_words)
```

しかし map の場合は filter の助けがないと記述ができません。

```.py
l = ['A', 'b', 'C', 'd', 'e', 'F', 'g']

change_words = map(lambda c: c.capitalize(), filter(lambda c: c == 'b', l))

print(list(change_words))
```

filter を使用すれば可能ですが、とても読みやすいプログラムとは言えません。

以上の観点から map や filter の代わりにリスト内包表記が良いとされています。

## おわりに
最初は lambda だけの案内でしたがコメントをいただき、
map や filter さらにはリスト内包表記に関することまで掘り下げることができました。
ありがとうございます。