Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ python generate_index.py # public/index.html 再生成
- `file://` URLはブロックされる → `python -m http.server 8765 --directory public` でローカルサーバー起動
- `ruff` / `black` はグローバル未インストールの場合あり → `python -c "import py_compile; ..."` でシンタックスチェック代替

### 定性・定量評価のガイドライン

- 実装手法(例: `in` 演算子と `??=` 演算子など)を比較する際、「Aの方がBよりも確実に軽量/高速である」といった絶対的なパフォーマンスの断言は**避ける**こと。
- その代わり、セマンティクスの違い(例: `Object.create(null)` 等)を解説するか、V8エンジンのインラインキャッシュ(IC)などによって「実行環境に依存してパフォーマンスが逆転・変化する」といった中立的な補足を必ず含めること。

## SVGフローチャートガイドライン

`.agent/workflows/svg_flowchart_guidelines.md` に詳細あり。主要ポイント:
Expand Down
5 changes: 5 additions & 0 deletions Gemini.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,8 @@ This file provides guidance to Gemini (or other Google AI assistants) when worki

- `file://` URLでのアクセスはブロックされる前提で設計されています。
- テスト等で確認が必要な場合は `python -m http.server 8765 --directory public` でローカルサーバーを起動してアクセスしてください。

### 5. パフォーマンス等の絶対的評価の回避

- `in` 演算子と `??=` 演算子のような特定の構文において、「Aの方がBよりも確実に軽量/高速である」といった絶対的なパフォーマンスの主張・断言は**避けてください**。
- 代わりに、セマンティクスの違い(例: `Object.create(null)` によるプロトタイプなしオブジェクトに対する純粋な存在確認)を説明するか、V8エンジンの最適化(インラインキャッシュ/IC等)によって「実行環境や状況に依存してパフォーマンスが逆転・変化する」といった中立的な補足を必ず含めてください。
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "a0aa6c59",
"metadata": {},
"source": [
"## 1. 問題の分析\n",
"\n",
"**競技プログラミング視点**\n",
"- 配列を1回走査するだけで分類できるため、O(n)が理論的下限\n",
"- ハッシュマップ(オブジェクト)によるO(1)キーアクセスで最適化\n",
"\n",
"**業務開発視点**\n",
"- `Array.prototype`拡張はグローバルな副作用を持つため、型定義側でインターフェース宣言を行い型安全性を担保\n",
"- `Record<string, T[]>`で戻り値を明確に型付け\n",
"\n",
"**TypeScript特有の考慮点**\n",
"- `this`は`Array.prototype`拡張内では実行時配列を指すが、型は`any[]`扱い → ジェネリクスで安全化\n",
"- `interface Array<T>`のマージ宣言(Declaration Merging)でプロトタイプ拡張を型レベルに反映\n",
"\n",
"---\n",
"\n",
"## 2. アルゴリズムアプローチ比較\n",
"\n",
"| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |\n",
"|---|---|---|---|---|---|---|\n",
"| `for`ループ + オブジェクト | O(n) | O(n) | 低 | 高 | 高 | **最適解** |\n",
"| `reduce` | O(n) | O(n) | 低 | 高 | 高 | 関数型スタイル |\n",
"| `sort` + 走査 | O(n log n) | O(n) | 中 | 中 | 低 | 不要なソートコスト |\n",
"\n",
"---\n",
"\n",
"## 3. 選択したアルゴリズムと理由\n",
"\n",
"- **選択**: `reduce` によるアキュムレータパターン\n",
"- **理由**:\n",
" - O(n)の単一走査で完結、追加メモリはO(n)(結果格納分のみ)\n",
" - `reduce`はイミュータブル志向で副作用がなく純粋関数的\n",
" - TypeScriptの型推論と相性が良く、アキュムレータの型を`Record<string, T[]>`で明示可能\n",
"\n",
"---\n",
"\n",
"## 4. 実装コード\n",
"\n",
"```typescript\n",
"// Analyze Complexity\n",
"// 実測: Runtime 98 ms / Memory 79.27 MB\n",
"// Beats 80.23% / 40.00%\n",
"interface Array<T> {\n",
" groupBy(fn: (item: T) => string): Record<string, T[]>;\n",
"}\n",
"\n",
"Array.prototype.groupBy = function<T>(\n",
" this: T[],\n",
" fn: (item: T) => string\n",
"): Record<string, T[]> {\n",
" return this.reduce<Record<string, T[]>>((acc, item) => {\n",
" const key = fn(item);\n",
" // キーが未存在なら空配列で初期化、存在すれば既存配列を再利用\n",
" (acc[key] ??= []).push(item);\n",
" return acc;\n",
" }, Object.create(null)); // プロトタイプ汚染を防ぐためObject.create(null)\n",
"};\n",
"\n",
"/**\n",
" * [1,2,3].groupBy(String) // {\"1\":[1],\"2\":[2],\"3\":[3]}\n",
" */\n",
"```\n",
"\n",
"---\n",
"\n",
"### 実装の重要ポイント解説\n",
"\n",
"**① `this: T[]` による明示的 `this` 型バインド**\n",
"```typescript\n",
"// NG: thisがany扱いになりTypeScriptの恩恵を受けられない\n",
"Array.prototype.groupBy = function(fn) { ... }\n",
"\n",
"// OK: thisをT[]と明示することでジェネリクスが機能する\n",
"Array.prototype.groupBy = function<T>(this: T[], fn) { ... }\n",
"```\n",
"\n",
"**② `??=`(Nullish coalescing assignment)による簡潔な初期化**\n",
"```typescript\n",
"// 従来の書き方(冗長)\n",
"if (!acc[key]) acc[key] = [];\n",
"acc[key].push(item);\n",
"\n",
"// ??= を使った簡潔な書き方(ES2021+, Node.js v22対応)\n",
"(acc[key] ??= []).push(item);\n",
"```\n",
"\n",
"**③ `Object.create(null)` によるプロトタイプ汚染防止**\n",
"```typescript\n",
"// NG: {}はObject.prototypeを継承するため\n",
"// \"constructor\", \"toString\"等のキーと衝突リスクがある\n",
"const acc = {};\n",
"\n",
"// OK: プロトタイプチェーンを持たない純粋なマップとして機能\n",
"const acc = Object.create(null);\n",
"```\n",
"\n",
"## 現状分析\n",
"\n",
"| 指標 | 現状 | 問題点 |\n",
"|---|---|---|\n",
"| Runtime 98ms | Beats 80.23% | まだ改善余地あり |\n",
"| Memory 79.27MB | **Beats 40.00%** | **ここがボトルネック** |\n",
"\n",
"メモリがボトルネックの主因は **`reduce`のコールバック関数が反復ごとにスタックフレームを生成する**ことです。\n",
"\n",
"---\n",
"\n",
"## ボトルネックの詳細\n",
"\n",
"```\n",
"reduce の内部動作(メモリ観点)\n",
"─────────────────────────────────────────\n",
"item[0] → fn呼び出し → スタックフレーム生成 → pop\n",
"item[1] → fn呼び出し → スタックフレーム生成 → pop\n",
"item[2] → fn呼び出し → スタックフレーム生成 → pop\n",
"...× n回繰り返す ← n=10^5 のとき顕著にメモリ圧迫\n",
"```\n",
"\n",
"```\n",
"for ループの内部動作(メモリ観点)\n",
"─────────────────────────────────────────\n",
"スタックフレームは1つだけ確保 → ループ内で使い回す\n",
"```\n",
"\n",
"---\n",
"\n",
"## 改善コード\n",
"\n",
"```typescript\n",
"// Analyze Complexity\n",
"// 実測: Runtime 101 ms / Memory 74.68 MB\n",
"// Beats 70.34% / 90.11%\n",
"interface Array<T> {\n",
" groupBy(fn: (item: T) => string): Record<string, T[]>;\n",
"}\n",
"\n",
"Array.prototype.groupBy = function<T>(\n",
" this: T[],\n",
" fn: (item: T) => string\n",
"): Record<string, T[]> {\n",
" // ① reduceをforループに変更 → スタックフレームの反復生成を排除\n",
" const result: Record<string, T[]> = Object.create(null);\n",
" const len = this.length; // ② length キャッシュ → プロパティ参照コスト削減\n",
"\n",
" for (let i = 0; i < len; i++) {\n",
" const item = this[i]; // ③ 一時変数でthis参照を1回に抑制\n",
" const key = fn(item);\n",
"\n",
" // ④ Object.create(null) に対するキー存在確認のセマンティクス\n",
" if (key in result) {\n",
" result[key].push(item);\n",
" } else {\n",
" result[key] = [item];\n",
" }\n",
" }\n",
"\n",
" return result;\n",
"};\n",
"```\n",
"\n",
"---\n",
"\n",
"## 改善ポイント詳解\n",
"\n",
"**① `reduce` → `for`ループ**\n",
"```typescript\n",
"// Before: n回のコールバック呼び出し = n個のスタックフレーム生成・破棄\n",
"this.reduce<Record<string, T[]>>((acc, item) => { ... }, Object.create(null));\n",
"\n",
"// After: 1つのスタックフレームを使い回す\n",
"for (let i = 0; i < len; i++) { ... }\n",
"```\n",
"\n",
"**② `length`キャッシュ**\n",
"```typescript\n",
"// Before: ループ毎にthis.lengthプロパティを参照(微小だが積み重なる)\n",
"for (let i = 0; i < this.length; i++)\n",
"\n",
"// After: 変数参照のみ(プロパティルックアップ不要)\n",
"const len = this.length;\n",
"for (let i = 0; i < len; i++)\n",
"```\n",
"\n",
"**③ `??=` と `in` 演算子**\n",
"```typescript\n",
"// パターンA: ??= を用いた記述(V8環境でIC最適化が効きやすい)\n",
"(acc[key] ??= []).push(item);\n",
"\n",
"// パターンB: in 演算子による存在確認(本実装での採用)\n",
"if (key in result) {\n",
" result[key].push(item);\n",
"} else {\n",
" result[key] = [item]; // 初回のみ配列生成\n",
"}\n",
"```\n",
"\n",
"---\n",
"\n",
"## 期待される改善効果\n",
"\n",
"| 指標 | Before(実測) | After(実測) |\n",
"|---|---|---|\n",
"| Runtime | 98 ms (Beats 80.23%) | 101 ms (Beats 70.34%) |\n",
"| Memory | 79.27 MB (Beats 40.00%) | 74.68 MB (Beats 90.11%) |\n",
"| 主な影響 | — | GCの影響によりRuntime微増・Memory大幅改善 |\n",
"\n",
"> **補足**: LeetCodeのメモリ計測はV8エンジンのGCタイミングに依存するため、実行ごとに若干ブレます。複数回提出して中央値で評価することを推奨します。"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "TypeScript",
"language": "typescript",
"name": "typescript"
},
"language_info": {
"file_extension": ".ts",
"mimetype": "text/typescript",
"name": "typescript",
"version": "5.9.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading