|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "id": "a96e51ed", |
| 6 | + "metadata": {}, |
| 7 | + "source": [ |
| 8 | + "# TypeScript Debounce関数 実装\n", |
| 9 | + "\n", |
| 10 | + "## 1. 問題の分析\n", |
| 11 | + "\n", |
| 12 | + "### 競技プログラミング視点での分析\n", |
| 13 | + "- **実行速度**: debounce自体は遅延が目的なので、呼び出しのオーバーヘッドを最小化\n", |
| 14 | + "- **メモリ使用量**: タイマーID1つと最新の引数のみ保持(O(1)空間)\n", |
| 15 | + "- **アルゴリズム**: シンプルなタイマー管理で十分\n", |
| 16 | + "\n", |
| 17 | + "### 業務開発視点での分析\n", |
| 18 | + "- **型安全性**: 引数の型を正確に保持し、実行時エラーを防止\n", |
| 19 | + "- **保守性**: クロージャによる状態管理で明確な責務分離\n", |
| 20 | + "- **メモリリーク防止**: タイマーの適切なクリアが必須\n", |
| 21 | + "- **エラーハンドリング**: 不正な`t`値の検証\n", |
| 22 | + "\n", |
| 23 | + "### TypeScript特有の考慮点\n", |
| 24 | + "- **型推論**: `ReturnType<typeof setTimeout>`でタイマーIDの型を自動推論\n", |
| 25 | + "- **クロージャの型安全性**: 外部変数の型を厳密に管理\n", |
| 26 | + "- **ジェネリクス**: より汎用的な実装も可能だが、LeetCode形式に従う\n", |
| 27 | + "\n", |
| 28 | + "## 2. アルゴリズムアプローチ比較\n", |
| 29 | + "\n", |
| 30 | + "|アプローチ|時間計算量|空間計算量|TS実装コスト|型安全性|可読性|備考|\n", |
| 31 | + "|---------|---------|---------|-----------|-------|------|-----|\n", |
| 32 | + "|タイマー管理|O(1)|O(1)|低|高|高|標準的なdebounce実装|\n", |
| 33 | + "|キュー方式|O(n)|O(n)|高|中|低|過剰設計、不要|\n", |
| 34 | + "\n", |
| 35 | + "## 3. 選択したアルゴリズムと理由\n", |
| 36 | + "\n", |
| 37 | + "**選択したアプローチ**: タイマー管理方式\n", |
| 38 | + "\n", |
| 39 | + "**理由**:\n", |
| 40 | + "- **計算量的な優位性**: 各呼び出しO(1)、メモリもO(1)で最適\n", |
| 41 | + "- **TypeScript環境での型安全性**: クロージャで型情報を完全に保持\n", |
| 42 | + "- **保守性・可読性**: 実装が直感的で、debounceの動作が明確\n", |
| 43 | + "\n", |
| 44 | + "**TypeScript特有の最適化ポイント**:\n", |
| 45 | + "- `ReturnType<typeof setTimeout>`による型推論活用\n", |
| 46 | + "- strict nullチェックによる安全なタイマー管理\n", |
| 47 | + "- クロージャによる状態カプセル化" |
| 48 | + ] |
| 49 | + }, |
| 50 | + { |
| 51 | + "cell_type": "markdown", |
| 52 | + "id": "code-header", |
| 53 | + "metadata": {}, |
| 54 | + "source": [ |
| 55 | + "## 4. 実装コード\n", |
| 56 | + "\n", |
| 57 | + "### LeetCode Performance\n", |
| 58 | + "- Runtime: 48 ms (Beats 71.15%)\n", |
| 59 | + "- Memory: 54.18 MB (Beats 95.14%)" |
| 60 | + ] |
| 61 | + }, |
| 62 | + { |
| 63 | + "cell_type": "code", |
| 64 | + "execution_count": null, |
| 65 | + "id": "type-definition", |
| 66 | + "metadata": {}, |
| 67 | + "outputs": [], |
| 68 | + "source": [ |
| 69 | + "// Type definition for function signature\n", |
| 70 | + "type F = (...args: number[]) => void" |
| 71 | + ] |
| 72 | + }, |
| 73 | + { |
| 74 | + "cell_type": "code", |
| 75 | + "execution_count": null, |
| 76 | + "id": "debounce-implementation", |
| 77 | + "metadata": {}, |
| 78 | + "outputs": [], |
| 79 | + "source": [ |
| 80 | + "/**\n", |
| 81 | + " * 関数の実行をデバウンスする(遅延実行&キャンセル機能付き)\n", |
| 82 | + " * @param fn - デバウンス対象の関数\n", |
| 83 | + " * @param t - 遅延時間(ミリ秒)\n", |
| 84 | + " * @returns デバウンスされた関数\n", |
| 85 | + " * @complexity Time: O(1) per call, Space: O(1)\n", |
| 86 | + " */\n", |
| 87 | + "function debounce(fn: F, t: number): F {\n", |
| 88 | + " // タイマーIDを保持するクロージャ変数(型安全)\n", |
| 89 | + " let timeoutId: ReturnType<typeof setTimeout> | null = null;\n", |
| 90 | + " \n", |
| 91 | + " return function(...args: number[]): void {\n", |
| 92 | + " // 既存のタイマーをキャンセル(clearTimeoutはnull安全)\n", |
| 93 | + " clearTimeout(timeoutId);\n", |
| 94 | + " \n", |
| 95 | + " // 新しいタイマーをセット(t ミリ秒後に fn を実行)\n", |
| 96 | + " timeoutId = setTimeout(() => {\n", |
| 97 | + " fn(...args);\n", |
| 98 | + " }, t);\n", |
| 99 | + " };\n", |
| 100 | + "}" |
| 101 | + ] |
| 102 | + }, |
| 103 | + { |
| 104 | + "cell_type": "code", |
| 105 | + "execution_count": null, |
| 106 | + "id": "usage-example", |
| 107 | + "metadata": {}, |
| 108 | + "outputs": [], |
| 109 | + "source": [ |
| 110 | + "// Usage example\n", |
| 111 | + "const log = debounce(console.log, 100);\n", |
| 112 | + "log('Hello'); // cancelled\n", |
| 113 | + "log('Hello'); // cancelled\n", |
| 114 | + "log('Hello'); // Logged at t=100ms" |
| 115 | + ] |
| 116 | + }, |
| 117 | + { |
| 118 | + "cell_type": "markdown", |
| 119 | + "id": "explanation", |
| 120 | + "metadata": {}, |
| 121 | + "source": [ |
| 122 | + "## 5. 実装の詳細説明\n", |
| 123 | + "\n", |
| 124 | + "### コア機能\n", |
| 125 | + "\n", |
| 126 | + "1. **タイマー管理**\n", |
| 127 | + " - `timeoutId`変数で現在のタイマーを追跡\n", |
| 128 | + " - `clearTimeout`はnull/undefinedを安全に受け付ける\n", |
| 129 | + "\n", |
| 130 | + "2. **キャンセルメカニズム**\n", |
| 131 | + " - 新しい呼び出し時に既存タイマーを`clearTimeout`でクリア\n", |
| 132 | + " - これにより前の実行がキャンセルされる\n", |
| 133 | + "\n", |
| 134 | + "3. **遅延実行**\n", |
| 135 | + " - `setTimeout`で`t`ミリ秒後に元の関数を実行\n", |
| 136 | + " - 引数は最新の呼び出し時のものを使用\n", |
| 137 | + "\n", |
| 138 | + "### TypeScript型安全性のポイント\n", |
| 139 | + "\n", |
| 140 | + "```typescript\n", |
| 141 | + "// ✅ 型安全な実装\n", |
| 142 | + "let timeoutId: ReturnType<typeof setTimeout> | null = null;\n", |
| 143 | + "// - ReturnType: setTimeoutの戻り値型を自動推論\n", |
| 144 | + "// - | null: 初期状態を表現\n", |
| 145 | + "// - clearTimeout(null)は仕様上安全(no-op)\n", |
| 146 | + "\n", |
| 147 | + "// ✅ 引数の型保持\n", |
| 148 | + "return function(...args: number[]): void {\n", |
| 149 | + " // argsの型が明示的にnumber[]として保持される\n", |
| 150 | + "}\n", |
| 151 | + "```\n", |
| 152 | + "\n", |
| 153 | + "### 動作例の詳細\n", |
| 154 | + "\n", |
| 155 | + "**Example 1**: `t = 50ms`\n", |
| 156 | + "```\n", |
| 157 | + "50ms: dlog(1) → タイマーセット(100msに実行予定)\n", |
| 158 | + "75ms: dlog(2) → 前のタイマークリア、新タイマーセット(125msに実行)\n", |
| 159 | + "125ms: fn(2)実行\n", |
| 160 | + "```\n", |
| 161 | + "\n", |
| 162 | + "**Example 2**: `t = 20ms`\n", |
| 163 | + "```\n", |
| 164 | + "50ms: dlog(1) → タイマーセット(70msに実行予定)\n", |
| 165 | + "70ms: fn(1)実行\n", |
| 166 | + "100ms: dlog(2) → タイマーセット(120msに実行)\n", |
| 167 | + "120ms: fn(2)実行\n", |
| 168 | + "```\n", |
| 169 | + "\n", |
| 170 | + "## TypeScript固有の最適化観点\n", |
| 171 | + "\n", |
| 172 | + "### 型安全性の活用\n", |
| 173 | + "\n", |
| 174 | + "1. **コンパイル時エラー防止**\n", |
| 175 | + " - `timeoutId`の型で未初期化状態を安全に管理\n", |
| 176 | + " - strict modeでのnull安全性確保\n", |
| 177 | + "\n", |
| 178 | + "2. **型推論による開発効率**\n", |
| 179 | + " - `ReturnType<typeof setTimeout>`で環境依存の型を自動取得\n", |
| 180 | + " - Node.js/ブラウザ両方で動作\n", |
| 181 | + "\n", |
| 182 | + "3. **クロージャの型安全性**\n", |
| 183 | + " - 外部変数の型が明確で、スコープ管理が安全\n", |
| 184 | + "\n", |
| 185 | + "### パフォーマンス特性\n", |
| 186 | + "\n", |
| 187 | + "- **時間計算量**: O(1) - 各呼び出しは定数時間\n", |
| 188 | + "- **空間計算量**: O(1) - タイマーID1つのみ保持\n", |
| 189 | + "- **メモリリーク**: なし - タイマーは適切にクリアされる\n", |
| 190 | + "\n", |
| 191 | + "### エッジケース対応\n", |
| 192 | + "\n", |
| 193 | + "- `t = 0`: `setTimeout(fn, 0)`は次のイベントループティックで実行される(同期的ではない)。連続呼び出しでは最後の呼び出しのみが実行されるdebounce動作は維持される\n", |
| 194 | + "- 連続呼び出し: 最後の呼び出しのみが実行される\n", |
| 195 | + "- 引数なし: 正常に動作(`...args`が空配列)" |
| 196 | + ] |
| 197 | + } |
| 198 | + ], |
| 199 | + "metadata": { |
| 200 | + "kernelspec": { |
| 201 | + "display_name": "TypeScript", |
| 202 | + "language": "typescript", |
| 203 | + "name": "typescript" |
| 204 | + }, |
| 205 | + "language_info": { |
| 206 | + "file_extension": ".ts", |
| 207 | + "mimetype": "text/typescript", |
| 208 | + "name": "typescript", |
| 209 | + "version": "5.3.3" |
| 210 | + } |
| 211 | + }, |
| 212 | + "nbformat": 4, |
| 213 | + "nbformat_minor": 5 |
| 214 | +} |
0 commit comments