Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

Ruby の多重代入はいろいろな書き方があって便利ですが、忘れがちなのでまとめておきます

変数への代入

何の変哲も無い代入

a = 1 #=> 1
a #=> 1

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

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

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

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

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

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

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

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

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

  • 変数に * をつけると、残り全部を配列として受け取れる
a, *b = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
b #=> [2, 3]
  • 残りの要素数が 1 つでも配列になる
a, *b = 1, 2 #=> [1, 2]
a #=> 1
b #=> [2]
  • 残りの要素がない場合には nil ではなく空の配列 [] になる
a, *b = 1 #=> 1
a #=> 1
b #=> []

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

  • 明示的に捨てる場合
a, * = 1, 2, 3 #=> [1, 2, 3]
a #=> 1
  • * を省略して , で終わっても、以降は捨てられる
a, = 1, 2, 3 #=> [1, 2, 3]
a #=> 1

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

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

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

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

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

_, a, b, * = 1, 2, 3, 4 #=> [1, 2, 3, 4]
a #=> 2
b #=> 3
  • _ は普通の変数なので、実は捨てられていなくて、アクセスできる
    • pry などでは特殊な動きをするので注意

全部捨てる

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

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

ネスト

  • 要素が配列になっている場合、変数側に () を使うと、1レベル下の要素を代入することができる
a, (b, c) = 1, [2, 3] #=> [1, [2, 3]]
a #=> 1
b #=> 2
c #=> 3

Hash

  • Hash は通常では1つの値として代入される
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 されて多重代入できるようになる
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]

ブロック引数への代入

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

なんの変哲も無い代入

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

配列をそのまま受け取る

  • この例では * は省略できる
[[1, 2], [3, 4]].map { |*a| a } #=> [[1, 2], [3, 4]]

先頭だけをいただく

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

足りなければ nil になる

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

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

[[1, 2], [3, 4]].map(&->(a, b, c) { c })
#=> ArgumentError: wrong number of arguments (given 1, expected 3)
  • ので、要素の数が足りない恐れがあるときには、さらに () で括ると「第一引数にArray を受け取って、ネストした代入をする」みたいな扱いになって通る
[[1, 2], [3, 4]].map(&->((a, b, c)) { [b, c] }) #=> [[2, nil], [4, nil]]

名前付き引数

  • 通常は Hash が渡ってきても多重代入にはならない
[{ 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 を挟めば、それっぽくはなる
[{ 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 だけを代入することができる
[{ a: 1, b: 2 }, { a: 10, b: 20 }].map { |a:, b:| [a, b] }
#=> [[1, 2], [10, 20]]
  • ただし、名前付き引数を使うときには、想定されない key が来ると死ぬ
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |a:, b:| [a, b] }
#=> ArgumentError: unknown keyword: c
  • でも、最後に **x みたいなのを書けば、名前付きで指定していない部分の Hash がもらえる
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |a:, b:, **x| [a, b, x] }
#=> [[1, 2, {}], [10, 20, {:c=>99}]]
  • 捨てたい時は変数名を省略して ** だけにする
[{ a: 1, b: 2 }, { a: 10, b: 20, c: 99 }].map { |a:, b:, **| [a, b] }
#=> [[1, 2], [10, 20]]
  • 配列の最後の要素が Hash のときにもキーワード引数が使える
[[1, 2, 3, { a: 1, b: 2 }], [4, 5, 6, { a: 10, b: 20 }]].map { |*ary, a:, b:| [a, b] }
#=> [[1, 2], [10, 20]]
  • ネストの先で名前付き引数を使うことはできない
[[{ b: 10 }, 1]].map { |h, n| h } #=> [{:b=>10}]
[[{ b: 10 }, 1]].map { |(b:), n| b } #=> SyntaxError: unexpected '}', expecting end-of-input

まとめ

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

おまけ

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした