Pynote

Python、機械学習、画像処理について

Python - 関数の合成

試した環境

関数の合成

ある関数の返り値が次の関数の引数として渡されるように結合し、新たな関数を作成することを関数の合成 (function composition) という。

例えば、関数 f,g の合成関数 f(g(x)) を見ると、x は関数 g の引数で、g(x) が関数 f の引数となっている。

Python による実装

functools.reduce() を利用する。
まず、この関数について紹介する。

functools.reduce()
functools.reduce(function, iterable[, initializer])
  • 2つの引数を取る function を iterable に対して、最初から最後の要素にかけて累積的に適用し、iterable を単一の値に縮約する。
  • initializer が指定された場合は、計算の際に iterable の最初に置かれ、また iterable が空の場合の返り値となる。
  • initializer が指定されておらず、iterable が1つの要素しか持たない場合、その要素が返される。
import functools

def add(x, y):
    return x + y

functools.reduce(add, [1, 2, 3, 4, 5])  # 15

関数 add をリスト [1, 2, 3, 4, 5] に対して、最初から最後の要素にかけて累積的に適用するとは以下の流れで行われる。

1. add(1, 2) := 3
2. add(add(1, 2), 3) := 3 + 3 = 6
3. add(add(add(1, 2), 3), 4) := 6 + 4 = 10
4. add(add(add(add(1, 2), 3), 4), 5) := 10 + 5 = 15
5. リストが空になったのでストップし、15 を返す。

# initializer が 1 の場合、[1, 2, 3, 4, 5] の先頭に initializer の値 1 が置かれる。
functools.reduce(add, [1, 2, 3, 4, 5], 1)  # 16
# initializer が 1 で iterable が空の場合、initializer の値 1 が返される。
functools.reduce(add, [], 1)  # 1
# iiterable の要素が1つで、initializer が指定されていない場合、その要素が返される。
functools.reduce(add, [1])  # 1

function composition の実装例

def compose(*funcs):
    return functools.reduce(lambda f, g: lambda x: g(f(x)), funcs, lambda x: x)
  • 引数
    • function
      • 2つの関数 f, g を引数として受け取り、合成関数 g \circ f を返す高階関数である。
      • lambda f, g: lambda x: g(f(x))
    • iterable
      • 合成する関数のリスト
    • initializer
      • 恒等関数 f(x) = x

使用例は以下のようになる。

def sin(x):
    return 2 * x

def decrement(x):
    return x - 1

def increment(x):
    return x + 1

# 合成関数 func(x) = decrement(double(increment(x))) を作成する。
func = compose(decrement, double, increment)

func(10)  # (10 - 1) * 2 + 1 = 19

iterable の要素を任意の引数をとる関数に対応させる。

これまでのバージョンでは iterable の要素には1つの引数をとり、1つの返り値を返す関数しか指定できなかった。
これを各関数が任意の引数をとってもよいように拡張する。

x を *args, **kwargs とすることで、任意の位置引数及びキーワード引数に対応できるようになる。

def compose(*funcs):
    return functools.reduce(lambda f, g: lambda *args, **kwargs: g(f(*args, **kwargs)), funcs)

def double(x):
    return 2 * x

def add(x, y):
    return x + y

# 合成関数 func(x) = double(add(x, y)) を作成する。
func = compose(add, double)

func(10, 1)  # (10, 1) * 2 = 22