この記事は広告を含みます。
こんにちは。今日もPythonで関数型プログラミングっぽい書き方について、紹介してゆきます。
前回は「同じ入力なら同じ結果を返す純粋関数」や map、filter を解説したけど、、、
今回は「関数を引数に取ったり返したりする」高階関数
や、reduce、partial、関数合成といった技も扱っていきます。
1. 高階関数って何?
高階関数って、名前の通り「関数を受け取ったり返したりする関数」のこと。 たとえば、こんな関数を作るとする:
def do_twice(func, x): """ 引数に渡された関数 func を、値 x に対して2回連続で適用する """ return func(func(x))
ここでは、func はただの引数名です。Pythonには元から用意されてるわけじゃなくて、呼び出し時に渡される関数を受け取るための名前にすぎないです。
def add_one(n): return n + 1 result = do_twice(add_one, 5) # 5 -> 6 -> 7 になる
2. 関数を返す関数
関数を返す関数も作れる。たとえば、ある数を足す関数を動的に作り出すには:
def make_adder(n): """ n を足す関数を作って返す """ def adder(x): return x + n return adder add_five = make_adder(5) print(add_five(10)) # => 15
この方法だと、必要に応じて「足す数」を動的に変えられる。面白いですね。
3. reduce を使ってデータをまとめる
reduce はリストの全要素を1つの値にまとめたいときに便利。 たとえば、リストの合計を計算する例はこう:
from functools import reduce numbers = [1, 2, 3, 4, 5] sum_all = reduce(lambda x, y: x + y, numbers) # 結果は15
また、リストの最大値を求める場合もこんな感じで書ける:
numbers = [1, 7, 3, 9, 2] max_val = reduce(lambda x, y: x if x > y else y, numbers) # 結果は9
4. 部分適用で便利な関数を作る
functools.partial を使うと、一部の引数をあらかじめ固定した関数が作れる。 たとえば、累乗を計算する関数を部分適用で作ってみる
from functools import partial def power(base, exp): return base ** exp square = partial(power, exp=2) cube = partial(power, exp=3) print(square(5)) # => 25 print(cube(2)) # => 8
5. 関数合成で関数をつなげる
数学の関数合成のように、1つの関数の出力を別の関数の入力に使うテクニック。 まずは自前で書く方法を!
def f(x): return x + 1 def g(x): return x * 2 def compose(f, g): """ f のあとに g を実行する関数を返す (つまり、g(f(x)) を計算する関数) """ def composite_function(x): return g(f(x)) return composite_function gf = compose(f, g) print(gf(3)) # => 8 (3 + 1 = 4 で、4 * 2 = 8)
ちなみに、外部ライブラリの toolz を使うと、もっとスマートに書けるんだけど、今回は手作りの例で十分だと思う
6. まとめ
今回の章では、Pythonで関数型プログラミングっぽい書き方を深掘りしてみた。
- 高階関数:関数を引数に取ったり返したりする
- reduce:リストの全要素をまとめる
- partial:引数の一部を固定して新しい関数を作る
- 関数合成:関数同士をつなげて新たな処理を作る
どれも、コードをすっきりさせたり、バグを減らすための有用なテクニックな気がする。
コードまとめ
# 高階関数:関数を引数として使う例 def do_twice(func, x): """ 引数に渡された関数 func を、値 x に対して2回連続で適用する """ return func(func(x)) def add_one(n): return n + 1 result = do_twice(add_one, 5) print("do_twice result:", result) # 5 -> 6 -> 7 # 関数を返す関数の例 def make_adder(n): """ n を足す関数を作って返す """ def adder(x): return x + n return adder add_five = make_adder(5) print("make_adder result:", add_five(10)) # 10 + 5 => 15 # reduce を使った例 from functools import reduce numbers = [1, 2, 3, 4, 5] sum_all = reduce(lambda x, y: x + y, numbers) print("reduce sum_all:", sum_all) # 結果: 15 numbers = [1, 7, 3, 9, 2] max_val = reduce(lambda x, y: x if x > y else y, numbers) print("reduce max_val:", max_val) # 結果: 9 # 部分適用 (partial) の例 from functools import partial def power(base, exp): return base ** exp square = partial(power, exp=2) cube = partial(power, exp=3) print("square(5):", square(5)) # 結果: 25 print("cube(2):", cube(2)) # 結果: 8 # 関数合成の例 def f(x): return x + 1 def g(x): return x * 2 def compose(f, g): """ f のあとに g を実行する関数を返す (つまり、g(f(x)) を計算する関数) """ def composite_function(x): return g(f(x)) return composite_function gf = compose(f, g) print("compose result:", gf(3)) # 結果: 8 (3 + 1 = 4, 4 * 2 = 8)