|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "id": "9b8b80fb", |
| 6 | + "metadata": {}, |
| 7 | + "source": [ |
| 8 | + "# TypeScript Counter 問題の完全解析\n", |
| 9 | + "\n", |
| 10 | + "## 1. 問題の分析\n", |
| 11 | + "\n", |
| 12 | + "### 競技プログラミング視点での分析\n", |
| 13 | + "- **実行速度最優先**: クロージャーを利用した O(1) の定数時間アクセス\n", |
| 14 | + "- **メモリ使用量**: 単一の数値変数のみを保持 (O(1) 空間)\n", |
| 15 | + "- **最適化ポイント**: プリミティブ型の直接操作、不要なオブジェクト生成の回避\n", |
| 16 | + "\n", |
| 17 | + "### 業務開発視点での分析\n", |
| 18 | + "- **型安全性**: 引数と戻り値の厳密な型定義が必要\n", |
| 19 | + "- **保守性**: クロージャーの仕組みを明確にドキュメント化\n", |
| 20 | + "- **エラーハンドリング**: 制約条件 (-1000 ≤ n ≤ 1000) のバリデーション\n", |
| 21 | + "- **予測可能性**: Pure function ではなく状態を持つが、副作用は限定的\n", |
| 22 | + "\n", |
| 23 | + "### TypeScript特有の考慮点\n", |
| 24 | + "- **型推論の活用**: 戻り値の型を明示的に定義\n", |
| 25 | + "- **ジェネリクス**: この問題では不要(number型固定)\n", |
| 26 | + "- **型ガード**: 入力値の範囲検証\n", |
| 27 | + "- **クロージャー型定義**: 関数型の明示的な型注釈\n", |
| 28 | + "\n", |
| 29 | + "## 2. アルゴリズムアプローチ比較\n", |
| 30 | + "\n", |
| 31 | + "| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |\n", |
| 32 | + "|---------|----------|----------|------------|---------|--------|------|\n", |
| 33 | + "| クロージャー + 後置インクリメント | O(1) | O(1) | 低 | 高 | 高 | 最もシンプル、LeetCodeの想定解 |\n", |
| 34 | + "| クロージャー + 前置インクリメント | O(1) | O(1) | 低 | 高 | 中 | 初回呼び出し時の処理が複雑化 |\n", |
| 35 | + "| オブジェクト指向 (class) | O(1) | O(1) | 中 | 高 | 中 | 過剰設計、LeetCode形式に不適 |\n", |
| 36 | + "| Generator関数 | O(1) | O(1) | 中 | 中 | 低 | 呼び出し形式が異なる |\n", |
| 37 | + "\n", |
| 38 | + "## 3. 選択したアルゴリズムと理由\n", |
| 39 | + "\n", |
| 40 | + "### 選択したアプローチ\n", |
| 41 | + "**クロージャー + 後置インクリメント (n++) 方式**\n", |
| 42 | + "\n", |
| 43 | + "### 理由\n", |
| 44 | + "\n", |
| 45 | + "#### 計算量的な優位性\n", |
| 46 | + "- 時間計算量: O(1) - 各呼び出しで定数時間\n", |
| 47 | + "- 空間計算量: O(1) - 単一の数値変数のみ\n", |
| 48 | + "- インクリメント演算は CPU レベルで最適化済み\n", |
| 49 | + "\n", |
| 50 | + "#### TypeScript環境での型安全性\n", |
| 51 | + "- 引数 `n: number` の厳密な型定義\n", |
| 52 | + "- 戻り値の関数型を明示: `() => number`\n", |
| 53 | + "- コンパイル時に型不一致を検出可能\n", |
| 54 | + "\n", |
| 55 | + "#### 保守性・可読性の観点\n", |
| 56 | + "- クロージャーパターンは JavaScript/TypeScript の標準的なイディオム\n", |
| 57 | + "- 後置インクリメント (`n++`) により「現在値を返してから増加」が自明\n", |
| 58 | + "- コード量が最小で理解しやすい\n", |
| 59 | + "\n", |
| 60 | + "### TypeScript特有の最適化ポイント\n", |
| 61 | + "- **厳密な型定義**: strict mode でのコンパイル時検証\n", |
| 62 | + "- **readonly 不要**: クロージャー内の変数は外部から直接アクセス不可\n", |
| 63 | + "- **型推論の活用**: 内部変数 `n` の型は自動推論される\n", |
| 64 | + "\n", |
| 65 | + "## 4. 実装コード\n", |
| 66 | + "\n", |
| 67 | + "```typescript\n", |
| 68 | + "/**\n", |
| 69 | + " * カウンター関数を生成する\n", |
| 70 | + " * 初回呼び出し時は初期値nを返し、以降呼び出すたびに1ずつ増加した値を返す\n", |
| 71 | + " * \n", |
| 72 | + " * @param n - カウンターの初期値 (-1000 <= n <= 1000)\n", |
| 73 | + " * @returns 呼び出すたびにインクリメントされる値を返す関数\n", |
| 74 | + " * @throws {RangeError} nが制約範囲外の場合\n", |
| 75 | + " * @complexity Time: O(1), Space: O(1)\n", |
| 76 | + " * \n", |
| 77 | + " * @example\n", |
| 78 | + " * const counter = createCounter(10);\n", |
| 79 | + " * counter(); // 10\n", |
| 80 | + " * counter(); // 11\n", |
| 81 | + " * counter(); // 12\n", |
| 82 | + " */\n", |
| 83 | + "function createCounter(n: number): () => number {\n", |
| 84 | + " // 入力検証: 制約条件のチェック\n", |
| 85 | + " if (n < -1000 || n > 1000) {\n", |
| 86 | + " throw new RangeError('Initial value must be between -1000 and 1000');\n", |
| 87 | + " }\n", |
| 88 | + " \n", |
| 89 | + " // 型ガード: number型の確認\n", |
| 90 | + " if (typeof n !== 'number' || !Number.isFinite(n)) {\n", |
| 91 | + " throw new TypeError('Initial value must be a finite number');\n", |
| 92 | + " }\n", |
| 93 | + " \n", |
| 94 | + " /**\n", |
| 95 | + " * クロージャーによるカウンター実装\n", |
| 96 | + " * 外部スコープの変数nを保持し、呼び出すたびにインクリメント\n", |
| 97 | + " * \n", |
| 98 | + " * @returns 現在のカウント値(呼び出し後に内部状態が+1される)\n", |
| 99 | + " */\n", |
| 100 | + " return function(): number {\n", |
| 101 | + " // 後置インクリメント: 現在値を返した後にnを+1\n", |
| 102 | + " // この演算子により「返却」→「増加」の順序が保証される\n", |
| 103 | + " return n++;\n", |
| 104 | + " };\n", |
| 105 | + "}\n", |
| 106 | + "\n", |
| 107 | + "// LeetCode形式のエクスポート(互換性のため)\n", |
| 108 | + "const createCounterLeetCode = createCounter;\n", |
| 109 | + "\n", |
| 110 | + "/**\n", |
| 111 | + " * 使用例とテストケース\n", |
| 112 | + " */\n", |
| 113 | + "// Example 1\n", |
| 114 | + "const counter1 = createCounter(10);\n", |
| 115 | + "console.assert(counter1() === 10, 'First call should return 10');\n", |
| 116 | + "console.assert(counter1() === 11, 'Second call should return 11');\n", |
| 117 | + "console.assert(counter1() === 12, 'Third call should return 12');\n", |
| 118 | + "\n", |
| 119 | + "// Example 2\n", |
| 120 | + "const counter2 = createCounter(-2);\n", |
| 121 | + "console.assert(counter2() === -2, 'First call should return -2');\n", |
| 122 | + "console.assert(counter2() === -1, 'Second call should return -1');\n", |
| 123 | + "console.assert(counter2() === 0, 'Third call should return 0');\n", |
| 124 | + "console.assert(counter2() === 1, 'Fourth call should return 1');\n", |
| 125 | + "console.assert(counter2() === 2, 'Fifth call should return 2');\n", |
| 126 | + "\n", |
| 127 | + "// エッジケース\n", |
| 128 | + "const counterMin = createCounter(-1000);\n", |
| 129 | + "console.assert(counterMin() === -1000, 'Minimum value test');\n", |
| 130 | + "\n", |
| 131 | + "const counterMax = createCounter(1000);\n", |
| 132 | + "console.assert(counterMax() === 1000, 'Maximum value test');\n", |
| 133 | + "```\n", |
| 134 | + "\n", |
| 135 | + "## LeetCode提出用フォーマット\n", |
| 136 | + "\n", |
| 137 | + "```typescript\n", |
| 138 | + "// Analyze Complexity\n", |
| 139 | + "// Runtime 43 ms\n", |
| 140 | + "// Beats 57.94%\n", |
| 141 | + "// Memory 53.82 MB\n", |
| 142 | + "// Beats 89.21%\n", |
| 143 | + "\n", |
| 144 | + "/**\n", |
| 145 | + " * @param {number} n\n", |
| 146 | + " * @return {Function} counter\n", |
| 147 | + " */\n", |
| 148 | + "function createCounter(n: number): () => number {\n", |
| 149 | + " return function(): number {\n", |
| 150 | + " return n++;\n", |
| 151 | + " };\n", |
| 152 | + "}\n", |
| 153 | + "\n", |
| 154 | + "/** \n", |
| 155 | + " * const counter = createCounter(10)\n", |
| 156 | + " * counter() // 10\n", |
| 157 | + " * counter() // 11\n", |
| 158 | + " * counter() // 12\n", |
| 159 | + " */\n", |
| 160 | + "```\n", |
| 161 | + "\n", |
| 162 | + "## TypeScript固有の最適化観点\n", |
| 163 | + "\n", |
| 164 | + "### 型安全性の活用\n", |
| 165 | + "\n", |
| 166 | + "1. **コンパイル時エラー防止**\n", |
| 167 | + " - `n: number` により文字列や他の型の混入を防止\n", |
| 168 | + " - `() => number` により戻り値の型不一致を検出\n", |
| 169 | + " - strict モードで `null`/`undefined` の混入を防止\n", |
| 170 | + "\n", |
| 171 | + "2. **実行時型検証**\n", |
| 172 | + " ```typescript\n", |
| 173 | + " if (typeof n !== 'number' || !Number.isFinite(n)) {\n", |
| 174 | + " throw new TypeError('Invalid input type');\n", |
| 175 | + " }\n", |
| 176 | + " ```\n", |
| 177 | + "\n", |
| 178 | + "3. **型推論の活用**\n", |
| 179 | + " - クロージャー内の `n` は自動的に `number` 型と推論される\n", |
| 180 | + " - 明示的な型注釈が不要で冗長性を削減\n", |
| 181 | + "\n", |
| 182 | + "### パフォーマンス最適化\n", |
| 183 | + "\n", |
| 184 | + "1. **プリミティブ型の直接操作**\n", |
| 185 | + " - オブジェクト生成のオーバーヘッドなし\n", |
| 186 | + " - 後置インクリメント演算子は CPU レベルで最適化\n", |
| 187 | + "\n", |
| 188 | + "2. **クロージャーの効率的利用**\n", |
| 189 | + " - 変数 `n` はヒープではなくクロージャースコープに保持\n", |
| 190 | + " - ガベージコレクションの対象が最小化\n", |
| 191 | + "\n", |
| 192 | + "3. **不要な処理の排除**\n", |
| 193 | + " - 条件分岐なし\n", |
| 194 | + " - 配列やオブジェクトの生成なし\n", |
| 195 | + "\n", |
| 196 | + "### 保守性と開発効率\n", |
| 197 | + "\n", |
| 198 | + "- **IntelliSense サポート**: 関数の型情報が IDE で自動表示\n", |
| 199 | + "- **リファクタリング安全性**: 型定義により変更影響範囲を把握\n", |
| 200 | + "- **自己文書化**: JSDoc コメントと型定義でドキュメント不要\n", |
| 201 | + "- **テスト容易性**: Pure な入出力で単体テストが簡単\n", |
| 202 | + "\n", |
| 203 | + "この実装は、LeetCode の制約条件を満たしつつ、TypeScript の型安全性を最大限活用した最適解となっています。" |
| 204 | + ] |
| 205 | + } |
| 206 | + ], |
| 207 | + "metadata": { |
| 208 | + "language_info": { |
| 209 | + "name": "python" |
| 210 | + } |
| 211 | + }, |
| 212 | + "nbformat": 4, |
| 213 | + "nbformat_minor": 5 |
| 214 | +} |
0 commit comments