高階関数とラムダ式

INPUT · スライド

全部に同じ処理をかける

01 / 07

もう内包表記でできている

「リストの中身を全部2倍にしたい」。第9章で、これは1行で書けるようになったね。

この章ではもうひとつの書きかたを覚えるよ。map(マップ)という組み込みの関数だ。

できることは内包表記とほとんど同じ。それでもわざわざ覚えるのは、他人の書いたコードに出てくるからだよ。書けなくてもいいけれど、読めないと困るんだ。

kazus = [1, 2, 3]print([x * 2 for x in kazus])

実行結果

[2, 4, 6]

02 / 07

map に関数を渡す

map には関数と回せるものの2つを渡す。

  • map(f, xs) … xs の中身ひとつずつに f をかける

渡すのは関数そのものなので、nibai にカッコを付けない。付けたら「呼んだ結果」を渡すことになってしまう。前のグループの key=len とまったく同じ話だね。

そして、返ってくるのはリストではない。map という専用の種類の値だよ。

def nibai(x):    return x * 2kazus = [1, 2, 3]m = map(nibai, kazus)print(type(m))

実行結果

<class 'map'>

03 / 07

そのまま表示しても中身が見えない

ここが map のいちばんの落とし穴。そのまま print しても中身は出てこない。

kazus = [1, 2, 3]print(map(nibai, kazus))<map object at 0x1234abcd>

うしろの番地は実行するたびに変わる。答え合わせには使えないし、そもそも読んでも何も分からないよね。

なぜこうなるかというと、map はまだ計算していないからだよ。第8章でやった「1つずつ配る係」——イテレータ——と同じで、頼まれてはじめて1つ作る。「これから2倍にしますよ」という予約票を受け取った状態なんだ。

04 / 07

list を通すと見える

予約票を実際の値に変えるには、list(...) を通す。中身を全部取り出してリストにしてくれるよ。

map を使ったら list で受ける。これをひとつの型として覚えてしまおう。表示するときも、あとでリストとして使いたいときも、通しておけば間違いがない。

def nibai(x):    return x * 2kazus = [1, 2, 3]print(list(map(nibai, kazus)))

実行結果

[2, 4, 6]

05 / 07

内包表記と並べて見る

同じことをする2つを並べてみよう。結果はまったく同じだよ。

list(map(nibai, kazus))
[nibai(x) for x in kazus]

どちらが読みやすいかは、正直なところ好みもある。ただPythonでは内包表記のほうが好まれることが多い。list で包む手間が要らないし、2倍くらいの処理なら lambda も def も書かずに x * 2 と直接書けるからね。

def nibai(x):    return x * 2kazus = [1, 2, 3]a = list(map(nibai, kazus))b = [nibai(x) for x in kazus]print(a)print(b)print(a == b)

実行結果

[2, 4, 6]
[2, 4, 6]
True

06 / 07

一度取り出すと空になる

もうひとつ、予約票であるがゆえの性質がある。一度取り出すと、2回目は空っぽだよ。

第8章でやったね。イテレータは配り終わると巻き戻せない。map もまったく同じ性質を持っているんだ。

だから結果を2回使いたいなら、先に list にして変数に入れておくこと。予約票のまま持ち回ると、2回目に空のリストが出てきて頭を抱えることになるよ。

def nibai(x):    return x * 2kazus = [1, 2, 3]m = map(nibai, kazus)print(list(m))print(list(m))

実行結果

[2, 4, 6]
[]

07 / 07

どちらを書けばいいか

自分で新しく書くなら、内包表記のほうをすすめる。list で包み忘れる事故も、2回目が空になる事故も起きないからね。

それでも map を覚えるのは、読めないと困るから。他の言語から来た人は map で書くことが多いし、ライブラリの中にも出てくる。

両方読めるようにしておく。それがこのレッスンの目標だよ。次は「全部にかける」ではなく「いるものだけ残す」ほうをやろう。