カリー化
同じ引数を、毎回書いている
税込み価格を出す関数を addTax(price, rate) の形で作ったとします。呼ぶ側は毎回 addTax(price, 0.1) と書きます。20 か所で呼んでいれば 0.1 が 20 回並び、税率が変わったら 20 か所を直します。
配列にまとめてかけたいときも、そのままでは渡せません。
JavaScript
prices.map(price => addTax(price, 0.1));この price => ... は、0.1 を渡すためだけに書いた包み紙です。中身のある処理は 1 つも増えていないのに、行だけが増えています。
先に決めておいて、後で残りを渡す
2 つの引数を同時に受け取るのをやめます。先に決まる方だけを受け取り、残りを受け取る関数を返す形にすると、設定を 1 回で済ませられます。
JavaScript
const taxAdder = (rate) => (price) => Math.round(price * (1 + rate));
const addJpTax = taxAdder(0.1);
addJpTax(980); // 1078
prices.map(addJpTax); // 包み紙が要らないtaxAdder(0.1) が返した関数は、0.1 を覚えたままです。関数が、作られたときの外側の変数を覚えている性質をクロージャと呼びます。この覚えておく仕組みがあるので、設定と入力を別のタイミングで渡せます。
先に受け取る引数は、変わりにくいものを選びます。税率は画面が変わっても同じですが、価格は 1 件ごとに変わります。変わりにくい方を先に置くと、作った関数を長く使い回せます。逆にすると毎回作り直すことになり、包み紙を書いていたときと手間が変わりません。
相性が良いのは「最初に 1 回決めて、あとは何度も使う値」です。税率のほかに、区切り文字、ログの接頭辞、通貨記号、切り上げの桁などが当てはまります。csvLine と tsvLine のように、区切り文字だけ違う関数を 2 本作る、といった使い方もできます。
呼び出しが 2 段階になる
この形にすると、呼び出しの括弧が 2 つ続きます。設定を先に渡してから値を渡すので、まとめて書けば taxAdder(0.1)(980) です。見慣れないうちは、この見た目でつまずきます。
よくある間違いは、1 段目で止めて結果を使おうとすることです。taxAdder(0.1) の中身は数値ではなく関数なので、そのまま足したり比べたりしても、期待した値にはなりません。テストの結果に function や <function ...> という文字が出てきたら、括弧が 1 つ足りていないと思ってください。
逆に、設定を渡さずにいきなり値を渡すのも同じ間違いです。1 段目が受け取るのは設定で、値ではありません。どちらの引数が先に決まるのかを決めてから書き始めると、この取り違えは起きません。
覚え方 ... 括弧が 2 つ続いたら「先に設定、後でデータ」。順番を決めるのは、どちらが先に決まるか。
Python では名前つきの内側の関数で書く
短い無名関数の構文を覚える必要はありません。普通の def を内側にも書けば、同じ形になります。
Python
def taxAdder(rate):
def total(price):
tax = price * rate
return price + tax
return total
withTax = taxAdder(0.1)
print(withTax(1000)) # 1100.0return total は税率を覚えた関数を返し、withTax(1000) は価格を渡して税込みの値を得ます。内側では外側の rate を使えます。税額を計算する段階と、価格に足す段階が内側にまとまっています。
演習の題材は整数の加算です。内側の def の置き場所は用意済みですが、先の値を覚えて計算する処理、関数を返す処理、関数を作って呼ぶ 2 段階を自分で書きます。
要件
- 内側の関数で、先に受け取った値と今回の値の和を返す
- 外側の関数から、内側の関数そのものを返す
- 最初に a で関数を作り、その関数を b で呼んで結果を返す