# 機能設計書 49-Enumerator::Lazyクラス

## 概要

本ドキュメントは、mruby-enum-lazy gemが提供するEnumerator::Lazyクラスの機能設計を記述する。遅延評価を行うEnumeratorを提供し、無限シーケンスや大規模データに対するフィルタリング・変換操作をメモリ効率的に実行する。

### 本機能の処理概要

mruby-enum-lazy gemは、Enumerator::LazyクラスとEnumerable#lazyメソッドを提供する。全てRuby(mrblib/lazy.rb)で実装されている。Lazyは各要素に対する操作（map, select, reject等）を即座に実行せず、forceやfirst等で要素が実際に必要になるまで評価を遅延させる。これにより、無限シーケンスに対するtake操作や、大規模データのパイプライン処理が可能になる。

**業務上の目的・背景**：遅延評価は関数型プログラミングの重要な概念であり、メモリ効率と表現力を両立させる。無限リストの処理、大規模データのストリーム処理、複数のフィルタ/変換操作のチェーンを効率的に実行するために不可欠である。

**機能の利用シーン**：無限シーケンスからの先頭N要素取得（`(1..Float::INFINITY).lazy.select(&:even?).first(5)`）、大規模データのパイプライン処理（map.select.take等のチェーン）、条件付きの要素探索に使用される。

**主要な処理内容**：
1. Enumerable#lazy - 遅延Enumeratorの生成
2. Lazy#map / Lazy#collect - 遅延map
3. Lazy#select / Lazy#find_all - 遅延select
4. Lazy#reject - 遅延reject
5. Lazy#grep / Lazy#grep_v - 遅延grep
6. Lazy#take / Lazy#take_while - 遅延take
7. Lazy#drop / Lazy#drop_while - 遅延drop
8. Lazy#flat_map / Lazy#collect_concat - 遅延flat_map
9. Lazy#zip - 遅延zip
10. Lazy#uniq - 遅延uniq
11. Lazy#force - 全要素の強制評価（to_aのエイリアス）

**関連システム・外部連携**：mruby-enumeratorに依存する（Enumeratorクラスの継承）。mruby-enum-extにも依存する。

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

## 関連画面

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

## 機能種別

ライブラリ拡張（遅延評価イテレーション）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| obj (initialize) | Enumerable | Yes | イテレーション対象 | eachメソッドを持つ |
| block (initialize) | Block | No | 要素変換ブロック | - |
| block (map等) | Block | Yes | 各操作の変換/フィルタブロック | 操作に応じて必要 |
| n (take/drop) | Integer | Yes | 取得/スキップ数 | 非負整数 |
| pattern (grep/grep_v) | Object | Yes | マッチングパターン | ===を持つ |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| lazy操作戻り値 | Enumerator::Lazy | チェーン可能な遅延Enumerator |
| force戻り値 | Array | 全要素を評価した配列 |
| first戻り値 | Object/Array | 先頭要素または先頭N要素 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. enum.lazy
   └─ Enumerator::Lazy.new(self) で遅延Enumeratorを生成

2. lazy.map { |x| x * 2 }
   └─ Lazy.new(self) { |yielder, val| yielder << block.call(val) }
       └─ 新しいLazyを返す（まだ何も実行しない）

3. lazy.select { |x| x.even? }
   └─ Lazy.new(self) { |yielder, val| yielder << val if block.call(val) }

4. lazy.take(n)
   └─ Lazy.new(self) { |yielder, val|
           yielder << val
           taken += 1
           raise StopIteration if taken >= n
       }

5. lazy.force / lazy.to_a
   └─ 遅延チェーンを実際に評価し、結果を配列に収集
```

### フローチャート

```mermaid
flowchart TD
    A[enum.lazy] --> B[Lazy.new self]
    B --> C[lazy.map block1]
    C --> D[Lazy.new prev_lazy, block1]
    D --> E[lazy.select block2]
    E --> F[Lazy.new prev_lazy, block2]
    F --> G[lazy.take n]
    G --> H[Lazy.new prev_lazy, take_block]

    H --> I[force / to_a / first]
    I --> J[チェーンを逆順に評価]
    J --> K[各要素をパイプライン処理]
    K --> L[結果配列]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-49-01 | 遅延評価 | 各操作は新しいLazyを返すだけで、実際の評価は行わない | map/select等呼び出し時 |
| BR-49-02 | StopIteration停止 | takeはN個取得後にStopIterationで評価を停止 | take(n)実行時 |
| BR-49-03 | チェーン構築 | 各操作はLazy.new(self)で前段のLazyをラップし、チェーンを構築 | 操作チェーン時 |
| BR-49-04 | forceで評価 | forceはto_aのエイリアスで、全要素を強制評価する | force呼び出し時 |
| BR-49-05 | to_enumのLazy化 | Lazy#to_enumはLazyを返す（通常のEnumeratorではない） | to_enum呼び出し時 |

### 計算ロジック

遅延チェーンの評価: 各Lazyはobj.each内でブロックを呼び、yieldするかどうかを判断する。StopIterationが発生すると即座にイテレーションを停止する。

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | to_enumで未定義メソッドを指定した場合 | 存在するメソッドを指定 |
| - | StopIteration | take等で内部的に使用（正常動作） | 通常はユーザーが意識する必要なし |

### リトライ仕様

リトライは不要

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

該当なし

## パフォーマンス要件

遅延評価により、必要な要素のみを処理するため、大規模データや無限シーケンスでもメモリ効率的。ただし、各要素に対するブロック呼び出しのオーバーヘッドが積み重なる。

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

無限シーケンスに対するforce/to_a呼び出しは無限ループになるため注意。

## 備考

- Enumerator::Lazy < Enumerator（継承関係）
- Lazy#to_enumはLazyを返す（Lazy専用オーバーライド）
- forceはto_aのエイリアス
- take(0)はStopIterationを即座に発生させる空のLazyを返す
- mruby-enum-extにも依存している

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | lazy.rb | `mrbgems/mruby-enum-lazy/mrblib/lazy.rb` | Lazy < Enumerator の継承関係（43行目） |

**読解のコツ**: Lazy < Enumeratorの継承により、Enumeratorの全インスタンス変数(@obj, @meth, @args等)を継承する。LazyのinitializeはEnumerator#initializeのsuperを呼び、Generator経由でイテレーションを定義する。

#### Step 2: Enumerable#lazyとLazy#initializeを理解する

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

**主要処理フロー**:
- **32-34行目**: `Enumerable#lazy` - `Enumerator::Lazy.new(self)`
- **59-72行目**: `Lazy#initialize` - superでEnumerator初期化、obj.each内でブロック呼び出しまたは直接yield

#### Step 3: 遅延操作メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | lazy.rb | `mrbgems/mruby-enum-lazy/mrblib/lazy.rb` | map, select, reject, take等 |

**主要処理フロー**:
- **120-124行目**: `map` - `Lazy.new(self) { |yielder, val| yielder << block.call(val) }`
- **137-143行目**: `select` - ブロックがtrueの場合のみyielderにpush
- **155-161行目**: `reject` - ブロックがfalseの場合のみyielderにpush
- **249-261行目**: `take` - N個yieldしたらStopIteration
- **206-215行目**: `drop` - 最初のN個をスキップ
- **292-300行目**: `flat_map` - block結果をeachで展開

#### Step 4: to_enumとforceを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | lazy.rb | `mrbgems/mruby-enum-lazy/mrblib/lazy.rb` | to_enum, force |

**主要処理フロー**:
- **93-105行目**: `to_enum` - Lazyを返すオーバーライド（通常のEnumeratorではなくLazy）
- **361行目**: `force` - `alias force to_a`

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

```
Enumerable#lazy
    |
    +-- Enumerator::Lazy.new(self)
            |
            +-- super { |yielder| obj.each { |x| block ? block.call(yielder, x) : yielder << x } }
            |
            +-- lazy.map(&block)
            |       +-- Lazy.new(self) { |yielder, val| yielder << block.call(val) }
            |
            +-- lazy.select(&block)
            |       +-- Lazy.new(self) { |yielder, val| yielder << val if block.call(val) }
            |
            +-- lazy.take(n)
            |       +-- Lazy.new(self) { |yielder, val| yielder << val; raise StopIteration if taken >= n }
            |
            +-- lazy.force / lazy.to_a
                    +-- 全チェーンを実際に評価
```

### データフロー図

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

Enumerable               --> enum.lazy                    --> Lazy
Lazy + block              --> lazy.map { ... }            --> Lazy (遅延チェーン追加)
Lazy + block              --> lazy.select { ... }         --> Lazy (遅延チェーン追加)
Lazy + n                  --> lazy.take(n)                --> Lazy (N個制限追加)
Lazy                      --> lazy.force                  --> Array (全評価結果)

[パイプライン例]
(1..Inf).lazy --> .map{|i| i**2} --> .select{|i| i.even?} --> .take(5) --> .force
                                                                           --> [4, 16, 36, 64, 100]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| lazy.rb | `mrbgems/mruby-enum-lazy/mrblib/lazy.rb` | ソース | Lazy全メソッドのRuby実装 |
| mrbgem.rake | `mrbgems/mruby-enum-lazy/mrbgem.rake` | 設定 | gem定義ファイル（mruby-enumerator, mruby-enum-ext依存） |
| lazy.rb | `mrbgems/mruby-enum-lazy/test/lazy.rb` | テスト | Lazyクラスのテスト |
