Clojure- map関数と基本構文

## 1. 基本的な構文構造

### リスト記法（前置記法）
```
(関数名 引数1 引数2 引数3 ...)
```

**視覚的表現:**
```
    ┌─────────────────────────────┐
    │  (+ 1 2 3)                  │
    │   │ │ │ │                   │
    │   │ └─┴─┴── 引数     　　　　 │
    │   └─────── 関数           　 │
    └─────────────────────────────┘
```

## 2. map関数の構文

### 基本形
```clojure
(map 関数 コレクション)
```

### 例
```clojure
(map inc [1 2 3 4])
;; => (2 3 4 5)
```

**視覚的な動作:**
```
入力: [1 2 3 4]
      │ │ │ │
      ▼ ▼ ▼ ▼
     inc inc inc inc
      │ │ │ │
      ▼ ▼ ▼ ▼
出力: (2 3 4 5)
```

## 3. map関数の詳細パターン

### パターン1: 単一関数、単一コレクション
```clojure
(map #(* % 2) [1 2 3 4])
;; => (2 4 6 8)
```

```
[1 2 3 4] ──┐
            │    map
        #(* % 2)──┘
            │
            ▼
        (2 4 6 8)
```

### パターン2: 複数コレクション
```clojure
(map + [1 2 3] [4 5 6])
;; => (5 7 9)
```

```
[1 2 3] ──┐
          │ map +
[4 5 6] ──┘
          │
          ▼
       (5 7 9)

詳細:
1+4=5, 2+5=7, 3+6=9
```

### パターン3: 無名関数の使用
```clojure
(map (fn [x] (* x x)) [1 2 3 4])
;; => (1 4 9 16)
```

## 4. 関連する高階関数

### filter - 条件に合う要素を抽出
```clojure
(filter even? [1 2 3 4 5 6])
;; => (2 4 6)
```

```
[1 2 3 4 5 6]
 │ │ │ │ │ │
 ▼ ▼ ▼ ▼ ▼ ▼
even? チェック
 × ○ × ○ × ○
   │   │   │
   ▼   ▼   ▼
  (2   4   6)
```

### reduce - コレクションを単一の値に集約
```clojure
(reduce + [1 2 3 4])
;; => 10
```

## 5. 実用的な例

### データ変換の例
```clojure
;; 人のデータを変換
(def people [{:name "田中" :age 25}
             {:name "佐藤" :age 30}
             {:name "鈴木" :age 35}])

;; 名前だけを抽出
(map :name people)
;; => ("田中" "佐藤" "鈴木")

;; 年齢を5歳増やす
(map #(update % :age + 5) people)
;; => ({:name "田中", :age 30} 
;;     {:name "佐藤", :age 35} 
;;     {:name "鈴木", :age 40})
```

### チェーン処理の例
```clojure
(->> [1 2 3 4 5 6 7 8 9 10]
     (filter odd?)        ;; 奇数のみ
     (map #(* % %))       ;; 二乗
     (reduce +))          ;; 合計
;; => 165
```

**視覚的な流れ:**
```
[1 2 3 4 5 6 7 8 9 10]
         │
    filter odd?
         │
    [1 3 5 7 9]
         │
   map #(* % %)
         │
   [1 9 25 49 81]
         │
     reduce +
         │
        165
```

## 6. 無名関数の記法

### 3つの記法
```clojure
;; 1. fn記法
(map (fn [x] (* x 2)) [1 2 3])

;; 2. ショートハンド記法
(map #(* % 2) [1 2 3])

;; 3. 名前付き関数
(defn double [x] (* x 2))
(map double [1 2 3])
```

### ショートハンド記法の詳細
```clojure
#(...)     ;; 無名関数の開始
%          ;; 第1引数
%1         ;; 第1引数（明示的）
%2         ;; 第2引数
%&         ;; 残りの引数（可変長）
```

## 7. 実践的な使用パターン

### パターン1: データクリーニング
```clojure
(->> ["  hello  " "  world  " "  clojure  "]
     (map clojure.string/trim)
     (map clojure.string/upper-case))
;; => ("HELLO" "WORLD" "CLOJURE")
```

### パターン2: ネストしたデータの処理
```clojure
(def orders [{:items [{:name "商品A" :price 100}
                      {:name "商品B" :price 200}]}
             {:items [{:name "商品C" :price 150}]}])

;; 各注文の合計金額を計算
(map (fn [order]
       (->> (:items order)
            (map :price)
            (reduce +)))
     orders)
;; => (300 150)
```

## 8. パフォーマンスと遅延評価

### 遅延シーケンス
```clojure
;; mapは遅延評価される
(def lazy-seq (map println [1 2 3 4 5]))
;; この時点では何も出力されない

;; 実際に値が必要になったときに評価
(doall lazy-seq)
;; この時点で1 2 3 4 5が出力される
```

### 無限シーケンス
```clojure
;; 無限の数列
(def fibonacci-seq
  (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))

;; 最初の10個を取得
(take 10 fibonacci-seq)
;; => (0 1 1 2 3 5 8 13 21 34)
```

## 9. mapの返り値の型について

### 重要なポイント：mapは常に遅延シーケンスを返す

```clojure
;; 入力がベクターでも
(map inc [1 2 3])
;; => (2 3 4)  ← これは遅延シーケンス（lazy sequence）

;; 入力がリストでも
(map inc '(1 2 3))
;; => (2 3 4)  ← これも遅延シーケンス

;; 型を確認
(type (map inc [1 2 3]))
;; => clojure.lang.LazySeq
```

### 視覚的な比較

```
入力の型     │  map処理   │  出力の型
─────────────────────────────────────
[1 2 3] 　　│    map     │  LazySeq
ベクター     │    inc     │  (2 3 4)
            │            │
'(1 2 3)    │    map     │  LazySeq  
リスト       │    inc     │  (2 3 4)
            │            │
#{1 2 3}    │    map     │  LazySeq
セット       │    inc     │  (2 3 4)
```

## 10. Clojureのコレクション型の違い

### 基本的な4つのコレクション型

#### 1. ベクター（Vector）`[]`
```clojure
[1 2 3 4]
```
**特徴:**
- **インデックスアクセス**が高速 `O(1)`
- **末尾への追加**が高速
- **順序あり**
- **重複あり**

**視覚的構造:**
```
インデックス: 0  1  2  3
ベクター:    [1][2][3][4]
             ↑  ↑  ↑  ↑
          高速アクセス可能
```

#### 2. リスト（List）`'()`
```clojure
'(1 2 3 4)
```
**特徴:**
- **先頭への追加**が高速 `O(1)`
- **先頭アクセス**が高速、末尾は遅い `O(n)`
- **順序あり**
- **重複あり**

**視覚的構造:**
```
'(1 2 3 4)
  ↓ ↓ ↓ ↓
  1→2→3→4→nil
  ↑
先頭アクセス高速
```

#### 3. セット（Set）`#{}`
```clojure
#{1 2 3 4}
```
**特徴:**
- **重複なし**
- **順序なし**（ハッシュセット）
- **メンバーシップテスト**が高速 `O(1)`

**視覚的構造:**
```
#{1 2 3 4}
┌─────────────┐
│  3   1      │  ← 順序は保証されない
│     4   2   │
└─────────────┘
```

#### 4. マップ（Map）`{}`
```clojure
{:a 1 :b 2 :c 3}
```
**特徴:**
- **キー・値のペア**
- **キーによる高速検索** `O(1)`
- **順序なし**（ハッシュマップ）

**視覚的構造:**
```
{:a 1, :b 2, :c 3}
 ↓     ↓     ↓
:a ──→ 1
:b ──→ 2  
:c ──→ 3
```

### 5. 遅延シーケンス（Lazy Sequence）
```clojure
(map inc [1 2 3])  ; これが遅延シーケンス
```
**特徴:**
- **必要時に計算**される
- **無限シーケンス**を扱える
- **メモリ効率**が良い

## 11. 型変換の方法

### 遅延シーケンスから他の型への変換

```clojure
(def lazy-result (map inc [1 2 3]))

;; ベクターに変換
(vec lazy-result)
;; => [2 3 4]

;; リストに変換（実際は既にシーケンス）
(seq lazy-result)
;; => (2 3 4)

;; セットに変換
(set lazy-result)
;; => #{2 3 4}

;; 強制評価（型は変わらない）
(doall lazy-result)
;; => (2 3 4)
```

### 視覚的変換フロー
```
map処理
   ↓
LazySeq (2 3 4)
   ├─ vec ──→ [2 3 4]     ベクター
   ├─ set ──→ #{2 3 4}    セット  
   └─ doall → (2 3 4)     評価済みシーケンス
```

## 12. パフォーマンスの比較

### アクセス速度比較表
```
操作            │ ベクター │ リスト │ セット │
─────────────────────────────────────────
先頭アクセス     │   O(1)  │ O(1)  │  N/A  │
末尾アクセス     │   O(1)  │ O(n)  │  N/A  │
インデックス     │   O(1)  │ O(n)  │  N/A  │
先頭追加        │   O(1)  │ O(1)  │  N/A  │
末尾追加        │   O(1)  │ O(n)  │  N/A  │
メンバー検索     │   O(n)  │ O(n)  │ O(1)  │
```

## 13. 実用的な使い分け

### 使用場面別推奨

#### ベクター `[]` を使う場面
```clojure
;; インデックスアクセスが必要
(get [10 20 30 40] 2)  ; => 30

;; 末尾への追加が多い
(conj [1 2 3] 4)       ; => [1 2 3 4]
```

#### リスト `'()` を使う場面
```clojure
;; 先頭への追加が多い
(conj '(2 3 4) 1)      ; => (1 2 3 4)

;; 関数の引数リストやマクロ
'(defn my-func [x] (+ x 1))
```

#### セット `#{}` を使う場面
```clojure
;; 重複排除
(set [1 2 2 3 3 4])    ; => #{1 2 3 4}

;; メンバーシップテスト
(#{:admin :user :guest} :admin)  ; => :admin (truthy)
```

## 14. LazySeq（遅延シーケンス）とリストの違い

### 重要：LazySeqとリストは別物

```clojure
;; リスト（List）
(def my-list '(1 2 3 4))
(type my-list)
;; => clojure.lang.PersistentList

;; 遅延シーケンス（LazySeq）
(def my-lazy (map inc [1 2 3]))
(type my-lazy)
;; => clojure.lang.LazySeq

;; 見た目は同じでも...
my-list     ; => (1 2 3 4)
my-lazy     ; => (2 3 4)
```

### 視覚的な違い

```
リスト '(1 2 3 4)          遅延シーケンス (map inc [1 2 3])
┌─────────────────┐        ┌─────────────────────────────┐
│ [1]→[2]→[3]→[4] │        │ 未計算 → 未計算 → 未計算    │
│  ↑              │        │   ↓       ↓       ↓       │
│ 即座に全て       │        │  計算    計算    計算      │
│ アクセス可能     │        │ 必要時   必要時   必要時    │
└─────────────────┘        └─────────────────────────────┘
```

### 内部構造の違い

#### リスト（PersistentList）
```clojure
'(1 2 3 4)
```
**特徴:**
- **すべての要素が既に存在**
- **メモリに全て格納済み**
- **即座にアクセス可能**
- **有限**

**内部構造:**
```
先頭 → 1 → 2 → 3 → 4 → nil
       ↑   ↑   ↑   ↑
    すべて実体として存在
```

#### 遅延シーケンス（LazySeq）
```clojure
(map inc [1 2 3])
```
**特徴:**
- **必要になるまで計算されない**
- **一度計算されるとキャッシュ**
- **無限シーケンスも可能**
- **メモリ効率が良い**

**内部構造:**
```
LazySeq
├─ 関数: (fn [] (map inc [1 2 3]))
├─ 状態: 未実現
└─ キャッシュ: nil

↓ 最初のアクセス時

LazySeq
├─ 関数: (fn [] (map inc [1 2 3]))  
├─ 状態: 実現済み
└─ キャッシュ: (2 3 4)
```

### 実際の動作比較

```clojure
;; リストの場合
(def my-list '(1 2 3 4))
(println "リスト作成完了")  ; すぐに出力される

;; 遅延シーケンスの場合
(def my-lazy (map #(do (println "計算中:" %) 
                      (* % 2)) 
                 [1 2 3 4]))
(println "遅延シーケンス作成完了")  ; すぐに出力される（まだ計算されていない）

;; 実際にアクセスするまで計算されない
(first my-lazy)  ; この時点で「計算中: 1」が出力される
;; => 2

(doall my-lazy)  ; この時点で残りの「計算中: 2, 3, 4」が出力される
;; => (2 4 6 8)
```

### パフォーマンスの違い

#### メモリ使用量
```clojure
;; 大きなリスト（すべてメモリに保持）
(def big-list (range 1000000))  ; 100万個の数値をメモリに保持

;; 遅延シーケンス（必要な分だけ）
(def big-lazy (map inc (range 1000000)))  ; 計算はアクセス時のみ

;; 最初の10個だけ使用
(take 10 big-lazy)  ; メモリ使用量は最小限
```

#### 計算タイミング
```clojure
;; リスト：作成時にすべて計算
(time (def eager-list (doall (map #(Thread/sleep 1) (range 5)))))
;; => 5秒かかる

;; 遅延シーケンス：アクセス時に計算
(time (def lazy-seq (map #(Thread/sleep 1) (range 5))))
;; => 即座に完了（計算はまだされていない）

(time (doall lazy-seq))  ; この時点で5秒かかる
```

### 無限シーケンスの例

```clojure
;; リストでは不可能（メモリ不足でクラッシュ）
;; (def infinite-list (range))  ; これは危険

;; 遅延シーケンスなら可能
(def infinite-lazy (iterate inc 0))  ; 0, 1, 2, 3, ...

;; 必要な分だけ取得
(take 5 infinite-lazy)  ; => (0 1 2 3 4)
```

### 実用的な違いの例

#### ファイル処理での違い
```clojure
;; リスト方式（危険：大ファイルでメモリ不足）
(defn process-file-eager [filename]
  (doall (map process-line (line-seq (reader filename)))))

;; 遅延シーケンス方式（安全：一行ずつ処理）
(defn process-file-lazy [filename]
  (map process-line (line-seq (reader filename))))

;; 使用例
(take 10 (process-file-lazy "huge-file.txt"))  ; 最初の10行のみ処理
```

## 15. いつリストを使い、いつ遅延シーケンスを使うか

### リスト '() を使う場面
```clojure
;; 1. 小さなデータセット
(def colors '(:red :green :blue))

;; 2. 関数の引数（コード as データ）
'(defn my-func [x] (+ x 1))

;; 3. 先頭操作が多い場合
(conj '(2 3 4) 1)  ; => (1 2 3 4)
```

### 遅延シーケンス（map等の結果）を使う場面
```clojure
;; 1. 大きなデータセット
(map process-data huge-dataset)

;; 2. 無限シーケンス
(take 100 (iterate inc 0))

;; 3. パイプライン処理
(->> data
     (map transform1)
     (filter predicate)
     (map transform2)
     (take 10))

;; 4. メモリ効率が重要
(map expensive-computation large-collection)
```

### 変換のタイミング

```clojure
;; 遅延シーケンス → リスト（通常は不要、seqのまま使える）
(seq lazy-sequence)

;; 遅延シーケンス → ベクター（ランダムアクセスが必要）
(vec lazy-sequence)

;; 遅延シーケンス → 強制評価（副作用がある場合）
(doall lazy-sequence)
```

## 16. map使用時の実践的パターン

### パターン1: 結果をベクターで欲しい場合
```clojure
;; 一般的なパターン
(mapv inc [1 2 3 4])    ; => [2 3 4 5] (ベクター)

;; または
(vec (map inc [1 2 3 4]))  ; => [2 3 4 5]
```

### パターン2: セットで重複排除したい場合
```clojure
(set (map #(mod % 3) [1 2 3 4 5 6]))
;; => #{0 1 2}
```

### パターン3: 遅延評価を活用する場合
```clojure
;; 無限シーケンス
(def fibonacci (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))

;; 必要な分だけ取得
(take 10 fibonacci)
;; => (0 1 1 2 3 5 8 13 21 34)
```

## まとめ

### mapの返り値について
- **mapは常に遅延シーケンス**を返す
- **入力の型に関係なく**、出力は`LazySeq`
- **必要に応じて他の型に変換**する

### コレクション型の選択指針
1. **インデックスアクセス**が必要 → **ベクター**
2. **先頭操作**が多い → **リスト**
3. **重複排除・メンバー検索** → **セット**
4. **キー・値の関連付け** → **マップ**
5. **遅延評価・無限シーケンス** → **遅延シーケンス**

練習のポイント：
1. まずは単純な例から始める
2. 無名関数の記法に慣れる
3. 他の高階関数と組み合わせる
4. 実際のデータで試してみる
5. **返り値の型を意識する**
6. **適切なコレクション型を選択する**