ある日の我が家
娘(8歳)「うぇ〜ん、うぇ〜ん!」
ワイ「どないしたんや、娘ちゃん!?」
娘「うぇ〜ん、足せないよ〜!」
ワイ「足せない?」
ワイ「何が足せへんのや?」
娘「100
という数値に対して、配列内の数値を全て加算したいのに、できないよ〜!!!」
ワイ「なんや、そんなことかいな!」
ワイ「確かに8歳といったら、100
という数値に対して配列内の数値を全て加算したいお年頃よな〜!!!」
ワイ「パパに任しとき!」
ワイ「まずは───」
// 配列を準備
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 足し算関数を準備
const add = (a: number, b: number): number => {
return a + b;
};
ワイ「↑こうやな」
ワイ「数値が入った配列と、足し算関数を用意したわけや」
ワイ「そんで、100
に対して全部の数を足してあげなアカンから───」
// 数値 100 に対して、配列の中身を全て加算する
const result = array.reduce(add, 100);
console.log(result); // => 155
ワイ「↑こうや!」
娘「うわぁ〜、ありがとう!パパ!」
娘「でも、これ何が起きてるの?」
ワイ「おお、すまん」
ワイ「分かりにくかったか?」
ワイ「ほな、add
っていう足し算関数の中でconsole.log()
してみよか!」
// 足し算関数を準備
const add = (a: number, b: number): number => {
+ // どんな計算をしているのか出力
+ console.log(`${a} + ${b}`);
return a + b;
};
ワイ「↑こうやな!」
ワイ「ほな、もういちど実行してみるで!」
実行結果
100 + 1
101 + 2
103 + 3
106 + 4
110 + 5
115 + 6
121 + 7
128 + 8
136 + 9
145 + 10
娘「そっか!」
娘「配列の要素が10個あるから、add
関数も10回実行されてるんだね!」
ワイ「せやで」
ワイ「引数a
の方には、合計値が入ってきて」
ワイ「引数b
の方には、配列の要素が順番に入ってくる感じや!」
娘「そっか、引数a
の方は合計値なんだね」
娘「でも、1回目の計算の時だけは、初期値の100
が入って来るんだね」
ワイ「そうそう、そういう感じや!」
娘「ありがとう、パパ!」
5分後
娘「うぇ〜ん、うぇ〜ん!」
ワイ「今度はどないしたんや、娘ちゃん!?」
娘「うぇ〜ん、足せないよ〜!」
ワイ「今度は何が足せへんのや?」
娘「配列内のユーザーの年齢を合計したいのに、できないよ〜」
ワイ「なんや、そんなことかいな!」
ワイ「確かに8歳といったら、配列内のユーザーの年齢を合計したいお年頃よな〜!!!」
ワイ「パパに任しとき!」
ワイ「まず、ユーザーの型は───」
// ユーザーは名前と年齢を持っている
type User = {
name: string;
age: number;
};
ワイ「↑こんな感じやな!」
ワイ「そんで、ユーザーさん達を配列に入れてあげるから───」
// ユーザーの配列
const array: User[] = [
{
name: "太郎",
age: 100,
},
{
name: "二郎",
age: 200,
},
{
name: "三郎",
age: 300,
},
];
ワイ「↑こうやな!」
ワイ「ほんで、数値に対してユーザーの年齢を加算する関数が必要やから───」
// a という数値に対して、ユーザーの年齢を加算する関数
const addUserAge = (a: number, user: User): number => {
return a + user.age;
};
ワイ「↑こうや!」
ワイ「ほな、実行してみるで!」
// 実行
const result = array.reduce(addUserAge, 0);
console.log(result); // => 600
ワイ「お、合計値を求めることができたで!」
娘「うわぁ〜、ありがとう!パパ!」
娘「さっきは───」
数値
100
に対して、配列内の数値を順番に足していく
娘「───って感じだったけど」
娘「今回は───」
数値
0
に対して、配列内のユーザーの年齢を順番に足していく
娘「───って感じなんだね!」
ワイ「そういうことや!」
また5分後
娘「うぇ〜ん、うぇ〜ん!」
ワイ「またかいな・・・」
ワイ「これはもう、プログラミングの問題というより心の問題やで」
ワイ「一回、病院行った方がええんちゃうか?」
娘「うぇ〜ん、病院やだよ〜!」
娘「足せないよ〜!」
ワイ「しゃあないなぁ・・・」
ワイ「今度は何が足せへんのや?」
娘「関数の足し算ができないよ〜!」
ワイ「関数の足し算なら、こうやないか!」
// 足し算関数
const add = (a: number, b: number): number => {
return a + b;
};
娘「それは、足し算の関数でしょ!」
娘「私は、関数の足し算がしたいの!」
ワイ「関数の・・・足し算・・・?」
娘「そう!」
// 3 を足す関数
const add3 = (n: number): number => n + 3;
// 5 を足す関数
const add5 = (n: number): number => n + 5;
娘「↑この、add3
とadd5
っていう関数を合成して」
娘「add8
っていう関数を作りたいの!」
ワイ「なんや、そんなことかいな!」
ワイ「確かに8歳といったら、関数を合成したいお年頃よな〜!!!」
ワイ「パパに任しとき!」
ワイ「さっきの応用で行けるで!」
ワイ「さっきは───」
数値
0
に対して、配列内のユーザーの年齢を順番に足していく
ワイ「↑こうだったけど、次は───」
ある数値に対して、配列内の関数を順番に適用していく
ワイ「↑こういう事をすればええんや!」
ワイ「せやから、まずは───」
// 計算を表す型(数値を受け取って、数値を返す関数)
type Keisan = (n: number) => number;
ワイ「↑こう、計算を表す型を定義したで!」
ワイ「ほんで、add3
とadd5
は───」
// 3 を足す関数
const add3: Keisan = (n) => n + 3;
// 5 を足す関数
const add5: Keisan = (n) => n + 5;
ワイ「↑こうやな!」
ワイ「次は、計算を適用してあげる関数が必要やから───」
// a という数値に対して、func という計算関数を適用してあげる関数
const doKeisan = (a: number, func: Keisan): number => {
return func(a);
};
ワイ「↑こうやな!」
ワイ「そして───」
// 関数を2つ入れた配列を用意
const array = [add3, add5];
// 初期値 10 に対して、add3 と add5 を実行
const result = array.reduce(doKeisan, 10); // => 18
ワイ「↑こうや!」
娘「そっか、配列に関数を詰めてあげて、順番に実行する感じなんだね!」
娘「でも、このままだとなんか、合成してる感じがあんまりしないから───」
// 関数を合成する関数
const gousei = (array: Keisan[]) => {
// 「initialValue に対して array の中の関数を全部実行してくれる関数」を返す
return (initialValue: number) => array.reduce(doKeisan, initialValue);
};
娘「↑こう、関数にしてみた!」
ワイ「おお、関数を作ってくれる関数やな」
娘「うん、こんな感じで使うの!」
// add3 と add5 を合成して、add8 を作る
const add8 = gousei([add3, add5]);
// 実行
const result = add8(10); // => 18
ワイ「おお、ええやないか!」
ワイ「関数を合成してる、って感じするな!」
ワイ「でも、配列を渡すんやなくて───」
// add3 と add5 を合成して、add8 を作る
const add8 = gousei(add3, add5);
ワイ「↑こう、add3
とadd5
をそのまま渡したいなぁ」
娘「わかった、やってみる!」
// 関数を合成する関数
- const gousei = (array: Keisan[]) => {
+ const gousei = (...array: Keisan[]) => {
// 省略
};
娘「↑できた!」
ワイ「おお、そうか」
ワイ「可変長引数にすればいいだけやったな」
娘「うん!」
娘「実行してみるね!」
// add3 と add5 を合成して、add8 を作る
const add8 = gousei(add3, add5);
// 実行
const result = add8(10); // => 18
ワイ「おぉ〜、できとるわ」
ワイ「もちろん、こう───」
// 3つの関数を合成
const add13 = gousei(add3, add5, add5);
// 実行
const result = add13(10); // => 23
ワイ「3つ以上の関数も合成できるしな!」
娘「わ〜い!」
ワイ「ワ〜イ!」
まとめ
-
Array.prototype.reduce()
で色々できた
その日の夜
娘「でも実際、関数合成ってなんの役に立つの?」
ワイ「うーん、例えば、入力フォームのバリデーションをするときに───」
- 10文字以上である事を確認するバリデーション関数
- 500文字以下である事を確認するバリデーション関数
ワイ「↑こう、2つのバリデーション関数を作って」
ワイ「それを合成して───」
- 10文字以上かつ500文字以下である事を確認するバリデーション関数
ワイ「↑こういう関数を作ったりできるで」
娘「へぇ〜、単機能な関数をいくつか作って」
娘「それを合成して別の関数を作れるんだね」
ワイ「そんな感じやな」
ワイ「もっと色々なこともできると思うけど」
ワイ「パパの頭脳ではよく分かってへんわ」
ワイ「あとはChatGPTにでも聞いてや・・・」
〜おしまい〜