つくる 抽出・変換パイプライン
このレッスンでできるようになること。この章で学んだ filter・map・reduce を組み合わせて、抽出・変換・集計の流れを書けるようになります。並べ替えと判定は準備済みの補助関数に任せます。新しい文法は出てきません。
家計簿の集計は、たいてい同じ形をしています。まず必要な支出を絞り込み、次に扱いやすい形へ直し、最後に1つの答えへまとめる。この3段階を、それぞれ filter と map と reduce が担当します。段ごとに役割がはっきりしているので、後から条件を1つ足したいときも触る場所が1か所で済みます。
家計簿とは別の題材で、長さが2文字以上の単語を選び、文字数に変換し、その合計を求めてみます。
Python
from functools import reduce
words = ["山", "川辺", "図書室"]
is_long = lambda word: len(word) >= 2
picked = list(filter(is_long, words))
lengths = list(map(len, picked))
total_length = reduce(lambda total, length: total + length, lengths, 0)
print(total_length) # 5filter の後は単語のリスト、map の後は文字数のリスト、reduce の後は整数です。reduce の初期値を 0 にすると、選ばれた単語が無い場合も合計は 0 になります。map に渡す len は呼び出さず、関数そのものを渡しています。演習でも、変換用の補助関数を同じように map に渡します。
書くときの約束を3つ確認しておきます。1つめは、map と filter の結果を必ず list() に通すことです。中間の変数に map オブジェクトのまま入れておくと、2回目に使ったときには空になっていて、合計が 0 になるといった原因の分かりにくい不具合になります。
2つめは、金額を int のまま持ち回ることです。税込計算に * 1.1 を使うと端数が float に化け、合計や比較の結果が環境によってぶれます。* 110 // 100 で int を保ちます。
3つめは、並べ替えの順序を決めておくことです。演習では税込金額の降順、同額なら品名の文字コード順の昇順にします。この処理は補助関数に用意してあります。
演習で書くのは4行です。カテゴリが一致するかを返す lambda、その関数を使う filter、税込への変換関数を使う map、税込金額を足す reduce をつなぎます。変換用の _with_tax(expense) は支出1件を受け取り、品名と税込金額を持つ新しい辞書を返します。入力の辞書は書き換えません。
仕上げの _make_report(taxed, total, budget) も準備済みです。税込支出、合計、予算を渡すと、並べ替えた品名のリスト names、税込合計 total、合計が予算を超えるかの over_budget、税込1000円以上の支出があるかの has_expensive をまとめた辞書を返します。予算と同額なら over_budget は False です。合計との比較と、any による1件ごとの判定は役割が違います。ここでは両方を補助関数に任せます。
要件
- 支出の category が対象カテゴリと一致するかを返す lambda を変数に入れ、filter に渡す
- 抽出した支出を map と _with_tax で税込の辞書へ変換する。関数そのものを渡し、呼び出しのかっこを付けない
- filter と map の結果はそれぞれ list にして保存する
- reduce の2引数の lambda で、ここまでの合計に次の支出の amount を足す。初期値は 0 にする
- 対象カテゴリが無い場合も total は 0 にする。補助関数と最後の return は変更しない