概要

問題の要件

関数の配列 functions = [f1, f2, ..., fn] を受け取り、 右から左へ順に適用する合成関数を返す。

// 合成の定義
compose([f, g, h])(x)
// = f(g(h(x)))
// 空配列 → 恒等関数
compose([])(x) === x

制約条件

  • -1000 ≤ x ≤ 1000
  • 0 ≤ functions.length ≤ 1000
  • 各関数は整数→整数の変換

入出力例

Example 1
functions = [x=>x+1, x=>x*x, x=>2*x]
x = 4
// 2*4=8 → 8²=64 → 64+1=65
Output: 65
Example 2
functions = [x=>10*x, x=>10*x, x=>10*x]
x = 1
// 10*1=10 → 10*10=100 → 10*100=1000
Output: 1000
Example 3
functions = []
x = 42
// 恒等関数 → そのまま返す
Output: 42
🔑 核心アイデア
reduceRight の初期値 x が、 空配列時に恒等関数として自然に機能する。特別な分岐が不要。

ステップバイステップ可視化

例: functions = [x=>x+1, x=>x*x, x=>2*x], x = 4

TypeScript 実装

type F = (x: number) => number;

/**
 * 関数配列の右から左への合成を返す。
 * 空配列の場合は恒等関数を返す。
 *
 * @param functions - 合成する関数の配列(右端から順に適用)
 * @returns 合成された関数
 * @complexity Time: O(n) per call, Space: O(1)
 */
function compose(functions: readonly F[]): F {
    // reduceRight の初期値 x が空配列時の恒等関数を自然に実現する
    return function (x: number): number {
        return functions.reduceRight(
            (acc: number, fn: F): number => fn(acc),
            x
        );
    };
}

/**
 * const fn = compose([x => x + 1, x => x * x, x => 2 * x]);
 * fn(4); // 65  (2*4=8 → 8²=64 → 64+1=65)
 *
 * const id = compose([]);
 * id(42); // 42  (恒等関数)
 */

フローチャート

compose が呼ばれる クロージャを生成して返す 返された fn(x) が呼ばれる functions 空配列? はい x をそのまま返す // 恒等関数 いいえ reduceRight 開始 acc = x(初期値) まだ関数が 残っている? はい acc = fn(acc) 次の関数へ いいえ acc を返す // f(g(h(x))) の結果

フロー解説:
1. compose が呼ばれると即座にクロージャを返す(O(1))
2. 返された関数 fn(x) が呼ばれたとき実際の計算が始まる(O(n))
3. 空配列の場合は reduceRight の初期値 x がそのまま返る → 恒等関数
4. 要素がある場合は右端から fn を順に acc に適用し、最終 acc を返す

計算量分析

O(n)
時間計算量
n = 関数配列の長さ。
compose呼び出しは O(1)、
返された関数の実行が O(n)。
O(1)
空間計算量
クロージャが functions への参照を
1本保持するのみ。
配列のコピーなし。
アプローチ 時間計算量 空間計算量 型安全性 可読性
reduceRight ✓ O(n) O(1) ⭐⭐⭐ ⭐⭐⭐
for ループ(右→左) O(n) O(1) ⭐⭐⭐ ⭐⭐
再帰 O(n) O(n) ⭐⭐ ⭐⭐