# 機能設計書 48-Enumeratorクラス

## 概要

本ドキュメントは、mruby-enumerator gemが提供するEnumeratorクラスの機能設計を記述する。内部イテレータと外部イテレータの両方を提供し、Fiberベースの遅延イテレーションを実現する。Kernel#to_enum/enum_forや、Enumerable#zip, chunk, chunk_whileの拡張も含む。

### 本機能の処理概要

mruby-enumerator gemは、RubyのEnumeratorクラスをmrubyに提供する。全てRuby(mrblib/enumerator.rb)で実装されており、内部的にFiberを使用して外部イテレーション（next/peek）を実現する。Enumeratorは任意のオブジェクトのイテレーションメソッドをラップし、内部イテレータ（each）と外部イテレータ（next/peek）の両方のインターフェースを提供する。

**業務上の目的・背景**：Enumeratorは、Rubyのイテレーションパターンの中核を担うクラスである。ブロックなしでイテレーションメソッドを呼んだ時にEnumeratorを返すパターン（例：`array.each`がEnumeratorを返す）は、遅延評価やメソッドチェーンを可能にする。また、外部イテレータとしてnext/peekメソッドを提供することで、複数のイテレータの同期的な操作が可能になる。

**機能の利用シーン**：ブロックなしでイテレーションメソッドを呼んだ場合（`[1,2,3].each`）、外部イテレータとしてnext/peekを使う場合、with_index/with_objectでイテレーションに追加情報を付与する場合、Enumerator.newでカスタムイテレータを作成する場合、Enumerator.produceで無限シーケンスを生成する場合に使用される。

**主要な処理内容**：
1. Enumerator.new - ブロックまたはオブジェクト+メソッドからEnumeratorを生成
2. Enumerator#each - 内部イテレーションの実行
3. Enumerator#next / next_values - 外部イテレーション（Fiberベース）
4. Enumerator#peek / peek_values - 先読み（次の値を消費せずに取得）
5. Enumerator#rewind - イテレーション位置のリセット
6. Enumerator#feed - yieldの戻り値を設定
7. Enumerator#with_index / with_object - インデックス/オブジェクト付きイテレーション
8. Kernel#to_enum / enum_for - 任意オブジェクトからEnumeratorを生成
9. Enumerator.produce - 無限シーケンス生成
10. Enumerable#zip, chunk, chunk_while - Enumeratorベースの拡張

**関連システム・外部連携**：mruby-fiberに依存する（外部イテレーション用）。

**権限による制御**：なし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はライブラリ機能であり、画面との関連はない |

## 機能種別

イテレーションライブラリ（内部/外部イテレータ）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| obj (initialize) | Object | Conditional | イテレーション対象オブジェクト | blockなしの場合必須 |
| meth (initialize) | Symbol | No | 呼び出すメソッド名（デフォルト:each） | - |
| args (initialize) | Any | No | メソッドに渡す引数 | - |
| block (initialize) | Block | Conditional | Yielder経由でイテレーションを定義 | objなしの場合必須 |
| offset (with_index) | Integer | No | インデックス開始値（デフォルト0） | 整数に変換可能 |
| object (with_object) | Object | Yes | イテレーションに付随するオブジェクト | - |
| init (produce) | Object | No | 初期値 | - |
| block (produce) | Block | Yes | 各値を生成するブロック | 必須 |

### 入力データソース

メソッド呼び出し時の引数、ブロック、およびイテレーション対象オブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| each戻り値 | Object | イテレーションメソッドの戻り値 |
| next戻り値 | Object | 次の要素（単値） |
| next_values戻り値 | Array | 次の要素（配列） |
| peek戻り値 | Object | 先読み要素（単値） |
| peek_values戻り値 | Array | 先読み要素（配列） |
| with_index戻り値 | Enumerator/Object | ブロックなしならEnumerator |
| size戻り値 | Integer/Float/nil | イテレータのサイズ |

### 出力先

メソッドの戻り値として呼び出し元に返却

## 処理フロー

### 処理シーケンス

```
1. Enumerator.new { |yielder| ... }
   ├─ GeneratorオブジェクトをProcから生成
   └─ @obj=Generator, @meth=:each として保持

2. enum.next (外部イテレーション)
   ├─ @lookaheadがあれば消費して返す
   ├─ @stop_excがあればStopIteration発生
   ├─ @fibが未初期化 or dead の場合:
   │   └─ 新しいFiberを作成
   │       └─ Fiber内でeachを呼び、各要素でFiber.yield
   ├─ @fib.resumeで次の要素を取得
   └─ @stop_exc設定済みならStopIteration発生

3. enum.each(&block)
   ├─ 追加引数ありの場合: dupして@argsに追加
   ├─ ブロックなしの場合: selfを返す
   └─ @obj.__send__(@meth, *@args, **@kwd, &block)で実行
```

### フローチャート

```mermaid
flowchart TD
    A[enum.next] --> B{@lookahead?}
    B -->|Yes| C[lookahead消費して返す]
    B -->|No| D{@stop_exc?}
    D -->|Yes| E[StopIteration発生]
    D -->|No| F{@fib alive?}
    F -->|No| G[新Fiber作成]
    G --> H[Fiber内でeach実行]
    F -->|Yes| I[@fib.resume]
    H --> I
    I --> J{@stop_exc設定?}
    J -->|Yes| K[StopIteration発生]
    J -->|No| L[値を返す]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-48-01 | Fiberベース外部イテレーション | nextはFiberを使って遅延的に次の値を取得 | next/peek呼び出し時 |
| BR-48-02 | StopIteration終了 | イテレーション終了時にStopIterationが発生し、resultに最終値が設定される | next呼び出し時 |
| BR-48-03 | 先読みキャッシュ | peekで取得した値は@lookaheadにキャッシュされ、次のnextで消費される | peek後のnext呼び出し時 |
| BR-48-04 | feed値注入 | feed()で設定した値がyieldの戻り値として次のnext時に渡される | feed後のnext呼び出し時 |
| BR-48-05 | rewindリセット | rewindはFiber・先読み・feed値を全てリセットし、objにもrewindを呼ぶ | rewind呼び出し時 |
| BR-48-06 | 引数追加each | each(args)で追加引数を渡すと、既存の@argsに連結される | each(args)呼び出し時 |

### 計算ロジック

sizeの計算: @sizeプロパティがあればそれを返す。なければ@objにsizeメソッドがあれば委譲。

## データベース操作仕様

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | Enumerator.new()に引数もブロックもない場合 | ブロックまたはオブジェクトを渡す |
| - | StopIteration | nextでイテレーション終了時 | rescue StopIterationで捕捉 |
| - | TypeError | feedで既に値が設定済みの場合 | nextで消費してからfeedする |
| - | TypeError | initialize_copyで非Enumeratorをコピーしようとした場合 | Enumeratorを渡す |
| - | TypeError | initialize_copyで実行中Fiberのコピーを試みた場合 | rewind後にコピーする |

### リトライ仕様

リトライは不要

## トランザクション仕様

該当なし

## パフォーマンス要件

- each: 委譲先メソッドの性能に依存
- next/peek: Fiber切り替えのオーバーヘッドあり（Fiberのresume/yield）
- Enumerator.produce: 各要素生成にブロック呼び出し1回

## セキュリティ考慮事項

Enumerator内部でFiberを使用するため、Fiberのメモリ使用に注意。無限Enumerator（Enumerator.produce等）をto_aすると無限ループになる。

## 備考

- mruby-fiberに依存する（外部イテレーション用）
- Generator/Yielderは内部クラス（Enumerator内に定義）
- NONEは未指定引数の検出用センチネル値
- Enumerator.produceはStopIterationで停止するまで値を生成
- Enumerable#zipはEnumeratorのnextを使用してイテレーション
- Enumerable#chunkは:_alone/:_separatorの特殊値をサポート

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | enumerator.rb | `mrbgems/mruby-enumerator/mrblib/enumerator.rb` | Enumeratorのインスタンス変数（117-133行目） |

**読解のコツ**: Enumeratorは以下のインスタンス変数を持つ: `@obj`(対象オブジェクト), `@meth`(メソッド名), `@args`(引数), `@kwd`(キーワード引数), `@fib`(外部イテレーション用Fiber), `@dst`(遷移先Fiber), `@lookahead`(先読みバッファ), `@feedvalue`(feed値), `@stop_exc`(終了例外)

#### Step 2: 初期化と内部イテレーションを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | enumerator.rb | `mrbgems/mruby-enumerator/mrblib/enumerator.rb` | initialize, each, __enumerator_block_call |

**主要処理フロー**:
- **117-133行目**: `initialize` - ブロックがあればGenerator生成、なければobj+meth+argsを保持
- **304-319行目**: `each` - 追加引数のマージ処理後、__enumerator_block_call
- **321-323行目**: `__enumerator_block_call` - `@obj.__send__(@meth, *@args, **@kwd, &block)`

#### Step 3: 外部イテレーション(next/peek)を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | enumerator.rb | `mrbgems/mruby-enumerator/mrblib/enumerator.rb` | next, next_values, peek, peek_values |

**主要処理フロー**:
- **346-348行目**: `next` - next_valuesの結果を__svalueで単値変換
- **394-432行目**: `next_values` - Fiberベースの外部イテレーション核心部
  - **395-399行目**: @lookaheadチェック
  - **400行目**: @stop_excチェック
  - **404-421行目**: Fiberの(再)作成 - each内でFiber.yieldを使用
  - **423行目**: @fib.resumeで次の値を取得
- **454-456行目**: `peek` - peek_valuesの結果を__svalueで単値変換
- **484-489行目**: `peek_values` - @lookaheadにキャッシュ

#### Step 4: rewind/feedを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | enumerator.rb | `mrbgems/mruby-enumerator/mrblib/enumerator.rb` | rewind, feed |

**主要処理フロー**:
- **499-507行目**: `rewind` - @objにrewindがあれば呼ぶ、@fib/@dst/@lookahead/@feedvalue全リセット
- **554-558行目**: `feed` - @feedvalueが既にセットされていればTypeError

#### Step 5: Generator/Yielder内部クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | enumerator.rb | `mrbgems/mruby-enumerator/mrblib/enumerator.rb` | Generator, Yielder |

**主要処理フロー**:
- **561-573行目**: `Generator` - Procをラップし、each時にYielderを引数にしてProc呼び出し
- **576-591行目**: `Yielder` - yield/<<メソッドでブロックを呼び出す

#### Step 6: Kernel#to_enumとEnumerable拡張を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | enumerator.rb | `mrbgems/mruby-enumerator/mrblib/enumerator.rb` | Kernel#to_enum, Enumerable#zip/chunk/chunk_while |

**主要処理フロー**:
- **681-684行目**: `Kernel#to_enum` - `Enumerator.new(self, meth, *args, **kwd)`
- **689-722行目**: `Enumerable#zip` - 各引数をto_enum(:each)で変換、next使用
- **764-790行目**: `Enumerable#chunk` - :_alone/:_separatorの特殊値処理
- **842-860行目**: `Enumerable#chunk_while` - 隣接要素の比較でチャンク分割

### プログラム呼び出し階層図

```
Enumerator.new(obj, meth, *args, &block)
    |
    +-- block指定時: Generator.new(&block)
    |       +-- @proc = block
    |       +-- each: Yielder.new(&block) を@procに渡す
    |               +-- yield/<<: block.call(*args)
    |
    +-- enum.each(&block)
    |       +-- __enumerator_block_call
    |               +-- @obj.__send__(@meth, *@args, **@kwd, &block)
    |
    +-- enum.next
    |       +-- next_values
    |               +-- Fiber.new { each { |*args| Fiber.yield args } }
    |               +-- @fib.resume
    |
    +-- enum.peek
    |       +-- peek_values
    |               +-- next_values (キャッシュ)
    |
    +-- Kernel#to_enum(meth, *args)
            +-- Enumerator.new(self, meth, *args)
```

### データフロー図

```
[入力]                          [処理]                        [出力]

Object + :each           --> Enumerator.new(obj, :each) --> Enumerator
Enumerator + block       --> enum.each(&block)          --> イテレーション結果
Enumerator               --> enum.next                  --> 次の要素
                             (Fiber.resume)
Enumerator               --> enum.peek                  --> 先読み要素
                             (@lookaheadキャッシュ)
Object + value           --> enum.feed(value)           --> nil (次のyield戻り値に設定)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| enumerator.rb | `mrbgems/mruby-enumerator/mrblib/enumerator.rb` | ソース | Enumerator・Generator・Yielder・Kernel拡張・Enumerable拡張の全Ruby実装 |
| mrbgem.rake | `mrbgems/mruby-enumerator/mrbgem.rake` | 設定 | gem定義ファイル（mruby-fiber依存） |
| enumerator.rb | `mrbgems/mruby-enumerator/test/enumerator.rb` | テスト | Enumeratorクラスのテスト |
