# 機能設計書 12-データ変換

## 概要

本ドキュメントは、Nushellにおけるデータ変換機能の設計について記述する。each、collect、reduceコマンドを通じて、リストやテーブルの各要素を変換し、新しいデータを生成する機能を提供する。

### 本機能の処理概要

データ変換機能は、パイプラインを流れる構造化データに対して、ユーザー定義のクロージャを適用し、各要素を変換または集約する処理を行う。関数型プログラミングのmap/reduce/collectパターンをシェル環境で実現する。

**業務上の目的・背景**：データ処理において、取得したデータをそのまま使用することは稀であり、何らかの変換・加工が必要となる。eachコマンドは各要素に対する変換（map操作）、reduceコマンドは集約処理（fold操作）、collectコマンドはストリームのマテリアライズを提供し、柔軟なデータ加工パイプラインを構築できる。

**機能の利用シーン**：
- `ls | each {|f| $f.name | str upcase}` でファイル名を大文字変換
- `[1 2 3 4] | reduce {|it, acc| $it + $acc}` でリストの合計を計算
- `seq 1 100 | each {|x| $x * 2}` で各要素を2倍に変換
- `open file.txt | collect | save -f file.txt` でストリームを収集して同一ファイルに書き込み

**主要な処理内容**：
1. 入力データをイテレータとして取得
2. 各要素に対してクロージャを評価し結果を収集（each）
3. アキュムレータを使用して要素を順次集約（reduce）
4. ストリームを値としてマテリアライズ（collect）

**関連システム・外部連携**：nu-engine（クロージャ評価）、nu-protocol（パイプラインデータ）と連携。collectはIRコンパイラで特別に処理される。

**権限による制御**：特になし。純粋なデータ操作機能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインREPL | 主画面 | データ変換コマンドの入力と結果表示 |

## 機能種別

データ操作（変換・集約処理）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| closure | Closure | Yes (each/reduce) | 各要素に適用するクロージャ | 有効なクロージャ |
| --keep-empty, -k | Switch | No (each) | null結果を保持 | - |
| --flatten, -f | Switch | No (each) | 出力をフラット化 | - |
| --fold, -f | Any | No (reduce) | 初期値 | - |
| closure | Closure | No (collect) | 収集後に実行するクロージャ | - |
| --keep-env | Switch | No (collect) | 環境変数の変更を維持 | - |

### 入力データソース

- パイプライン入力: List、Table、Range、ByteStream
- each/reduceは反復可能なデータ型を要求
- collectは任意のPipelineDataを受け付け

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| transformed_list | List | (each) 変換後の要素リスト |
| aggregated_value | Any | (reduce) 集約結果 |
| collected_value | Any | (collect) マテリアライズされた値 |

### 出力先

- パイプライン出力: `PipelineData`として後続コマンドへ渡す
- 画面表示: 変換結果をテーブル/リスト形式で表示

## 処理フロー

### 処理シーケンス

```
1. コマンド引数の解析
   └─ クロージャ、オプションフラグを取得

2. each処理
   └─ 入力データをイテレータに変換
   └─ 各要素に対してclosure.run_with_value()を呼び出し
   └─ keep-emptyがfalseならNothingを除外
   └─ flattenが指定されていればストリームを平坦化

3. reduce処理
   └─ foldオプションまたは最初の要素をアキュムレータ初期値に設定
   └─ 残りの要素を順次クロージャで処理
   └─ 最終的なアキュムレータ値を返却

4. collect処理
   └─ IRコンパイラで特別処理（通常のrunは呼ばれない）
   └─ ストリームを単一の値に収集
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{コマンド種別}
    B -->|each| C[イテレータ取得]
    B -->|reduce| D[初期値設定]
    B -->|collect| E[IR特殊処理]

    C --> F[次の要素取得]
    F --> G{要素あり?}
    G -->|Yes| H[クロージャ評価]
    H --> I{flatten?}
    I -->|Yes| J[ストリーム展開]
    I -->|No| K[結果を追加]
    J --> F
    K --> L{keep-empty?}
    L -->|No| M{結果がNull?}
    M -->|Yes| F
    M -->|No| N[出力に追加]
    L -->|Yes| N
    N --> F
    G -->|No| O[リスト返却]

    D --> P[次の要素取得]
    P --> Q{要素あり?}
    Q -->|Yes| R[acc = closure(it, acc)]
    R --> P
    Q -->|No| S[accを返却]

    E --> T[値をマテリアライズ]
    T --> U[クロージャあれば実行]

    O --> V[終了]
    S --> V
    U --> V
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | Null除外 | eachはデフォルトでNothing値を結果から除外 | --keep-emptyなしの場合 |
| BR-02 | 初期値必須 | reduceは空リストでfold未指定の場合エラー | 入力が空の場合 |
| BR-03 | ストリーム収集 | collectはストリーム全体をメモリに読み込む | 常に適用 |
| BR-04 | 空入力透過 | eachは空入力をそのまま返す | PipelineData::Empty時 |

### 計算ロジック

reduceの処理ロジック:
```
acc = fold または input[0]
for value in input[1..]:
    acc = closure(value, acc)
return acc
```

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

該当なし（インメモリ処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| GenericError | 実行エラー | reduceで空入力かつfold未指定 | --fold オプションで初期値を指定 |
| OnlySupportsThisInputType | 型エラー | 非反復可能な型を入力 | リスト/テーブル/レンジを入力 |

### リトライ仕様

該当なし（同期処理のため）

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

該当なし（状態変更を伴わない）

## パフォーマンス要件

- ストリーミング処理: eachは入力をストリーミング処理し、メモリ効率を維持
- collect注意: 大きなストリームをcollectすると高メモリ使用量になる可能性
- flatten最適化: --flattenで中間リスト作成を回避

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

- クロージャ実行: ユーザー定義クロージャはエンジンのサンドボックス内で実行
- 環境変数: collectの--keep-envフラグで環境変数変更を制御

## 備考

- collectコマンドはIRコンパイラで特別にコンパイルされる（通常のrunは実行されない）
- eachの--flattenオプションは出力をストリームとして即座に返す（収集を待たない）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | each.rs | `crates/nu-command/src/filters/each.rs` | eachのシグネチャと入出力型(46-53行目) |
| 1-2 | reduce.rs | `crates/nu-command/src/filters/reduce.rs` | reduceのシグネチャとfoldオプション(13-31行目) |

**読解のコツ**: `input_output_types`でサポートされる型の組み合わせを確認。クロージャパラメータの形式（引数の数）に注意。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | each.rs | `crates/nu-command/src/filters/each.rs` | run関数(129-235行目)で各入力型の処理分岐を理解 |
| 2-2 | reduce.rs | `crates/nu-command/src/filters/reduce.rs` | run関数(96-131行目)で集約処理を理解 |
| 2-3 | collect.rs | `crates/nu-cmd-lang/src/core_commands/collect.rs` | run関数(43-56行目)はIRモードで使用されない点を確認 |

**主要処理フロー (each)**:
1. **137行目**: クロージャを取得
2. **142-227行目**: 入力型に応じた処理分岐
3. **160-168行目**: `input.into_iter().map()`でクロージャを各要素に適用
4. **229-233行目**: keep-emptyフラグに応じてNothingをフィルタ

**主要処理フロー (reduce)**:
1. **104行目**: foldオプションを取得
2. **109-110行目**: 初期値設定（foldまたは最初の要素）
3. **121-128行目**: `for value in iter`でクロージャを順次適用
4. **130行目**: 最終アキュムレータ値を返却

#### Step 3: クロージャ評価を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | each.rs | `crates/nu-command/src/filters/each.rs` | ClosureEvalの使用(149行目, 173行目) |
| 3-2 | each.rs | `crates/nu-command/src/filters/each.rs` | each_map関数(239-246行目)でエラーチェーン処理 |

**主要処理フロー**:
- **149行目**: `ClosureEval::new()`でクロージャ評価器を作成
- **243-244行目**: `closure.run_with_value()`でクロージャを実行し`into_value()`で結果を取得

#### Step 4: flatten処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | each.rs | `crates/nu-command/src/filters/each.rs` | flattenフラグの処理(151-168行目) |

**主要処理フロー**:
- **151-159行目**: `flat_map()`でストリーム出力を即座に展開
- **160-168行目**: 通常モードでは`map()`を使用

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

```
Each::run() / Reduce::run() / Collect::run()
    │
    ├─ Each:
    │      ├─ ClosureEval::new(engine_state, stack, closure)
    │      │
    │      └─ input.into_iter()
    │             │
    │             ├─ (flatten) flat_map(closure.run_with_value())
    │             │
    │             └─ (normal) map(each_map())
    │                    └─ closure.run_with_value()
    │                           └─ pipeline_data.into_value()
    │
    ├─ Reduce:
    │      ├─ ClosureEval::new(engine_state, stack, closure)
    │      │
    │      └─ for value in iter:
    │             └─ closure.add_arg(value).add_arg(acc)
    │                    └─ run_with_input(acc)
    │                           └─ into_value()
    │
    └─ Collect:
           └─ (IR compiled - run() not called)
```

### データフロー図

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

                       ┌─────────────────────────────────┐
PipelineData ─────────▶│           each処理               │
     │                 │  for element in input:          │
     │                 │    result = closure(element)    │
     │                 │    if !keep_empty && is_null:   │
     │                 │      skip                       │
     │                 │    else: output.push(result)    │
     │                 └─────────────────────────────────┘
     │                                  │
     │                                  ▼
     │                          List<Value>
     │
     │                 ┌─────────────────────────────────┐
     ├────────────────▶│          reduce処理              │
     │                 │  acc = fold or first           │
     │                 │  for element in rest:          │
     │                 │    acc = closure(element, acc) │
     │                 │  return acc                    │
     │                 └─────────────────────────────────┘
     │                                  │
     │                                  ▼
     │                              Value
     │
     │                 ┌─────────────────────────────────┐
     └────────────────▶│         collect処理              │
                       │  value = stream.collect()      │
                       │  if closure:                   │
                       │    return closure(value)       │
                       │  return value                  │
                       └─────────────────────────────────┘
                                        │
                                        ▼
                                    Value
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| each.rs | `crates/nu-command/src/filters/each.rs` | ソース | eachコマンド実装 |
| reduce.rs | `crates/nu-command/src/filters/reduce.rs` | ソース | reduceコマンド実装 |
| collect.rs | `crates/nu-cmd-lang/src/core_commands/collect.rs` | ソース | collectコマンド定義 |
| mod.rs | `crates/nu-command/src/filters/mod.rs` | ソース | フィルターモジュールエクスポート |
| utils.rs | `crates/nu-command/src/filters/utils.rs` | ソース | エラーチェーン処理ヘルパー |
| closure.rs | `crates/nu-engine/src/closure.rs` | ソース | ClosureEvalクロージャ評価器 |
