Rubyの多重代入あれこれまとめ

[Ruby の多重代入](https://docs.ruby-lang.org/ja/latest/doc/spec=2foperator.html#multiassign)はいろいろな書き方があって便利ですが、忘れがちなのでまとめておきます

## 変数への代入

### 何の変哲も無い代入

```rb
a = 1 #=> 1
a #=> 1
```

### 右辺が `,` 区切りで複数ある場合には配列に変換される

```rb
a = 1, 2 #=> [1, 2]
a #=> [1, 2]
```

### 左辺も `,` 区切りで複数ある場合には配列に変換された上で、左辺の各変数に右辺の各要素が代入される

```rb
a, b = 1, 2 #=> [1, 2]
a #=> 1
b #=> 2
```

### 右辺の要素の数が左辺の変数の数に満たない場合には、右辺の残りの要素に `nil` が代入される


```rb
a, b = 1 #=> 1
a #=> 1
b #=> nil
```

### 右辺の要素の数の方が多い場合、余った要素は無視される

```rb
a, b = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
b #=> 2
```

### 右辺の要素の残り全部を配列として受け取ることもできる

- 変数に `*` をつけると、残り全部を配列として受け取れる

```rb
a, *b = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
b #=> [2, 3]
```

- 残りの要素数が 1 つでも配列になる

```rb
a, *b = 1, 2 #=> [1, 2]
a #=> 1
b #=> [2]
```

- 残りの要素がない場合には `nil` ではなく空の配列 `[]` になる

```rb
a, *b = 1 #=> 1
a #=> 1
b #=> []
```

### 先頭だけ取って、あとは捨てる

- 明示的に捨てる場合

```rb
a, * = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
```

- `*` を省略して `,` で終わっても、以降は捨てられる

```rb
a, = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
```

### 末尾だけ取って、後は捨てる

```rb
*, b = 1, 2, 3 #=> [1, 2, 3]
b #=> 3
```

### 先頭と末尾だけ取って、後は捨てる

```rb
a, *, b = 1, 2, 3, 4 #=> [1, 2, 3, 4]
a #=> 1
b #=> 4
```

### 2個目と3個目だけ取って、後は捨てる

```rb
_, a, b, * = 1, 2, 3, 4 #=> [1, 2, 3, 4]
a #=> 2
b #=> 3
```

- `_` は普通の変数なので、実は捨てられていなくて、アクセスできる
 - pry などでは特殊な動きをするので注意

### 全部捨てる

```rb
* = 1, 2, 3 #=> [1, 2, 3]
```

えっ、全部捨てるなら、なんで代入にした？

### ネスト

- 要素が配列になっている場合、変数側に `()` を使うと、1レベル下の要素を代入することができる

```rb
a, (b, c) = 1, [2, 3] #=> [1, [2, 3]]
a #=> 1
b #=> 2
c #=> 3
```

### Hash

- Hash は通常では1つの値として代入される

```rb
a = { a: 1, b: 2 } #=> {:a=>1, :b=>2}
a #=> {:a=>1, :b=>2}
a, b = { a: 1, b: 2 } #=> {:a=>1, :b=>2}
a #=> {:a=>1, :b=>2}
b #=> nil
```

- `*` をつけると `Hash#to_a` されて多重代入できるようになる

```rb
a = *{ a: 1, b: 2 } #=> [[:a, 1], [:b, 2]]
a #=> [[:a, 1], [:b, 2]]
a, b = *{ a: 1, b: 2 } #=> [[:a, 1], [:b, 2]]
a #=> [:a, 1]
b #=> [:b, 2]
```

## ブロック引数への代入

多重代入の基礎が分かったら、ブロック引数への代入に応用してみましょう。

### なんの変哲も無い代入

```rb
[1, 2, 3].map { |n| n } #=> [1, 2, 3]
```

### 配列をそのまま受け取る

- この例では `*` は省略できる

```rb
[[1, 2], [3, 4]].map { |*a| a } #=> [[1, 2], [3, 4]]
```

### 先頭だけをいただく

```rb
[[1, 2], [3, 4]].map { |a,| a } #=> [1, 3]
```

### 足りなければ nil になる

```rb
[[1, 2], [3, 4]].map { |a, b, c| c } #=> [nil, nil]
```

### ブロックの代わりに lambda を使う場合、引数の数に要素の数が満たないと死ぬ

```rb
[[1, 2], [3, 4]].map(&->(a, b, c) { c })
#=> ArgumentError: wrong number of arguments (given 1, expected 3)
```

- ので、要素の数が足りない恐れがあるときには、さらに `()` で括ると「第一引数にArray を受け取って、ネストした代入をする」みたいな扱いになって通る

```rb
[[1, 2], [3, 4]].map(&->((a, b, c)) { [b, c] }) #=> [[2, nil], [4, nil]]
```

### 名前付き引数

- 通常は Hash が渡ってきても多重代入にはならない

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20 }].map { |a, b| [a, b] }
#=> [[{:a=>1, :b=>2}, nil], [{:a=>10, :b=>20}, nil]]
```

- `Hash#to_a` を挟めば、それっぽくはなる

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20 }].map(&:to_a).map { |a, b| [a, b] }
#=> [[[:a, 1], [:b, 2]], [[:a, 10], [:b, 20]]]
```

- 名前付き引数を使うと、value だけを代入することができる

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20 }].map { |a:, b:| [a, b] }
#=> [[1, 2], [10, 20]]
```

- ただし、名前付き引数を使うときには、想定されない key が来ると死ぬ

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |a:, b:| [a, b] }
#=> ArgumentError: unknown keyword: c
```

- でも、最後に `**x` みたいなのを書けば、名前付きで指定していない部分の Hash がもらえる

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |a:, b:, **x| [a, b, x] }
#=> [[1, 2, {}], [10, 20, {:c=>99}]]
```

- 捨てたい時は変数名を省略して `**` だけにする

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |a:, b:, **| [a, b] }
#=> [[1, 2], [10, 20]]
```

- 配列の最後の要素が Hash のときにもキーワード引数が使える

```rb
[[1, 2, 3, { a: 1, b: 2 }], [4, 5, 6, { a: 10, b: 20 }]].map { |*ary, a:, b:| [a, b] }
#=> [[1, 2], [10, 20]]
```

- ネストの先で名前付き引数を使うことはできない

```rb
[[{ b: 10 }, 1]].map { |h, n| h } #=> [{:b=>10}]
[[{ b: 10 }, 1]].map { |(b:), n| b } #=> SyntaxError: unexpected '}', expecting end-of-input
```

#### Ruby 3.0 以降
Ruby 3.0 以降ではブロック引数で名前付きの引数は使えないので、代わりにパターンマッチを使ったりするしかないかもしれない

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |h|
  h => {a:, b:}
  [a, b]
}
#=> [[1, 2], [10, 20]]
```

でも↑の例なら `Hash#values_at` の方が良さそう……

```rb
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |h|
  h.values_at(:a, :b)
}
#=> [[1, 2], [10, 20]]
```



### 右代入

[Ruby 3.0.0](https://www.ruby-lang.org/ja/news/2020/12/25/ruby-3-0-0-released/) に右代入が登場。これは熱い。

```rb
42 => n
n
#=> 42
```

でも多重代入しようとすると、だめっぽい

```rb
42, 42 => a, b
#=> -e:1: syntax error, unexpected ',', expecting end-of-input
#=> 42, 42 => a, b
```

多重代入したい時には配列を使うとできます

```rb
[42, 42] => [a, b] => c
a #=> 42
b #=> 42
c #=> [42,42]
```

右代入パターンマッチもあります

```rb
{ a: 42, b: 4242 } => { a:, b: }
a #=> 42
b #=> 4242
```

複雑なパターンでもいけます

```rb
[{ a: 42, b: 4242 }, { c: 424242, d: 42424242 }] => [{ a:, b: }, { c:, d: }]
a #=> 42
b #=> 4242
c #=> 424242
d #=> 42424242
```

## まとめ

- Ruby の代入の柔軟性の高さ凄い

## おまけ

- あ、すごい偶然なんですけど、今日、わしの誕生日です。毎年、みなさまのおかげで生かされております。[毎度どうもありがとうございます](http://yancya.jp/hoshi_wish)