# 機能設計書 57-diff_by

## 概要

本ドキュメントは、Hack Standard Library (HSL) の `Vec\diff_by` 関数の機能設計書である。この関数はスカラー関数を使用して要素の同一性を判定し、最初のTraversableにのみ存在する要素を返す。非arraykey型の要素を含むコレクションの差分計算に対応する。

### 本機能の処理概要

**業務上の目的・背景**：
`Vec\diff` 関数はarraykey型（intまたはstring）の要素のみを扱える制約がある。しかし、実際のアプリケーションでは、オブジェクトや配列などの複合型を含むコレクションの差分計算が必要になることがある。例えば、ユーザーオブジェクトのリストからIDを基準に除外したり、商品オブジェクトをSKUで比較したりする場合が該当する。`Vec\diff_by` 関数は、スカラー関数を使用して各要素をarraykey値に変換し、その値で比較することで、非arraykey型の要素でも差分計算を可能にする。

**機能の利用シーン**：
- オブジェクトのリストをID属性で差分計算
- 複合データ構造の特定フィールドで比較
- タプルや配列の特定要素で差分を取得
- エンティティのリストを主キーで比較
- カスタム比較ロジックによる集合演算

**主要な処理内容**：
1. 最初のTraversable（差分の基準）を受け取る
2. 2番目のTraversable（除外対象）を受け取る
3. スカラー関数（要素をarraykeyに変換する関数）を受け取る
4. 除外対象の各要素をスカラー関数で変換し、keysetを構築
5. 基準の各要素について、スカラー関数の結果がkeysetに含まれない要素のみを抽出
6. 結果をvecとして返却

**関連システム・外部連携**：
内部実装では `Keyset\map` と `Vec\filter` を使用している。arraykey型の要素を扱う場合は `Vec\diff` を使用する方が効率的。

**権限による制御**：
本機能は純粋関数であり、権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| N/A | 画面なし | - | 本プロジェクトはUIを持たないライブラリのため該当なし |

## 機能種別

集合演算処理 / データフィルタリング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| $first | Traversable<Tv> | Yes | 差分の基準となるコレクション | 任意の型の要素を持つTraversable |
| $second | Traversable<Tv> | Yes | 除外対象のコレクション | $firstと同じ型の要素を持つTraversable |
| $scalar_func | (function(Tv)[_]: Ts as arraykey) | Yes | 要素をarraykeyに変換するスカラー関数 | arraykey型の値を返す関数 |

### 入力データソース

関数呼び出し時の引数として直接渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | vec<Tv> | 最初のTraversableから除外対象の要素を除いたvec |

### 出力先

関数の戻り値として呼び出し元に返却される。

## 処理フロー

### 処理シーケンス

```
1. 入力検証
   └─ $first が空の場合、空のvecを返却
2. ショートカット判定
   └─ $second が空の場合、$first をそのままvecに変換して返却
3. スカラー変換と除外集合構築
   └─ Keyset\map で $second の各要素をスカラー関数で変換し、keysetを構築
4. 差分計算
   └─ $first の各要素について、スカラー関数の結果がkeysetに含まれない要素のみを抽出
5. 結果返却
   └─ 抽出された要素をvecとして返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[入力: $first, $second, $scalar_func]
    B --> C{$first は空?}
    C -->|Yes| D[空のvecを返却]
    C -->|No| E{$second は空?}
    E -->|Yes| F[$first をvecに変換して返却]
    E -->|No| G[Keyset\map で $second を変換]
    G --> H[filter: スカラー結果がsetに含まれない要素を抽出]
    H --> I[結果vecを返却]
    D --> J[終了]
    F --> J
    I --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-57-01 | スカラー関数制約 | スカラー関数はarraykey型の値を返す必要がある | 常時 |
| BR-57-02 | 順序保持 | $first の要素の順序は結果でも保持される | 常時 |
| BR-57-03 | 同一型制約 | $first と $second は同じ要素型でなければならない | 常時 |
| BR-57-04 | 関数適用回数 | スカラー関数は各要素に対して1回ずつ適用される | 常時 |

### 計算ロジック

- 計算量: O((n + m) * s) - nは$firstのサイズ、mは$secondのサイズ、sはスカラー関数の計算量
- 空間計算量: O(n + m) - nは$firstのサイズ、mは$secondのサイズ

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

### 操作別データベース影響一覧

本機能はデータベース操作を行わない純粋関数である。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 本関数は例外をスローしない（スカラー関数が例外をスローする可能性あり） | - |

### リトライ仕様

リトライは不要（同期的な純粋関数のため）

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

該当なし（データベース操作を行わないため）

## パフォーマンス要件

- 計算量: O((n + m) * s) - スカラー関数の計算量に依存
- 純粋関数として定義（ctx $scalar_funcにより関数の副作用を継承）
- keysetを使用した高速な存在確認

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

- スカラー関数は信頼できるコードから提供されるべき
- 入力データの検証は呼び出し側の責任

## 備考

- arraykey型の要素を扱う場合は `Vec\diff()` を使用する方が効率的
- 結果は常に新しいvecとして返される（入力は変更されない）
- `ctx $scalar_func` により、スカラー関数の副作用コンテキストを継承

---

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

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

### 推奨読解順序

#### Step 1: 型パラメータを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | select.php | `src/vec/select.php` | 関数シグネチャの型パラメータ（56-60行目） |

**読解のコツ**:
- `Tv` は任意の型パラメータ（arraykey制約なし）
- `Ts as arraykey` はスカラー関数の戻り値型制約
- `(function(Tv)[_]: Ts)` は Tv を Ts に変換するクロージャ型

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | select.php | `src/vec/select.php` | diff_by 関数の実装（56-72行目） |

**主要処理フロー**:
1. **61-63行目**: $first が空の場合の早期リターン
2. **65-67行目**: $second が空の場合のショートカット
3. **69行目**: `Keyset\map` で除外対象をスカラー変換
4. **70-71行目**: `filter` で差分を計算（スカラー関数を再適用して比較）

#### Step 3: テストケースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | VecSelectTest.php | `tests/vec/VecSelectTest.php` | diff_by テスト（51-85行目） |

**主要ポイント**:
- **66-72行目**: 文字列の先頭2文字で比較する例
- `Str\slice($str, 0, 2)` でスカラー関数を定義

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

```
Vec\diff_by($first, $second, $scalar_func)
    │
    ├─ [empty check] $first が空 → vec[] を返却
    │
    ├─ [shortcut] $second が空 → cast_clear_legacy_array_mark($first)
    │
    └─ [main logic]
           │
           ├─ Keyset\map($second, $scalar_func)  // 除外集合の構築
           │
           └─ Vec\filter($first, ...)  // 差分計算
                  │
                  └─ C\contains_key($set, $scalar_func($value))  // 存在確認
```

### データフロー図

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

$first ─────────────────────▶
 (Traversable<Tv>)             Vec\diff_by ──────────▶ vec<Tv>
                                    │                  (差分結果)
$second ────────────────────▶       │
 (Traversable<Tv>)                  │
                                    ▼
$scalar_func ───────────────▶  Keyset\map($second, $scalar_func)
 (function(Tv): Ts)                 │
                                    ▼
                               Vec\filter + $scalar_func
                                    │
                                    ▼
                               結果vec
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| select.php | `src/vec/select.php` | ソース | diff_by関数のパブリックインターフェース |
| select.php | `src/keyset/transform.php` | ソース | Keyset\map関数 |
| VecSelectTest.php | `tests/vec/VecSelectTest.php` | テスト | diff_by関数のユニットテスト |
