関数型プログラミング
Python
lambda・map・filter・itertools・generator
lambda と高階関数
map・filter・reduce・sorted
from functools import reduce
# lambda(無名関数、シンプルな処理のみに)
double = lambda x: x * 2
add = lambda x, y: x + y
# map: 各要素に関数を適用
nums = [1, 2, 3, 4, 5]
doubled = list(map(lambda x: x * 2, nums)) # [2,4,6,8,10]
# 内包表記が読みやすい場合が多い:
doubled = [x * 2 for x in nums]
# filter: 条件を満たす要素を残す
evens = list(filter(lambda x: x % 2 == 0, nums)) # [2,4]
evens = [x for x in nums if x % 2 == 0] # 内包表記版
# reduce: 累積処理
total = reduce(lambda acc, x: acc + x, nums, 0) # 15
product = reduce(lambda acc, x: acc * x, nums, 1) # 120
# sorted のキー関数
users = [{'name': 'Charlie', 'age': 30}, {'name': 'Alice', 'age': 25}]
sorted_by_age = sorted(users, key=lambda u: u['age'])
sorted_by_name = sorted(users, key=lambda u: u['name'])
# operator モジュール(lambda の代わり)
from operator import attrgetter, itemgetter
sorted(users, key=itemgetter('age')) # dictのキー
sorted(objects, key=attrgetter('name')) # 属性
sorted(objects, key=attrgetter('x', 'y')) # 複数属性ジェネレーター
yield・yield from・ジェネレーター式
# ジェネレーター関数(yield で値を返しつつ状態を保持)
def count_up(start: int = 0):
while True:
yield start
start += 1
counter = count_up()
next(counter) # 0
next(counter) # 1
next(counter) # 2
# 有限ジェネレーター
def fibonacci(n: int):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
list(fibonacci(8)) # [0,1,1,2,3,5,8,13]
# yield from(サブジェネレーターに委譲)
def chain(*iterables):
for it in iterables:
yield from it
list(chain([1,2], [3,4], [5,6])) # [1,2,3,4,5,6]
# ジェネレーター式(メモリ効率)
sum_sq = sum(x**2 for x in range(1_000_000)) # リストを生成しない
# 実用パターン:大きなファイルを行ごとに処理
def read_large_file(path: str):
with open(path) as f:
yield from f # 1行ずつ yield
# パイプライン
def grep(pattern, lines):
import re
return (line for line in lines if re.search(pattern, line))
def head(lines, n=10):
return (line for _, line in zip(range(n), lines))
lines = read_large_file('app.log')
errors = grep(r'ERROR', lines)
first_10 = head(errors, 10)
for line in first_10:
print(line, end='')itertools
chain・product・groupby・islice・accumulate
import itertools
# 結合・繰り返し
list(itertools.chain([1,2], [3,4], [5])) # [1,2,3,4,5]
list(itertools.chain.from_iterable([[1,2],[3,4]])) # [1,2,3,4]
list(itertools.repeat('x', 3)) # ['x','x','x']
list(itertools.cycle('AB')) # A,B,A,B,...(無限)
# スライス
list(itertools.islice(range(100), 5, 15, 2)) # [5,7,9,11,13]
# 組み合わせ
list(itertools.product('AB', repeat=2)) # AA,AB,BA,BB
list(itertools.combinations('ABCD', 2)) # AB,AC,AD,BC,BD,CD
list(itertools.permutations('ABC', 2)) # AB,AC,BA,BC,CA,CB
# 累積
list(itertools.accumulate([1,2,3,4,5])) # [1,3,6,10,15]
list(itertools.accumulate([1,2,3,4,5], max)) # [1,2,3,4,5]
# グループ化(事前ソートが必要)
data = [('A',1),('A',2),('B',3),('B',4),('C',5)]
for key, group in itertools.groupby(data, key=lambda x: x[0]):
print(key, list(group))
# A [('A',1),('A',2)]
# B [('B',3),('B',4)]
# フィルタリング
list(itertools.takewhile(lambda x: x < 5, [1,2,3,4,5,6])) # [1,2,3,4]
list(itertools.dropwhile(lambda x: x < 5, [1,2,3,4,5,6])) # [5,6]
list(itertools.compress('ABCDE', [1,0,1,0,1])) # ['A','C','E']
# ペアリング
list(itertools.pairwise([1,2,3,4,5])) # [(1,2),(2,3),(3,4),(4,5)]functools
lru_cache・partial・wraps・reduce
import functools
# lru_cache: メモ化(結果をキャッシュ)
@functools.lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
if n < 2: return n
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(50) # 高速(再帰でも O(n))
fibonacci.cache_info() # CacheInfo(hits=48, misses=51, ...)
fibonacci.cache_clear()
# cache(Python 3.9+): maxsize=None の lru_cache
@functools.cache
def expensive(n: int) -> int: ...
# partial: 引数を一部固定した新しい関数を作る
from functools import partial
def power(base: float, exp: float) -> float:
return base ** exp
square = partial(power, exp=2)
cube = partial(power, exp=3)
square(5) # 25
cube(3) # 27
# よく使われる例
from functools import partial
int_from_hex = partial(int, base=16)
int_from_hex('ff') # 255
int_from_hex('1a') # 26
# total_ordering: __eq__ と1つの比較メソッドから全て生成
@functools.total_ordering
class Card:
def __eq__(self, other): ...
def __lt__(self, other): ...
# __le__, __gt__, __ge__ は自動生成