第 10 章: 高階関数と関数合成¶
10.1 はじめに¶
第 3 部でオブジェクト指向設計を完成させました。第 4 部では、Python の 関数型プログラミング 機能を FizzBuzz に適用します。この章では、高階関数 と 関数合成 を導入し、命令的なコードを宣言的に書き換えます。
10.2 ラムダ式¶
Python では lambda キーワードで無名関数を定義できます。
# 通常の関数定義
def double(x: int) -> int:
return x * 2
# ラムダ式
double = lambda x: x * 2
10.3 組み込み高階関数¶
Python には map()、filter()、sorted() などの高階関数が組み込まれています。
numbers = [1, 2, 3, 4, 5]
# map: 各要素を変換
doubled = list(map(lambda x: x * 2, numbers))
# filter: 条件に合う要素を抽出
evens = list(filter(lambda x: x % 2 == 0, numbers))
# sorted: カスタムキーでソート
sorted(numbers, key=lambda x: -x)
10.4 リスト内包表記¶
Python では、map() + filter() の代わりに リスト内包表記 がよく使われます。
# map + filter 相当
result = [x * 2 for x in numbers if x % 2 == 0]
10.5 FizzBuzzListCommand のリファクタリング¶
従来のループを関数型スタイルに書き換えます。
Before(命令的)¶
def execute(self) -> FizzBuzzList:
result = FizzBuzzList()
for i in range(1, self.MAX_NUMBER + 1):
result.add(self._type.generate(i))
return result
After(関数型)¶
def execute(self) -> FizzBuzzList:
values = [self._type.generate(i) for i in range(1, self.MAX_NUMBER + 1)]
return FizzBuzzList(values)
10.6 FizzBuzzList にフィルタリングを追加¶
特定の条件に合う値だけを抽出するメソッドを追加します。
from collections.abc import Callable
class FizzBuzzList:
def filter(self, predicate: Callable[[FizzBuzzValue], bool]) -> "FizzBuzzList":
return FizzBuzzList([v for v in self._values if predicate(v)])
テスト¶
def test_Fizzだけをフィルタリングする(self) -> None:
type_ = FizzBuzzType.create(1)
command = FizzBuzzListCommand(type_)
result = command.execute()
fizzes = result.filter(lambda v: v.value == "Fizz")
assert fizzes.size() > 0
assert all(fizzes.get(i).value == "Fizz" for i in range(fizzes.size()))
10.7 関数合成¶
functools モジュールを使って関数を合成します。
from functools import reduce
def compose(*funcs):
"""右から左に関数を合成する。"""
return reduce(lambda f, g: lambda x: f(g(x)), funcs)
FizzBuzzList での文字列変換¶
class FizzBuzzList:
def to_formatted_string(self, separator: str = "\n") -> str:
return separator.join(repr(v) for v in self._values)
10.8 まとめ¶
| 概念 | Java | Python |
|---|---|---|
| ラムダ式 | (x) -> x * 2 |
lambda x: x * 2 |
| map | stream.map() |
map() / リスト内包表記 |
| filter | stream.filter() |
filter() / リスト内包表記 |
| reduce | stream.reduce() |
functools.reduce() |
| 関数合成 | f.andThen(g) |
compose(f, g) |
次の章では、不変データとパイプライン処理を適用します。