Clojureの`:keys`の戻り値について

## 基本的な概念

`:keys`はClojureの**分割束縛 - destructuring**で使用される特別なキーワードです。関数の戻り値そのものではなく、マップから特定のキーの値を抽出するための構文です。

## `:keys`の動作原理

```clojure
;; 元のマップ
(def person {:name "田中" :age 30 :city "東京"})

;; :keysを使った分割束縛
(let [{:keys [name age]} person]
  (println name age))
;; => 田中 30
```

## 視覚的な説明図

```
マップの構造:
┌─────────────────────────────────┐
│ {:name "田中"                   │
│  :age 30                       │
│  :city "東京"}                 │
└─────────────────────────────────┘
                │
                ▼
        {:keys [name age]}
                │
                ▼
┌─────────────────────────────────┐
│ name → "田中"                   │
│ age  → 30                      │
└─────────────────────────────────┘
```

## 使用例とパターン

### 1. 関数の引数での使用

```clojure
(defn greet [{:keys [name age]}]
  (str "こんにちは、" name "さん（" age "歳）"))

(greet {:name "佐藤" :age 25})
;; => "こんにちは、佐藤さん（25歳）"
```

### 2. let束縛での使用

```clojure
(let [data {:x 10 :y 20 :z 30}
      {:keys [x y]} data]
  (+ x y))
;; => 30
```

### 3. 関数の戻り値を分割束縛

```clojure
(defn get-coordinates []
  {:x 100 :y 200})

(let [{:keys [x y]} (get-coordinates)]
  (println "座標:" x y))
;; => 座標: 100 200
```

## 重要なポイント

1. **`:keys`自体は値を返さない** - 分割束縛の構文
2. **元のマップから値を抽出** - キー名と同じ変数名を作成
3. **存在しないキーは`nil`になる**

```clojure
(let [{:keys [name email]} {:name "山田"}]
  (println name email))
;; => 山田 nil
```

## 他の分割束縛オプション

- `:keys` - キーワードキー用
- `:strs` - 文字列キー用  
- `:syms` - シンボルキー用

```clojure
;; 文字列キーの場合
(let [{:strs [name age]} {"name" "鈴木" "age" 35}]
  (println name age))
;; => 鈴木 35
```

## `:keys`と`:as`の組み合わせ

`:keys`で値を抽出しつつ、元のマップ全体も保持したい場合は`:as`を使います。

### 基本的な使用法

```clojure
(defn process-user [{:keys [name age] :as params}]
  (println "名前:" name)
  (println "年齢:" age)
  (println "全体のパラメータ:" params))

(process-user {:name "田中" :age 30 :city "東京"})
;; => 名前: 田中
;; => 年齢: 30
;; => 全体のパラメータ: {:name "田中", :age 30, :city "東京"}
```

### 視覚的な説明図

```
元の引数: {:name "田中" :age 30 :city "東京"}
                    │
                    ├─────────────────┐
                    │                 │
                    ▼                 ▼
            :keys で抽出        :as で元のまま保持
                    │                 │
                    ▼                 ▼
┌─────────────────────────────────────────────┐
│ 抽出された個別の値:                         │
│ name → "田中"                               │
│ age  → 30                                  │
│                                             │
│ 元の引数そのまま:                           │
│ params → {:name "田中" :age 30 :city "東京"} │
│          ↑                                 │
│          完全に同じオブジェクト             │
└─────────────────────────────────────────────┘
```

### 重要なポイント

**`:as`は分割束縛の結果ではなく、元の引数をそのまま参照します**

```clojure
(defn demo [{:keys [name] :as original}]
  (println "抽出した値:" name)
  (println "元の引数:" original)
  (println "同じオブジェクト？" (identical? original {:name "田中" :age 30 :city "東京"})))

;; 引数として渡されたマップと:asで束縛された値は同一
(let [input-map {:name "田中" :age 30 :city "東京"}]
  (demo input-map))
```

### 実用的な例

```clojure
(defn validate-and-process [{:keys [name email] :as user-data}]
  (cond
    (empty? name) {:error "名前が必要です" :data user-data}
    (empty? email) {:error "メールが必要です" :data user-data}
    :else {:success true :user user-data :greeting (str "こんにちは " name "さん")}))

(validate-and-process {:name "佐藤" :email "sato@example.com" :phone "090-1234-5678"})
;; => {:success true, 
;;     :user {:name "佐藤", :email "sato@example.com", :phone "090-1234-5678"}, 
;;     :greeting "こんにちは 佐藤さん"}
```

### let束縛での使用

```clojure
(let [config {:host "localhost" :port 3000 :debug true}
      {:keys [host port] :as full-config} config]
  (println "接続先:" host ":" port)
  (when (:debug full-config)
    (println "デバッグモード有効 - 設定:" full-config)))
```

### ネストした分割束縛での使用

```clojure
(defn process-request [{:keys [user params] :as request}]
  (let [{:keys [name id] :as user-info} user
        {:keys [action data] :as request-params} params]
    {:processed-by name
     :user-id id
     :action action
     :original-request request}))
```

## 分割束縛とLazy Sequenceの関係

**重要：分割束縛は必要な部分だけlazy sequenceを実現（realize）します**

### Lazy Sequenceでの分割束縛

```clojure
;; 無限のlazy sequence
(defn infinite-numbers []
  (iterate inc 0))

;; 分割束縛で最初の3つだけを取得
(let [[a b c] (infinite-numbers)]
  (println a b c))
;; => 0 1 2
;; 無限シーケンスだが、必要な3つだけが計算される
```

### 視覚的な説明図

```
Lazy Sequence: [0 1 2 3 4 5 6 ...∞]
                │ │ │
                ▼ ▼ ▼  (必要な分だけ実現)
        [a b c] の分割束縛
                │
                ▼
┌─────────────────────────────┐
│ a → 0  (実現済み)           │
│ b → 1  (実現済み)           │  
│ c → 2  (実現済み)           │
│                             │
│ 残りは未実現のまま          │
└─────────────────────────────┘
```

### 実例での動作確認

```clojure
;; 副作用のあるlazy sequence（計算のタイミングを確認）
(defn debug-seq []
  (map (fn [x] 
         (println "計算中:" x)
         x) 
       (range 10)))

;; 分割束縛で最初の2つだけ使用
(let [[first second] (debug-seq)]
  (println "結果:" first second))

;; 出力:
;; 計算中: 0
;; 計算中: 1
;; 結果: 0 1
;; → 必要な2つだけが計算された！
```

### マップの分割束縛は即座に実現

```clojure
;; マップの場合は即座にキーにアクセス
(defn create-map-with-lazy-vals []
  {:a (do (println "aを計算") 1)
   :b (do (println "bを計算") 2)
   :c (delay (do (println "cを計算（遅延）") 3))})

(let [{:keys [a b]} (create-map-with-lazy-vals)]
  (println "使用:" a b))

;; 出力:
;; aを計算
;; bを計算
;; 使用: 1 2
;; → :cは使わないが、マップ作成時に:aと:bは計算される
```

### ネストしたシーケンスでの分割束縛

```clojure
;; ネストしたlazy sequenceの場合
(defn nested-lazy []
  (map (fn [x] 
         (println "外側計算:" x)
         (map (fn [y] 
                (println "内側計算:" y)
                (* x y))
              (range 5)))
       (range 3)))

;; 最初の要素の最初の2つだけを取得
(let [[[a b] & rest] (nested-lazy)]
  (println "結果:" a b))

;; 出力:
;; 外側計算: 0
;; 内側計算: 0
;; 内側計算: 1
;; 結果: 0 0
```

### パフォーマンスの考慮事項

```clojure
;; 大きなlazy sequenceでの効率的な分割束縛
(defn process-large-data []
  (->> (range 1000000)
       (map #(* % %))
       (filter even?)))

;; 最初の10個だけを処理
(let [[a b c & more] (take 10 (process-large-data))]
  (println "最初の3つ:" a b c)
  (println "残り:" (count more)))
;; → 100万個全てではなく、必要な分だけが計算される
```

## まとめ

**分割束縛は確かにlazy sequenceを必要な分だけ実現します：**

1. **シーケンスの分割束縛** → 必要な要素数だけ計算
2. **マップの分割束縛** → キーへの即座アクセス（既に実現済みの値）
3. **ネストした構造** → 必要な深さまで段階的に計算
4. **パフォーマンス** → メモリ効率が良い（全体を実現しない）

これがClojureの遅延評価の強力な特徴の一つです。