Skip to content

Commit d59dc09

Browse files
authored
Merge pull request #262 from myoshi2891/dev-from-macmini
Update Array Reduce Transformation docs and notebook
2 parents c4514d5 + 7e0e0e0 commit d59dc09

3 files changed

Lines changed: 121 additions & 40 deletions

File tree

JavaScript/2626. Array Reduce Transformation/Claude Code Sonnet 4.5 extended/ArrayReduceTransformation_TS.ipynb

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@
205205
"\n",
206206
"**改善ポイント**:\n",
207207
"- ✅ 空配列チェックを削除(オーバーヘッド削減)\n",
208-
"- ✅ 変数名を短縮(`accumulator` → `val`)でメモリアクセス最適化\n",
208+
"- ✅ 変数名を短縮(`accumulator` → `val`)については、可読性と記述の簡潔さを優先しました(ランタイム性能への影響はありません)\n",
209209
"- ✅ 極限までシンプルに\n",
210210
"\n",
211211
"**予想パフォーマンス**: Runtime ~42-45ms, Memory ~55-56MB\n",
@@ -275,8 +275,8 @@
275275
"```\n",
276276
"\n",
277277
"**改善ポイント**:\n",
278-
"- ✅ ループアンローリングでブランチ予測とパイプライン効率を向上\n",
279-
"- ✅ 4要素ずつ処理することで、ループオーバーヘッドを75%削減\n",
278+
"- ⚠️ ループアンローリングは理論上オーバーヘッドを削減しますが、現代のJITでは自動最適化されるため、手動で行うとむしろ遅くなる場合があります(実測: 47ms vs 40ms)\n",
279+
"- ⚠️ ワークロードやランタイム環境に依存するため、常に有効とは限りません\n",
280280
"\n",
281281
"**予想パフォーマンス**: Runtime ~38-42ms(大きな配列で効果大)\n",
282282
"\n",
@@ -346,7 +346,7 @@
346346
" \"target\": \"ES2022\",\n",
347347
" \"module\": \"ES2022\",\n",
348348
" \"strict\": true,\n",
349-
" \"noUncheckedIndexedAccess\": false,\n",
349+
" \"noUncheckedIndexedAccess\": true,\n",
350350
" \"skipLibCheck\": true\n",
351351
" }\n",
352352
"}\n",
@@ -375,6 +375,55 @@
375375
"- ✅ 可読性: 維持\n",
376376
"- ✅ 保守性: 維持"
377377
]
378+
},
379+
{
380+
"cell_type": "code",
381+
"execution_count": null,
382+
"id": "d2b4f3e0",
383+
"metadata": {},
384+
"outputs": [],
385+
"source": [
386+
"/**\n",
387+
" * Demonstration of the reduce function\n",
388+
" */\n",
389+
"type Fn = (accum: number, curr: number) => number;\n",
390+
"\n",
391+
"function arrayReduce(nums: number[], fn: Fn, init: number): number {\n",
392+
" let val = init;\n",
393+
" for (let i = 0; i < nums.length; i++) {\n",
394+
" val = fn(val, nums[i]);\n",
395+
" }\n",
396+
" return val;\n",
397+
"}\n",
398+
"\n",
399+
"// Test cases\n",
400+
"const nums1 = [1, 2, 3, 4];\n",
401+
"const fn1 = (acc: number, curr: number) => acc + curr;\n",
402+
"const init1 = 0;\n",
403+
"\n",
404+
"const nums2 = [1, 2, 3, 4];\n",
405+
"const fn2 = (acc: number, curr: number) => acc * curr;\n",
406+
"const init2 = 1;\n",
407+
"\n",
408+
"const nums3: number[] = [];\n",
409+
"const fn3 = (acc: number, curr: number) => 0;\n",
410+
"const init3 = 25;\n",
411+
"\n",
412+
"console.log(\"Test Case 1: Sum\");\n",
413+
"console.log(`Input: nums = [${nums1}], init = ${init1}`);\n",
414+
"console.log(`Output: ${arrayReduce(nums1, fn1, init1)}`);\n",
415+
"console.log(\"Expected: 10\");\n",
416+
"\n",
417+
"console.log(\"\\nTest Case 2: Product\");\n",
418+
"console.log(`Input: nums = [${nums2}], init = ${init2}`);\n",
419+
"console.log(`Output: ${arrayReduce(nums2, fn2, init2)}`);\n",
420+
"console.log(\"Expected: 24\");\n",
421+
"\n",
422+
"console.log(\"\\nTest Case 3: Empty Array\");\n",
423+
"console.log(`Input: nums = [], init = ${init3}`);\n",
424+
"console.log(`Output: ${arrayReduce(nums3, fn3, init3)}`);\n",
425+
"console.log(\"Expected: 25\");"
426+
]
378427
}
379428
],
380429
"metadata": {
@@ -392,4 +441,4 @@
392441
},
393442
"nbformat": 4,
394443
"nbformat_minor": 5
395-
}
444+
}

JavaScript/2626. Array Reduce Transformation/Claude Code Sonnet 4.5 extended/README.md

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,9 @@ class Solution:
165165
# 累積値を初期値で初期化
166166
accumulator: int = init
167167

168-
# 配列長をキャッシュ(毎回の len() 呼び出しを回避)
169-
length: int = len(nums)
170-
171168
# 各要素に対してreducer関数を順次適用
172-
for i in range(length):
173-
accumulator = fn(accumulator, nums[i])
169+
for num in nums:
170+
accumulator = fn(accumulator, num)
174171

175172
# 最終累積値を返す(空配列の場合は init がそのまま返る)
176173
return accumulator
@@ -197,19 +194,20 @@ class Solution:
197194

198195
<h2 id="cpython">CPython最適化ポイント</h2>
199196

200-
### 1. 属性アクセスの削減
197+
### 1. 直接反復(Direct Iteration)の推奨
201198

202199
```python
203-
# ❌ 遅い: 毎回 len(nums) を呼び出す
200+
# ❌ 遅い: インデックスアクセスに伴うオーバーヘッド(__getitem__呼び出し)
204201
for i in range(len(nums)):
205202
accumulator = fn(accumulator, nums[i])
206203

207-
# ✅ 速い: len() の結果をキャッシュ
208-
length = len(nums)
209-
for i in range(length):
210-
accumulator = fn(accumulator, nums[i])
204+
# ✅ 速い: 直接反復で要素を取得(内部イテレータが効率的)
205+
for num in nums:
206+
accumulator = fn(accumulator, num)
211207
```
212208

209+
**解説**: `range(len(nums))` における `len()` は1回しか評価されませんが、ループ内での `nums[i]` は毎回 `__getitem__` を呼び出し、境界チェックも行います。直接反復の方が一般的に高速です。
210+
213211
### 2. 不要な条件分岐の回避
214212

215213
```python
@@ -226,9 +224,9 @@ for i in range(len(nums)):
226224

227225
### 3. ループ方式の選択
228226

229-
- **`range(len(nums))`**: インデックスベースで最速
230-
- **`for num in nums`**: イテレータ生成のオーバーヘッドあり
231-
- **`enumerate(nums)`**: さらにオーバーヘッド増加
227+
- **`for num in nums`**: 最速(`__getitem__`オーバーヘッドなし)
228+
- **`range(len(nums))`**: インデックスが必要な場合のみ使用
229+
- **`enumerate(nums)`**: インデックスと値の両方が必要な場合に使用(若干のタプル生成コストあり)
232230

233231
### 4. 型ヒントの影響
234232

@@ -300,9 +298,9 @@ assert Solution().reduce([1000], lambda a, x: a + x, 1000) == 2000
300298

301299
**A**: 本問題は reduce の内部動作を理解するための教育的課題。実務では `functools.reduce` を使うべき。
302300

303-
### Q2: `len(nums)` のキャッシングは本当に速いのか
301+
### Q2: `range(len(nums))` では `len()` が毎回呼ばれるのか
304302

305-
**A**: CPythonでは `len()` は O(1) だが、関数呼び出しのオーバーヘッドがある。ループ内で毎回呼び出すより、事前にキャッシュする方が 5-10% 高速
303+
**A**: いいえ。`range` オブジェクト生成時に1回だけ評価されます。ただし、ループ内で `nums[i]` を使うとインデックスアクセスのコストがかかるため、直接反復(`for num in nums`)の方が効率的です
306304

307305
### Q3: 再帰実装の方が関数型的では?
308306

@@ -319,7 +317,7 @@ def reduce_recursive(self, nums: list[int], fn: Callable, init: int) -> int:
319317

320318
### Q4: `for num in nums` の方が Pythonic では?
321319

322-
**A**: 可読性では優れるが、インデックスベースの `range(len(nums))` の方が 3-5% 高速。LeetCodeのような競技環境では後者を推奨
320+
**A**: はい。可読性が高く、かつ CPython では `__getitem__` のオーバーヘッドを回避できるため、パフォーマンス面でも(多くの場合)有利です
323321

324322
### Q5: 空配列チェックを追加すべきか?
325323

JavaScript/2626. Array Reduce Transformation/Claude Code Sonnet 4.5 extended/README_react.html

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
<!-- Tailwind CSS -->
99
<script src="https://cdn.tailwindcss.com"></script>
10+
<!-- Note: Tailwind CDN is for development/prototyping only. -->
1011

1112
<!-- Google Fonts -->
1213
<link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -20,25 +21,41 @@
2021
<link
2122
rel="stylesheet"
2223
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css"
24+
integrity="sha384-wFjoQjtV1y5jVHbt0p35Ui8aV8GVpEZkyF99OXWqP/eNJDU93D3Ugxkoyh6Y2I4A"
25+
crossorigin="anonymous"
2326
/>
2427
<link
2528
rel="stylesheet"
2629
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css"
30+
integrity="sha384-nUkTNLI8COlMCRJ0FHIdX76If83145OTCLUx4gQyfnO0gGeO/sD9czGEUBxtkcUv"
31+
crossorigin="anonymous"
2732
/>
2833
<link
2934
rel="stylesheet"
3035
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css"
36+
integrity="sha384-EUzJ34/1CCeefTGUKLgvA5Z/vYIwi+Jyu8aAaCfFDxfwZ3Xs3OfkkIeegsLRM11e"
37+
crossorigin="anonymous"
3138
/>
3239

3340
<!-- React & ReactDOM -->
34-
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
3541
<script
36-
crossorigin
37-
src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
42+
crossorigin="anonymous"
43+
src="https://unpkg.com/react@18/umd/react.production.min.js"
44+
integrity="sha384-DGyLxAyjq0f9SPpVevD6IgztCFlnMF6oW/XQGmfe+IsZ8TqEiDrcHkMLKI6fiB/Z"
45+
></script>
46+
<script
47+
crossorigin="anonymous"
48+
src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"
49+
integrity="sha384-gTGxhz21lVGYNMcdJOyq01Edg0jhn/c22nsx0kyqP0TxaV5WVdsSH1fSDUf5YJj1"
3850
></script>
3951

4052
<!-- Babel Standalone -->
41-
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
53+
<script
54+
src="https://unpkg.com/@babel/standalone/babel.min.js"
55+
integrity="sha384-Fo0OdKhdnE7y2WmzjOMW4PYjHkkANeu1501pWTqKrzAPeJMFQb4ZTdAA9dtrVUJV"
56+
crossorigin="anonymous"
57+
></script>
58+
<!-- Note: Runtime transpilation is for demos only. -->
4259

4360
<style>
4461
html {
@@ -321,7 +338,7 @@ <h3 class="text-xl font-semibold text-teal-800 mt-8 mb-3">最適化ポイント<
321338
</h2>
322339
<div class="mt-[20px] overflow-x-auto">
323340
<svg
324-
viewBox="0 0 840 700"
341+
viewBox="0 0 840 620"
325342
style="max-width: 100%; height: auto; color: #333"
326343
role="img"
327344
aria-label="Array Reduce Transformation flowchart"
@@ -862,11 +879,31 @@ <h4 class="font-semibold text-purple-800 mb-3 text-lg">最適化のポイント<
862879
</div>
863880

864881
<!-- Prism.js Scripts -->
865-
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
866-
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-typescript.min.js"></script>
867-
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
868-
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
869-
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
882+
<script
883+
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"
884+
integrity="sha384-06z5D//U/xpvxZHuUz92xBvq3DqBBFi7Up53HRrbV7Jlv7Yvh/MZ7oenfUe9iCEt"
885+
crossorigin="anonymous"
886+
></script>
887+
<script
888+
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-typescript.min.js"
889+
integrity="sha384-PeOqKNW/piETaCg8rqKFy+Pm6KEk7e36/5YZE5XO/OaFdO+/Aw3O8qZ9qDPKVUgx"
890+
crossorigin="anonymous"
891+
></script>
892+
<script
893+
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"
894+
integrity="sha384-nUkTNLI8COlMCRJ0FHIdX76If83145OTCLUx4gQyfnO0gGeO/sD9czGEUBxtkcUv"
895+
crossorigin="anonymous"
896+
></script>
897+
<script
898+
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"
899+
integrity="sha384-EUzJ34/1CCeefTGUKLgvA5Z/vYIwi+Jyu8aAaCfFDxfwZ3Xs3OfkkIeegsLRM11e"
900+
crossorigin="anonymous"
901+
></script>
902+
<script
903+
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"
904+
integrity="sha384-ZdEfx8sYX8i4IVXU1tUbqwOp4PBUCCmnpagpiHchnstXkEczkzPfUd9fvBrntM+F"
905+
crossorigin="anonymous"
906+
></script>
870907

871908
<!-- React Component -->
872909
<script type="text/babel">
@@ -1323,12 +1360,7 @@ <h4 class="font-semibold text-purple-800 mb-3 text-lg">最適化のポイント<
13231360
// 自動再生ロジック(v3.3)
13241361
useEffect(() => {
13251362
if (isPlaying) {
1326-
if (activeStep > stepsData.length) {
1327-
setIsPlaying(false);
1328-
setActiveStep(1);
1329-
return;
1330-
}
1331-
1363+
// Note: activeStep > stepsData.length check is unreachable due to handlers constrains
13321364
timerRef.current = setTimeout(() => {
13331365
if (activeStep === stepsData.length) {
13341366
setActiveStep(1);
@@ -1393,8 +1425,10 @@ <h3 className="mt-0 mb-4 text-teal-800 text-xl font-semibold">
13931425
'bg-white border-slate-200 hover:border-emerald-500 hover:translate-x-1',
13941426
isActive
13951427
? 'bg-[linear-gradient(135deg,#d1fae5,#a7f3d0)] border-emerald-500 shadow-[0_4px_12px_rgba(16,185,129,0.20)]'
1396-
: '',
1397-
].join(' ')}
1428+
: null,
1429+
]
1430+
.filter(Boolean)
1431+
.join(' ')}
13981432
onClick={() => handleStepClick(step.step)}
13991433
aria-label={`ステップ${step.step}: ${step.title}`}
14001434
aria-current={isActive ? 'step' : undefined}

0 commit comments

Comments
 (0)