LoginSignup
29
8

ある日の我が家

娘(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;

娘「↑この、add3add5っていう関数を合成して」
娘「add8っていう関数を作りたいの!」

ワイ「なんや、そんなことかいな!」
ワイ「確かに8歳といったら、関数を合成したいお年頃よな〜!!!」
ワイ「パパに任しとき!」
ワイ「さっきの応用で行けるで!」
ワイ「さっきは───」

数値0に対して、配列内のユーザーの年齢を順番に足していく

ワイ「↑こうだったけど、次は───」

ある数値に対して、配列内の関数を順番に適用していく

ワイ「↑こういう事をすればええんや!」
ワイ「せやから、まずは───」

    // 計算を表す型(数値を受け取って、数値を返す関数)
    type Keisan = (n: number) => number;

ワイ「↑こう、計算を表す型を定義したで!」
ワイ「ほんで、add3add5は───」

    // 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);

ワイ「↑こう、add3add5をそのまま渡したいなぁ」

娘「わかった、やってみる!」

    // 関数を合成する関数
-   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にでも聞いてや・・・」

〜おしまい〜

参考文献

29
8
13

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
8