From a2b142c7a6eb9888faa844d65d301aa80684b02f Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Tue, 17 Feb 2026 08:00:18 +0900 Subject: [PATCH 1/4] feat: Add 2627. Debounce and move 1174. Immediate Food Delivery II to Intermediate Select --- .../Debounce_TS.ipynb | 184 +++ .../Claude Code Sonnet 4.5 extended/README.md | 493 +++++++ .../README_react.html | 1287 +++++++++++++++++ .../Immediate_Food_Delivery_II.html | 0 .../Immediate_Food_Delivery_II_pandas.md | 0 .../Immediate_Food_Delivery_II_postgre.md | 0 6 files changed, 1964 insertions(+) create mode 100644 JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb create mode 100644 JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md create mode 100644 JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html rename SQL/Leetcode/{Intermediate Join => Intermediate Select}/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II.html (100%) rename SQL/Leetcode/{Intermediate Join => Intermediate Select}/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_pandas.md (100%) rename SQL/Leetcode/{Intermediate Join => Intermediate Select}/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_postgre.md (100%) diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb new file mode 100644 index 00000000..24219cac --- /dev/null +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb @@ -0,0 +1,184 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a96e51ed", + "metadata": {}, + "source": [ + "# TypeScript Debounce関数 実装\n", + "\n", + "## 1. 問題の分析\n", + "\n", + "### 競技プログラミング視点での分析\n", + "- **実行速度**: debounce自体は遅延が目的なので、呼び出しのオーバーヘッドを最小化\n", + "- **メモリ使用量**: タイマーID1つと最新の引数のみ保持(O(1)空間)\n", + "- **アルゴリズム**: シンプルなタイマー管理で十分\n", + "\n", + "### 業務開発視点での分析\n", + "- **型安全性**: 引数の型を正確に保持し、実行時エラーを防止\n", + "- **保守性**: クロージャによる状態管理で明確な責務分離\n", + "- **メモリリーク防止**: タイマーの適切なクリアが必須\n", + "- **エラーハンドリング**: 不正な`t`値の検証\n", + "\n", + "### TypeScript特有の考慮点\n", + "- **型推論**: `ReturnType`でタイマーIDの型を自動推論\n", + "- **クロージャの型安全性**: 外部変数の型を厳密に管理\n", + "- **ジェネリクス**: より汎用的な実装も可能だが、LeetCode形式に従う\n", + "\n", + "## 2. アルゴリズムアプローチ比較\n", + "\n", + "|アプローチ|時間計算量|空間計算量|TS実装コスト|型安全性|可読性|備考|\n", + "|---------|---------|---------|-----------|-------|------|-----|\n", + "|タイマー管理|O(1)|O(1)|低|高|高|標準的なdebounce実装|\n", + "|キュー方式|O(n)|O(n)|高|中|低|過剰設計、不要|\n", + "\n", + "## 3. 選択したアルゴリズムと理由\n", + "\n", + "**選択したアプローチ**: タイマー管理方式\n", + "\n", + "**理由**:\n", + "- **計算量的な優位性**: 各呼び出しO(1)、メモリもO(1)で最適\n", + "- **TypeScript環境での型安全性**: クロージャで型情報を完全に保持\n", + "- **保守性・可読性**: 実装が直感的で、debounceの動作が明確\n", + "\n", + "**TypeScript特有の最適化ポイント**:\n", + "- `ReturnType`による型推論活用\n", + "- strict nullチェックによる安全なタイマー管理\n", + "- クロージャによる状態カプセル化\n", + "\n", + "## 4. 実装コード\n", + "\n", + "```typescript\n", + "// Analyze Complexity\n", + "// Runtime 48 ms\n", + "// Beats 71.15%\n", + "// Memory 54.18 MB\n", + "// Beats 95.14%\n", + "\n", + "type F = (...args: number[]) => void\n", + "\n", + "/**\n", + " * 関数の実行をデバウンスする(遅延実行&キャンセル機能付き)\n", + " * @param fn - デバウンス対象の関数\n", + " * @param t - 遅延時間(ミリ秒)\n", + " * @returns デバウンスされた関数\n", + " * @complexity Time: O(1) per call, Space: O(1)\n", + " */\n", + "function debounce(fn: F, t: number): F {\n", + " // タイマーIDを保持するクロージャ変数(型安全)\n", + " let timeoutId: ReturnType | null = null;\n", + " \n", + " return function(...args: number[]): void {\n", + " // 既存のタイマーがあればキャンセル\n", + " if (timeoutId !== null) {\n", + " clearTimeout(timeoutId);\n", + " }\n", + " \n", + " // 新しいタイマーをセット(t ミリ秒後に fn を実行)\n", + " timeoutId = setTimeout(() => {\n", + " fn(...args);\n", + " }, t);\n", + " };\n", + "}\n", + "\n", + "/**\n", + " * const log = debounce(console.log, 100);\n", + " * log('Hello'); // cancelled\n", + " * log('Hello'); // cancelled\n", + " * log('Hello'); // Logged at t=100ms\n", + " */\n", + "```\n", + "\n", + "## 5. 実装の詳細説明\n", + "\n", + "### コア機能\n", + "\n", + "1. **タイマー管理**\n", + " - `timeoutId`変数で現在のタイマーを追跡\n", + " - `null`チェックで型安全性を確保\n", + "\n", + "2. **キャンセルメカニズム**\n", + " - 新しい呼び出し時に既存タイマーを`clearTimeout`でクリア\n", + " - これにより前の実行がキャンセルされる\n", + "\n", + "3. **遅延実行**\n", + " - `setTimeout`で`t`ミリ秒後に元の関数を実行\n", + " - 引数は最新の呼び出し時のものを使用\n", + "\n", + "### TypeScript型安全性のポイント\n", + "\n", + "```typescript\n", + "// ✅ 型安全な実装\n", + "let timeoutId: ReturnType | null = null;\n", + "// - ReturnType: setTimeoutの戻り値型を自動推論\n", + "// - | null: 初期状態とクリア後の状態を表現\n", + "// - strict null checkで安全性確保\n", + "\n", + "// ✅ 引数の型保持\n", + "return function(...args: number[]): void {\n", + " // argsの型が明示的にnumber[]として保持される\n", + "}\n", + "```\n", + "\n", + "### 動作例の詳細\n", + "\n", + "**Example 1**: `t = 50ms`\n", + "```\n", + "50ms: dlog(1) → タイマーセット(100msに実行予定)\n", + "75ms: dlog(2) → 前のタイマークリア、新タイマーセット(125msに実行)\n", + "125ms: fn(2)実行\n", + "```\n", + "\n", + "**Example 2**: `t = 20ms`\n", + "```\n", + "50ms: dlog(1) → タイマーセット(70msに実行予定)\n", + "70ms: fn(1)実行\n", + "100ms: dlog(2) → タイマーセット(120msに実行)\n", + "120ms: fn(2)実行\n", + "```\n", + "\n", + "## TypeScript固有の最適化観点\n", + "\n", + "### 型安全性の活用\n", + "\n", + "1. **コンパイル時エラー防止**\n", + " - `timeoutId`の`null`チェックで未初期化エラーを防止\n", + " - strict modeでのnull安全性確保\n", + "\n", + "2. **型推論による開発効率**\n", + " - `ReturnType`で環境依存の型を自動取得\n", + " - Node.js/ブラウザ両方で動作\n", + "\n", + "3. **クロージャの型安全性**\n", + " - 外部変数の型が明確で、スコープ管理が安全\n", + "\n", + "### パフォーマンス特性\n", + "\n", + "- **時間計算量**: O(1) - 各呼び出しは定数時間\n", + "- **空間計算量**: O(1) - タイマーID1つのみ保持\n", + "- **メモリリーク**: なし - タイマーは適切にクリアされる\n", + "\n", + "### エッジケース対応\n", + "\n", + "- `t = 0`: 即座に実行(実質debounceなし)\n", + "- 連続呼び出し: 最後の呼び出しのみが実行される\n", + "- 引数なし: 正常に動作(`...args`が空配列)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "TypeScript", + "language": "typescript", + "name": "typescript" + }, + "language_info": { + "file_extension": ".ts", + "mimetype": "text/typescript", + "name": "typescript", + "version": "5.3.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md new file mode 100644 index 00000000..94ad0229 --- /dev/null +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md @@ -0,0 +1,493 @@ +# Debounce - 関数実行の遅延とキャンセル制御 + +## 目次 + +- [概要](#overview) +- [アルゴリズム要点(TL;DR)](#tldr) +- [図解](#figures) +- [正しさのスケッチ](#correctness) +- [計算量](#complexity) +- [Python実装](#impl) +- [CPython最適化ポイント](#cpython) +- [エッジケースと検証観点](#edgecases) +- [FAQ](#faq) + +--- + +

概要

+ +### 問題要約 + +関数 `fn` と遅延時間 `t`(ミリ秒)を受け取り、デバウンスされた関数を返す。デバウンスされた関数は以下の性質を持つ: + +- 実行が `t` ミリ秒遅延される +- 遅延時間内に再度呼び出されると、前の実行がキャンセルされる +- 最後の呼び出しから `t` ミリ秒後に実行される + +### 要件 + +- **正当性**: 最後の呼び出しのみが遅延後に実行される +- **安定性**: タイマーの適切な管理によりメモリリークを防ぐ +- **制約**: `0 <= t <= 1000`、lodashなどの外部ライブラリ不可 + +--- + +

アルゴリズム要点(TL;DR)

+ +- **戦略**: `threading.Timer` を使った遅延実行とキャンセル制御 +- **データ構造**: クロージャで `Timer` オブジェクトを保持 +- **計算量**: Time O(1) per call, Space O(1) +- **メモリ要約**: アクティブなタイマー1つのみを保持(前のタイマーは必ずキャンセル) + +**核心アイデア**: + +1. 関数が呼ばれるたびに、既存のタイマーをキャンセル +2. 新しいタイマーを `t/1000` 秒後にセット +3. クロージャで最新の引数とタイマー参照を保持 + +--- + +

図解

+ +### フローチャート + +```mermaid +flowchart TD + Start[Start debounced call] --> CheckTimer{Active timer exists} + CheckTimer -- Yes --> Cancel[Cancel existing timer] + CheckTimer -- No --> SetTimer[Set new timer] + Cancel --> SetTimer + SetTimer --> Store[Store args in closure] + Store --> Wait[Wait t milliseconds] + Wait --> Execute[Execute fn with stored args] + Execute --> End[End] +``` + +**説明**: デバウンス関数呼び出しの流れ。既存タイマーがあればキャンセルし、新しいタイマーをセット。最後の呼び出しから `t` ミリ秒後に実行。 + +### データフロー図 + +```mermaid +graph LR + subgraph Input + A[Function fn] --> D + B[Delay t ms] --> D + C[Args at call time] --> D + end + subgraph Core + D[Debounce wrapper] --> E[Timer management] + E --> F[Closure state] + F --> G[Scheduled execution] + end + G --> H[Output: fn called with latest args] +``` + +**説明**: 入力された関数と遅延時間から、タイマー管理を行うラッパー関数を生成。クロージャで状態を保持し、スケジュールされた実行を制御。 + +--- + +

正しさのスケッチ

+ +### 不変条件 + +- **タイマー一意性**: 常に最大1つのアクティブなタイマーのみ存在 +- **引数保持**: クロージャは最新の呼び出し時の引数を保持 +- **実行保証**: キャンセルされない限り、タイマーは `t` ミリ秒後に確実に実行 + +### 網羅性 + +1. **初回呼び出し**: タイマーなし → 新規タイマーセット +2. **連続呼び出し(t内)**: タイマーあり → キャンセル → 新規タイマーセット +3. **間隔を空けた呼び出し**: 前のタイマー実行済み → 新規タイマーセット + +### 基底条件 + +- `t = 0` の場合: 即座に実行(実質的にデバウンスなし) +- 引数なしの場合: 空の `*args, **kwargs` で正常動作 + +### 終了性 + +- 各タイマーは有限時間 `t` 後に必ず実行またはキャンセルされる +- デッドロックやスタックなし + +--- + +

計算量

+ +### 時間計算量 + +- **呼び出しあたり**: O(1) + - タイマーのキャンセル: O(1) + - 新規タイマーの生成: O(1) +- **実行時**: O(f) where f は元の関数 `fn` の計算量 + +### 空間計算量 + +- **O(1)**: タイマーオブジェクト1つと引数のタプル/辞書のみ +- **引数のサイズ**: O(args_size) だが、これは呼び出し元の責任 + +### Pure vs In-place + +| 観点 | Pure | In-place | +| ------ | -------------------------- | -------- | +| 副作用 | なし(新関数を返す) | - | +| メモリ | O(1) | - | +| 適用性 | 高(関数型プログラミング) | - | + +※ debounce自体はPure関数(新しい関数を返す)だが、返された関数は副作用を持つ可能性あり + +--- + +

Python実装

+ +```python +from __future__ import annotations +from typing import Callable, Any +from threading import Timer + + +def debounce(fn: Callable[..., Any], t: float) -> Callable[..., None]: + """ + 関数の実行をデバウンスする(遅延実行&キャンセル機能付き) + + Args: + fn: デバウンス対象の関数 + t: 遅延時間(ミリ秒) + + Returns: + デバウンスされた関数 + + Complexity: + Time: O(1) per call + Space: O(1) + + Example: + >>> def log(*args): + ... print(args) + >>> dlog = debounce(log, 100) + >>> dlog('Hello') # cancelled + >>> dlog('Hello') # cancelled + >>> dlog('Hello') # Logged after 100ms + """ + # タイマーオブジェクトを保持するクロージャ変数 + timer: Timer | None = None + + def debounced_func(*args: Any, **kwargs: Any) -> None: + nonlocal timer + + # 既存のタイマーがあればキャンセル + if timer is not None: + timer.cancel() + + # 新しいタイマーをセット(t/1000 秒後に fn を実行) + # threading.Timer は秒単位なので、ミリ秒を秒に変換 + timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs) + timer.start() + + return debounced_func + + +# LeetCode形式の実装例(JavaScriptの問題をPythonで表現) +class Solution: + """ + LeetCode形式のラッパークラス + 実際のLeetCodeにはこの問題はJavaScript/TypeScriptのみだが、 + Pythonで同等の機能を提供 + """ + + def debounce(self, fn: Callable[..., Any], t: int) -> Callable[..., None]: + """ + Args: + fn: デバウンス対象の関数 + t: 遅延時間(ミリ秒、整数) + + Returns: + デバウンスされた関数 + """ + timer: Timer | None = None + + def debounced(*args: Any, **kwargs: Any) -> None: + nonlocal timer + + # 基底条件: タイマーが存在すればキャンセル + if timer is not None: + timer.cancel() + + # 遷移: 新しいタイマーを作成して開始 + # t ミリ秒 = t/1000 秒 + timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs) + timer.start() + + return debounced + + +# 使用例 +if __name__ == "__main__": + import time + + def log(*inputs): + print(f"[{time.time():.3f}] Called with: {inputs}") + + # Example 1: t = 50ms + dlog = debounce(log, 50) + start = time.time() + + time.sleep(0.05) # 50ms + dlog(1) + + time.sleep(0.025) # 75ms total + dlog(2) + + # 2が125ms(75 + 50)に実行される + time.sleep(0.1) # 待機 +``` + +### 主要ステップの説明 + +1. **クロージャ変数**: `timer` を `nonlocal` で宣言し、外部関数のスコープで保持 +2. **タイマーキャンセル**: 既存タイマーがあれば `cancel()` を呼び出し +3. **タイマー生成**: `Timer(秒数, 関数, args, kwargs)` で新しいタイマーを作成 +4. **タイマー開始**: `start()` でバックグラウンドスレッドが起動 +5. **遅延実行**: `t/1000` 秒後に `fn(*args, **kwargs)` が自動実行 + +--- + +

CPython最適化ポイント

+ +### 1. Timer vs asyncio + +**threading.Timer**(本実装): + +- ✅ 同期コードで使いやすい +- ✅ 追加の依存なし +- ❌ スレッドオーバーヘッドあり + +**asyncio(代替案)**: + +- ✅ スレッドなしで軽量 +- ✅ 大量の同時debounce処理に有利 +- ❌ async/awaitの学習コスト + +```python +# asyncio版(参考) +import asyncio + +def debounce_async(fn: Callable, t: float) -> Callable: + task: asyncio.Task | None = None + + async def debounced(*args, **kwargs): + nonlocal task + if task is not None: + task.cancel() + + async def delayed(): + await asyncio.sleep(t / 1000.0) + fn(*args, **kwargs) + + task = asyncio.create_task(delayed()) + + return debounced +``` + +### 2. 属性アクセス削減 + +```python +# ❌ 遅い: 毎回メソッド解決 +if timer is not None: + timer.cancel() + +# ✅ 同じ(Pythonでは最適化余地少ない) +# Cレベルの最適化はインタプリタ任せ +``` + +### 3. nonlocal vs クラス + +**クロージャ(本実装)**: + +- シンプルで関数型的 +- メモリ効率的 + +**クラスベース**: + +```python +class Debouncer: + def __init__(self, fn: Callable, t: float): + self.fn = fn + self.t = t + self.timer: Timer | None = None + + def __call__(self, *args, **kwargs): + if self.timer: + self.timer.cancel() + self.timer = Timer(self.t / 1000.0, self.fn, args, kwargs) + self.timer.start() +``` + +### 4. GIL(Global Interpreter Lock)の影響 + +- `threading.Timer` は別スレッドで実行 +- I/Oバウンドな `fn` には有効 +- CPUバウンドな場合は `multiprocessing` を検討 + +--- + +

エッジケースと検証観点

+ +### 1. t = 0 の場合 + +```python +dlog = debounce(log, 0) +dlog("instant") # 即座に実行(デバウンスなし) +``` + +**期待動作**: タイマーは即座に発火、実質的にデバウンスなし + +### 2. 連続呼び出し + +```python +dlog(1) # キャンセルされる +dlog(2) # キャンセルされる +dlog(3) # 50ms後に実行 +``` + +**検証**: 最後の呼び出しのみが実行される + +### 3. 引数なし + +```python +dlog() # 正常動作 +``` + +**検証**: `*args` が空タプルでも問題なし + +### 4. キーワード引数 + +```python +dlog(x=1, y=2) +``` + +**検証**: `**kwargs` で正しく渡される + +### 5. スレッド安全性 + +```python +# 複数スレッドから呼び出し +from threading import Thread + +t1 = Thread(target=dlog, args=(1,)) +t2 = Thread(target=dlog, args=(2,)) +t1.start() +t2.start() +``` + +**注意**: `threading.Timer` 自体はスレッドセーフだが、`nonlocal timer` への同時アクセスは保護されていない。本格的なマルチスレッド環境では `Lock` が必要。 + +### 6. メモリリーク防止 + +```python +# タイマーが適切にキャンセルされるか確認 +import gc + +dlog(1) +dlog(2) # 前のタイマーがキャンセルされる +gc.collect() # ガベージコレクション +``` + +**検証**: キャンセルされたタイマーが正しく解放される + +### 7. 長時間の遅延 + +```python +dlog_long = debounce(log, 10000) # 10秒 +dlog_long("delayed") +# 10秒後に実行されるか確認 +``` + +--- + +

FAQ

+ +### Q1. なぜ `threading.Timer` を使うのか? + +**A**: Pythonの標準ライブラリで最もシンプルに遅延実行を実現できるため。`time.sleep` はブロッキングで使えない。 + +### Q2. `asyncio` 版との使い分けは? + +**A**: + +- **同期コード**: `threading.Timer` 版 +- **非同期コード**: `asyncio` 版 +- **大量の同時debounce**: `asyncio` 版(スレッドオーバーヘッド削減) + +### Q3. デコレータとして使える? + +**A**: はい。以下のように使用可能: + +```python +@debounce(fn=lambda: None, t=100) # ❌ 引数が合わない + +# 正しいデコレータ化 +def debounce_decorator(t: float): + def decorator(fn: Callable): + return debounce(fn, t) + return decorator + +@debounce_decorator(100) +def my_func(): + print("Called") +``` + +### Q4. JavaScriptのdebounceとの違いは? + +**A**: + +- **JavaScript**: イベントループベース(`setTimeout`) +- **Python**: スレッドベース(`threading.Timer`) +- **動作**: 概念的には同じだが、実装基盤が異なる + +### Q5. throttle との違いは? + +**A**: + +- **debounce**: 最後の呼び出しから `t` ミリ秒後に実行 +- **throttle**: 最初の呼び出しを即座に実行し、以降 `t` ミリ秒間は無視 + +```python +# throttle の例(参考) +def throttle(fn: Callable, t: float) -> Callable: + last_call = [0.0] + + def throttled(*args, **kwargs): + now = time.time() + if now - last_call[0] >= t / 1000.0: + last_call[0] = now + fn(*args, **kwargs) + + return throttled +``` + +### Q6. LeetCodeで提出できる? + +**A**: この問題はJavaScript/TypeScript専用。Python版は学習目的の実装例。 + +### Q7. 実務での使用例は? + +**A**: + +- **検索ボックス**: ユーザー入力後500msでAPI呼び出し +- **ウィンドウリサイズ**: レイアウト再計算を遅延 +- **自動保存**: 編集停止後3秒で保存 + +```python +# 実務例: Flask APIでの検索 +@app.route('/search') +def search(): + query = request.args.get('q') + # debounceはフロントエンド側で実装されることが多い + # バックエンドでは rate limiting で対応 + return jsonify(search_db(query)) +``` + +--- + +**以上で解説を終わります。** この実装は教育目的であり、実務では各フレームワークの公式debounce実装(例: RxPY, Django Channels)の使用を推奨します。 diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html new file mode 100644 index 00000000..4ae0f39d --- /dev/null +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html @@ -0,0 +1,1287 @@ + + + + + + Debounce - 関数実行の遅延とキャンセル制御 | Python実装 + + + + + + + + + + + + + + + + + +
+ + + + +
+

+ アルゴリズム概要 +

+ +
+
+

問題の説明

+

+ 関数 fn と遅延時間 + t(ミリ秒)を受け取り、デバウンスされた関数を返す。 + デバウンスされた関数は以下の性質を持つ: +

+
    +
  • + 実行が + t + ミリ秒遅延される +
  • +
  • + 遅延時間内に再度呼び出されると、前の実行がキャンセルされる +
  • +
  • + 最後の呼び出しから + t + ミリ秒後に実行される +
  • +
+
+ +
+

入出力例

+
+

Example 1: t = 50ms

+
calls = [
+  {"t": 50, "inputs": [1]},
+  {"t": 75, "inputs": [2]}
+]
+Output: [{"t": 125, "inputs": [2]}]
+
+説明: 1回目の呼び出しは2回目によってキャンセルされる
+     2回目は75ms + 50ms = 125msに実行される
+
+ +
+

Example 2: t = 20ms

+
calls = [
+  {"t": 50, "inputs": [1]},
+  {"t": 100, "inputs": [2]}
+]
+Output: [{"t": 70, "inputs": [1]}, {"t": 120, "inputs": [2]}]
+
+説明: 1回目は50ms + 20ms = 70msに実行
+     2回目は100ms + 20ms = 120msに実行
+
+
+ +
+

戦略

+
    +
  • + threading.Timer + を使った遅延実行とキャンセル制御 +
  • +
  • + クロージャで + Timer + オブジェクトを保持 +
  • +
  • 関数が呼ばれるたびに、既存のタイマーをキャンセル
  • +
  • + 新しいタイマーを + t/1000 + 秒後にセット +
  • +
  • 最新の引数をクロージャで保持
  • +
+
+ +
+

主要ポイント

+
+
+

⏱️ 時間計算量

+

O(1) per call

+
+
+

💾 空間計算量

+

O(1) - タイマー1つのみ

+
+
+
+
+
+ + +
+

+ ステップバイステップ解説 +

+
+
+ + +
+

+ Python実装 +

+
from __future__ import annotations
+from typing import Callable, Any
+from threading import Timer
+
+
+def debounce(fn: Callable[..., Any], t: float) -> Callable[..., None]:
+    """
+    関数の実行をデバウンスする(遅延実行&キャンセル機能付き)
+
+    Args:
+        fn: デバウンス対象の関数
+        t: 遅延時間(ミリ秒)
+
+    Returns:
+        デバウンスされた関数
+
+    Complexity:
+        Time: O(1) per call
+        Space: O(1)
+    """
+    # タイマーオブジェクトを保持するクロージャ変数
+    timer: Timer | None = None
+
+    def debounced_func(*args: Any, **kwargs: Any) -> None:
+        nonlocal timer
+
+        # 既存のタイマーがあればキャンセル
+        if timer is not None:
+            timer.cancel()
+
+        # 新しいタイマーをセット(t/1000 秒後に fn を実行)
+        # threading.Timer は秒単位なので、ミリ秒を秒に変換
+        timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs)
+        timer.start()
+
+    return debounced_func
+
+
+# LeetCode形式の実装例
+class Solution:
+    """
+    LeetCode形式のラッパークラス
+    実際のLeetCodeにはこの問題はJavaScript/TypeScriptのみだが、
+    Pythonで同等の機能を提供
+    """
+
+    def debounce(self, fn: Callable[..., Any], t: int) -> Callable[..., None]:
+        """
+        Args:
+            fn: デバウンス対象の関数
+            t: 遅延時間(ミリ秒、整数)
+
+        Returns:
+            デバウンスされた関数
+        """
+        timer: Timer | None = None
+
+        def debounced(*args: Any, **kwargs: Any) -> None:
+            nonlocal timer
+
+            # 基底条件: タイマーが存在すればキャンセル
+            if timer is not None:
+                timer.cancel()
+
+            # 遷移: 新しいタイマーを作成して開始
+            # t ミリ秒 = t/1000 秒
+            timer = Timer(t / 1000.0, fn, args=args, kwargs=kwargs)
+            timer.start()
+
+        return debounced
+
+ + +
+

+ フローチャート +

+
+ + + + + + + + + + + + + + + + + デバウンス呼び出し + + + + + + + + + タイマーあり? + + + timer != None + + + + + + はい + + + + + + タイマーキャンセル + + + timer.cancel() + + + + + + + + + いいえ + + + + + + 新規タイマー + + + Timer(t/1000) + + + + + + + + + 引数保存 + + + クロージャに保持 + + + + + + + + + タイマー開始 + + + timer.start() + + + + + + t ms 待機後 + + + + + + fn(*args, **kwargs) 実行 + + +
+ +

+ フローの説明:
+ 1. デバウンス関数が呼ばれると、まず既存のタイマーの有無を確認
+ 2. タイマーがあれば即座にキャンセル(前の実行を中止)
+ 3. 新しいタイマーを t/1000 秒後に設定
+ 4. 呼び出し時の引数をクロージャに保存
+ 5. タイマーを開始し、t ミリ秒待機
+ 6. 待機完了後、保存された引数で元の関数 fn を実行 +

+
+ + +
+

+ 計算量分析 +

+ +
+
+

時間計算量

+
+

O(1) per call

+
    +
  • タイマーのキャンセル: O(1)
  • +
  • 新規タイマーの生成: O(1)
  • +
  • 実行時: O(f) where f は元の関数 fn の計算量
  • +
+
+
+ +
+

空間計算量

+
+

O(1)

+
    +
  • タイマーオブジェクト1つと引数のタプル/辞書のみ
  • +
  • 引数のサイズ: O(args_size) だが、これは呼び出し元の責任
  • +
+
+
+ +
+

実装比較

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 実装方式 + + メリット + + デメリット + + 用途 +
+ threading.Timer
(本実装) +
+ ✅ 同期コードで使いやすい
+ ✅ 追加の依存なし +
+ ❌ スレッドオーバーヘッド + + 汎用的な用途 +
+ asyncio + + ✅ スレッドなしで軽量
+ ✅ 大量の同時debounce処理に有利 +
+ ❌ async/awaitの学習コスト + + 非同期処理が主体 +
+ time.sleep + + ✅ 最もシンプル + + ❌ ブロッキング
+ ❌ キャンセル不可 +
+ debounceには不適 +
+
+
+
+
+ + +
+

+ © 2026 Algorithm Visualization | Python + React Implementation +

+
+
+ + + + + + + + + + + + + + + + + + + diff --git a/SQL/Leetcode/Intermediate Join/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II.html b/SQL/Leetcode/Intermediate Select/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II.html similarity index 100% rename from SQL/Leetcode/Intermediate Join/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II.html rename to SQL/Leetcode/Intermediate Select/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II.html diff --git a/SQL/Leetcode/Intermediate Join/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_pandas.md b/SQL/Leetcode/Intermediate Select/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_pandas.md similarity index 100% rename from SQL/Leetcode/Intermediate Join/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_pandas.md rename to SQL/Leetcode/Intermediate Select/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_pandas.md diff --git a/SQL/Leetcode/Intermediate Join/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_postgre.md b/SQL/Leetcode/Intermediate Select/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_postgre.md similarity index 100% rename from SQL/Leetcode/Intermediate Join/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_postgre.md rename to SQL/Leetcode/Intermediate Select/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II_postgre.md From d0288495c3de2772e8271b2b185dc2d482a29682 Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Tue, 17 Feb 2026 10:03:00 +0900 Subject: [PATCH 2/4] fix: Address code review findings in Debounce implementation - Fixed SVG marker ID collisions in README_react.html (reactArrowGreen/Red) - Replaced dev CDN bundles with production + SRI hashes for security - Removed unreachable guard in auto-play useEffect - Clarified t=0 behavior with race condition warnings - Fixed debounce_async type hints (Callable -> Callable[..., Coroutine]) - Enhanced decorator syntax documentation with correct/incorrect examples - Added proper closure to Python example code - Restructured Debounce_TS.ipynb with executable code cells - Updated t=0 docs to explain event-loop tick scheduling vs synchronous --- .../Debounce_TS.ipynb | 76 +++++++++++++------ .../Claude Code Sonnet 4.5 extended/README.md | 29 +++++-- .../README_react.html | 57 +++++++++----- 3 files changed, 117 insertions(+), 45 deletions(-) diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb index 24219cac..058c2d45 100644 --- a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb @@ -44,19 +44,39 @@ "**TypeScript特有の最適化ポイント**:\n", "- `ReturnType`による型推論活用\n", "- strict nullチェックによる安全なタイマー管理\n", - "- クロージャによる状態カプセル化\n", - "\n", + "- クロージャによる状態カプセル化" + ] + }, + { + "cell_type": "markdown", + "id": "code-header", + "metadata": {}, + "source": [ "## 4. 実装コード\n", "\n", - "```typescript\n", - "// Analyze Complexity\n", - "// Runtime 48 ms\n", - "// Beats 71.15%\n", - "// Memory 54.18 MB\n", - "// Beats 95.14%\n", - "\n", - "type F = (...args: number[]) => void\n", - "\n", + "### LeetCode Performance\n", + "- Runtime: 48 ms (Beats 71.15%)\n", + "- Memory: 54.18 MB (Beats 95.14%)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "type-definition", + "metadata": {}, + "outputs": [], + "source": [ + "// Type definition for function signature\n", + "type F = (...args: number[]) => void" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "debounce-implementation", + "metadata": {}, + "outputs": [], + "source": [ "/**\n", " * 関数の実行をデバウンスする(遅延実行&キャンセル機能付き)\n", " * @param fn - デバウンス対象の関数\n", @@ -79,16 +99,28 @@ " fn(...args);\n", " }, t);\n", " };\n", - "}\n", - "\n", - "/**\n", - " * const log = debounce(console.log, 100);\n", - " * log('Hello'); // cancelled\n", - " * log('Hello'); // cancelled\n", - " * log('Hello'); // Logged at t=100ms\n", - " */\n", - "```\n", - "\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "usage-example", + "metadata": {}, + "outputs": [], + "source": [ + "// Usage example\n", + "const log = debounce(console.log, 100);\n", + "log('Hello'); // cancelled\n", + "log('Hello'); // cancelled\n", + "log('Hello'); // Logged at t=100ms" + ] + }, + { + "cell_type": "markdown", + "id": "explanation", + "metadata": {}, + "source": [ "## 5. 実装の詳細説明\n", "\n", "### コア機能\n", @@ -160,7 +192,7 @@ "\n", "### エッジケース対応\n", "\n", - "- `t = 0`: 即座に実行(実質debounceなし)\n", + "- `t = 0`: `setTimeout(fn, 0)`は次のイベントループティックで実行される(同期的ではない)。連続呼び出しでは最後の呼び出しのみが実行されるdebounce動作は維持される\n", "- 連続呼び出し: 最後の呼び出しのみが実行される\n", "- 引数なし: 正常に動作(`...args`が空配列)" ] diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md index 94ad0229..2e4b437b 100644 --- a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md @@ -239,7 +239,11 @@ if __name__ == "__main__": dlog(2) # 2が125ms(75 + 50)に実行される - time.sleep(0.1) # 待機 + time.sleep(0.1) # 待機してタイマーを発火させる + + # タイマースレッドが実行を完了するのを待つ + # 注: threading.Timerはデーモンスレッドではないため、 + # メインスレッド終了後も実行される ``` ### 主要ステップの説明 @@ -271,8 +275,12 @@ if __name__ == "__main__": ```python # asyncio版(参考) import asyncio +from typing import Callable, Coroutine, Any -def debounce_async(fn: Callable, t: float) -> Callable: +def debounce_async(fn: Callable, t: float) -> Callable[..., Coroutine[Any, Any, None]]: + """ + Returns a coroutine function that must be awaited when called. + """ task: asyncio.Task | None = None async def debounced(*args, **kwargs): @@ -337,10 +345,10 @@ class Debouncer: ```python dlog = debounce(log, 0) -dlog("instant") # 即座に実行(デバウンスなし) +dlog("instant") # Timer(0, fn, ...)でスレッド起動 ``` -**期待動作**: タイマーは即座に発火、実質的にデバウンスなし +**期待動作**: `Timer(0, fn, ...)` はスケジューラによる遅延があり、完全に即座ではない。連続呼び出しでは前のタイマーがキャンセル前に発火する可能性がある。本当に即座に実行したい場合は `log` を直接呼び出すか、小さい正の値(例: 1ms)を使用することを推奨。 ### 2. 連続呼び出し @@ -424,9 +432,15 @@ dlog_long("delayed") **A**: はい。以下のように使用可能: ```python -@debounce(fn=lambda: None, t=100) # ❌ 引数が合わない +# ❌ 間違った使い方 - debounceは直接デコレータとして使えない +# @debounce # これはエラーになる -# 正しいデコレータ化 +# ❌ 技術的には動くが意味的に間違い +@debounce(fn=lambda: None, t=100) +# 問題: fnをキーワード引数で渡すと、lambda関数がデバウンスされ、 +# 装飾対象の関数は単なる引数として扱われてしまう + +# ✅ 正しいデコレータ化の方法1: ラッパー関数を使う def debounce_decorator(t: float): def decorator(fn: Callable): return debounce(fn, t) @@ -435,6 +449,9 @@ def debounce_decorator(t: float): @debounce_decorator(100) def my_func(): print("Called") + +# ✅ 正しい使い方2: 直接呼び出す +my_func_debounced = debounce(my_func, 100) ``` ### Q4. JavaScriptのdebounceとの違いは? diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html index 4ae0f39d..00dac04e 100644 --- a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html @@ -776,22 +776,51 @@

実装比較

- - + + - + - - - - - + + + + + + From d8cbb857566a003dcd52cc8c0088105deed43caa Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Tue, 17 Feb 2026 10:26:34 +0900 Subject: [PATCH 4/4] fix: Address second round of code review findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove redundant null-check before clearTimeout in Debounce_TS.ipynb (clearTimeout is null-safe per spec) - Add SVG accessibility: role='img' and aria-label on React visualization - Remove unused SVG marker definitions (arrowVis, reactArrowGreen, reactArrowRed) that had zero references in the component - Fix remaining t=0 misleading text in README.md 基底条件 section - Add expected output to Python example for completeness - Fix stale 'Production with SRI' comment in README_react.html --- .../Debounce_TS.ipynb | 14 +++---- .../Claude Code Sonnet 4.5 extended/README.md | 8 ++-- .../README_react.html | 37 ++----------------- 3 files changed, 13 insertions(+), 46 deletions(-) diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb index 058c2d45..8671b83d 100644 --- a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/Debounce_TS.ipynb @@ -89,10 +89,8 @@ " let timeoutId: ReturnType | null = null;\n", " \n", " return function(...args: number[]): void {\n", - " // 既存のタイマーがあればキャンセル\n", - " if (timeoutId !== null) {\n", - " clearTimeout(timeoutId);\n", - " }\n", + " // 既存のタイマーをキャンセル(clearTimeoutはnull安全)\n", + " clearTimeout(timeoutId);\n", " \n", " // 新しいタイマーをセット(t ミリ秒後に fn を実行)\n", " timeoutId = setTimeout(() => {\n", @@ -127,7 +125,7 @@ "\n", "1. **タイマー管理**\n", " - `timeoutId`変数で現在のタイマーを追跡\n", - " - `null`チェックで型安全性を確保\n", + " - `clearTimeout`はnull/undefinedを安全に受け付ける\n", "\n", "2. **キャンセルメカニズム**\n", " - 新しい呼び出し時に既存タイマーを`clearTimeout`でクリア\n", @@ -143,8 +141,8 @@ "// ✅ 型安全な実装\n", "let timeoutId: ReturnType | null = null;\n", "// - ReturnType: setTimeoutの戻り値型を自動推論\n", - "// - | null: 初期状態とクリア後の状態を表現\n", - "// - strict null checkで安全性確保\n", + "// - | null: 初期状態を表現\n", + "// - clearTimeout(null)は仕様上安全(no-op)\n", "\n", "// ✅ 引数の型保持\n", "return function(...args: number[]): void {\n", @@ -174,7 +172,7 @@ "### 型安全性の活用\n", "\n", "1. **コンパイル時エラー防止**\n", - " - `timeoutId`の`null`チェックで未初期化エラーを防止\n", + " - `timeoutId`の型で未初期化状態を安全に管理\n", " - strict modeでのnull安全性確保\n", "\n", "2. **型推論による開発効率**\n", diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md index 2e4b437b..fad92015 100644 --- a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README.md @@ -102,7 +102,7 @@ graph LR ### 基底条件 -- `t = 0` の場合: 即座に実行(実質的にデバウンスなし) +- `t = 0` の場合: `Timer(0, fn, ...)` でスレッドが起動されるため完全に即座ではなく、連続呼び出しではレースの可能性がある - 引数なしの場合: 空の `*args, **kwargs` で正常動作 ### 終了性 @@ -241,9 +241,9 @@ if __name__ == "__main__": # 2が125ms(75 + 50)に実行される time.sleep(0.1) # 待機してタイマーを発火させる - # タイマースレッドが実行を完了するのを待つ - # 注: threading.Timerはデーモンスレッドではないため、 - # メインスレッド終了後も実行される + # 期待出力(タイムスタンプは実行環境依存): + # [xxx.xxx] Called with: (2,) + # → dlog(1)はキャンセルされ、dlog(2)のみが実行される ``` ### 主要ステップの説明 diff --git a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html index 42e0c10b..9969a9e9 100644 --- a/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html +++ b/JavaScript/2627. Debounce/Claude Code Sonnet 4.5 extended/README_react.html @@ -776,7 +776,7 @@

実装比較

- +