高階関数とラムダ式

INPUT · スライド

関数を受け取る関数を書く

01 / 07

ここまではずっと渡す側だった

この章でやってきたことを並べてみよう。

  • sorted(namaes, key=len) … 並べる基準を渡した
  • max(hitos, key=lambda h: h["tosi"]) … 比べる基準を渡した
  • map(nibai, kazus) … かける処理を渡した
  • filter(seisuu, kazus) … 残すかどうかの条件を渡した

どれも、もう用意されているものに関数を渡していた。渡した関数がいつ呼ばれるかは、中に隠れていて見えなかったね。

このレッスンでは立場を入れかえる。関数を受け取る側を自分で書くんだ。

02 / 07

引数で関数を受け取る

種明かしをすると、受け取る側は特別なことを何もしていない。受け取った引数にカッコを付けて呼ぶだけだよ。

引数はこれまで数や文字を受け取ってきた。関数も値だから、まったく同じように受け取れる。

下の tekiyou は、map とほとんど同じ仕事をしている。中で何が起きているか、これで全部見えるね。

def tekiyou(f, xs):    kekka = []    for x in xs:        kekka.append(f(x))    return kekkadef nibai(x):    return x * 2print(tekiyou(nibai, [1, 2, 3]))

実行結果

[2, 4, 6]

03 / 07

カッコを付けるか付けないか

ここでもういちど、この章でいちばん大事な区別を確認しよう。

  • nibai … 関数そのもの。持ち運べる
  • nibai(3) … 関数を動かした結果。ここでは 6

関数そのものを print すると、こうなるよ。

print(nibai)<function nibai at 0x1234abcd>

うしろの番地はやっぱり毎回変わる。読んでも意味は無いけれど、「これは関数だよ」と言われていることだけ分かればいい。

渡すときにうっかりカッコを付けると、関数ではなく呼んだ結果が渡ってしまう。f(x) のところで TypeError になるので、そのときはここを疑おう。

04 / 07

あとで何をするかは呼ぶ側が決める

tekiyou のいいところは、中身を1文字も変えずに、やることを変えられることだよ。

渡す関数を差しかえるだけで、2倍にもなるし、文字数を数えることにもなる。tekiyou 自身は「全部に何かをかける」という流れだけを持っていて、何をかけるかは知らないんだ。

これが「あとで何をするかは、呼ぶ側に決めてもらう」ということ。関数を部品として作るときの、いちばん強い考えかただよ。

def tekiyou(f, xs):    kekka = []    for x in xs:        kekka.append(f(x))    return kekkadef nibai(x):    return x * 2print(tekiyou(nibai, [1, 2, 3]))print(tekiyou(len, ["ゆい", "はると"]))print(tekiyou(lambda x: x + 1, [1, 2, 3]))

実行結果

[2, 4, 6]
[2, 3]
[2, 3, 4]

05 / 07

条件も同じように渡せる

渡すのは「作り変えかた」でなくてもいい。True か False を返す関数を渡せば、filter と同じものが作れるよ。

違いは f(x) を if の条件に使っているところだけ。受け取って呼ぶという骨格はまったく同じだね。

map も filter も魔法ではなかった。中では、あなたがいま書いたのと同じことをしているだけだよ。

def erabu(f, xs):    kekka = []    for x in xs:        if f(x):            kekka.append(x)    return kekkaprint(erabu(lambda x: x > 0, [3, -1, 5, -2]))

実行結果

[3, 5]

06 / 07

JavaScript でいうコールバック

JavaScript をさわったことがある人なら、これはまったく同じ話だと気づいたはず。

向こうでは、引数として渡される関数のことをコールバック関数と呼ぶ。forEach や map に渡していたあれだよ。呼びかたが違うだけで、やっていることは寸分たがわず同じなんだ。

Pythonでは「コールバック」という言葉はJavaScriptほど前に出てこない。そのかわり、関数を受け取ったり返したりする関数のことを高階関数と呼ぶ。sorted も map も filter も、そしていま書いた tekiyou も、全部が高階関数だよ。

07 / 07

第10章のまとめ

この章で身につけたことを並べておこう。

  • 関数は値。カッコを付けなければ関数そのもので、変数に入れられるし渡せる
  • ラムダ式で、その場かぎりの小さな関数を名前なしで作れる
  • key= に基準を渡すと、sorted や max や min の並べかた・比べかたを決められる
  • map と filter で、全部にかける・いるものだけ残すができる。ただし list を通すこと
  • そして、受け取る側も自分で書ける

「処理そのものを引数にできる」と分かると、関数の作りかたが変わる。流れだけを書いておいて、中身はあとで決めてもらう——そういう部品が作れるようになったよ。

次の章では、知っていると急に楽になる道具を集めて覚えよう。

namaes = ["みなと", "ゆい", "はると"]print(sorted(namaes, key=len))print(list(map(len, namaes)))print(list(filter(lambda n: len(n) == 3, namaes)))

実行結果

['ゆい', 'みなと', 'はると']
[3, 2, 3]
['みなと', 'はると']