LoginSignup
38
33

More than 5 years have passed since last update.

Rubyで複数の変数に別々の値を代入するイディオム

Last updated at Posted at 2013-09-24

Rubyで複数の変数に配列の値を展開するイディオムやその実行結果を色々調べてみました。

# カンマ区切りでxとyに別々の値を代入
x, y = 1, 2 # x => 1, y => 2

# 配列にしても同じ
x, y = [1, 2] # x => 1, y => 2

# 要素数が多いと切り捨てられる
x, y = [1, 2, 3, 4]  # x => 1, y => 2

# 配列とカンマ区切りが並ぶと、xには配列が代入される
x, y = [1, 2], 3, 4 # x => [1, 2], y => 3

# *を付けると配列が展開される(x, y = 1, 2, 3, 4と同じ)
x, y = *[1, 2], 3, 4 # x => 1, y => 2

# x, y = 2, 3, 4と同じ
x, y = *[2], 3, 4 # x => 2, y => 3

# x, y = 3, 4と同じ
x, y = *[], 3, 4 # x => 3, y => 4

# *を付けなければ、xには空の配列が代入される
x, y = [], 3, 4 # x => [], y => 3

# 配列の配列だけなら、*があってもなくても同じ
x, y = [[1, 2], [3, 4]] # x => [1, 2], y => [3, 4]
x, y = *[[1, 2], [3, 4]] # x => [1, 2], y => [3, 4]

# 配列の配列とカンマ区切りの値が混在していると、*の有無で挙動が変わる
x, y = [[1, 2], [3, 4]], 5, 6 # x => [[1, 2], [3, 4]], y => 5
x, y = *[[1, 2], [3, 4]], 5, 6 # x => [1, 2], y => [3, 4]

# 値や配列が一つだと、xにのみ値が入る
x, y = 1 # x => 1, y => nil
x, y = [1] # x => 1, y => nil
x, y = *[1] # x => 1, y => nil

# 配列が空だと、どちらもnilになる
x, y = [] # x => nil, y => nil
x, y = *[] # x => nil, y => nil

なんでこんなこと調べたの?

なんでこんなこと調べたのかというと、ここのコードの意味がよくわからなかったからです。

secret, guess = *[@secret.to_a, guess.to_a].transpose.reject {|s, g| mark >> '+' if s == g }.transpose || [], []
secret.each {|c| mark << '-' if guess.delete_first c }

おそらく|| []の部分は不要で、こう書いても動作は同じでした。

secret, guess = *[@secret.to_a, guess.to_a].transpose.reject {|s, g| mark >> '+' if s == g }.transpose, []
secret.each {|c| mark << '-' if guess.delete_first c }

最後に", []"が必要な理由

[@secret.to_a, guess.to_a].transpose.reject {|s, g| mark >> '+' if s == g }.transposeの部分は通常、[[1,2],[3,4]]のような配列の配列が返ってきます。
なので、secret, guess = *[[1,2], [3, 4]]となり、secret = [1,2]guess = [3,4]となります。

しかし、全ての要素がrejectされると結果は[]になります。
すると、secret, guess = *[]となり、secretguessnilとなります。

secretnilだと、次の行でundefined method 'each' for nil:NilClassのエラーが出るので、secret, guess = *[], []として、secret[]が代入されるようにしているわけです。

Be more readable

でもこれだとちょっとトリッキーなので、僕だったらこう書くかな~と思いました。

secret, guess = *[@secret.to_a, guess.to_a].transpose.reject {|s, g| mark >> '+' if s == g }.transpose
secret.each {|c| mark << '-' if guess.delete_first c } unless secret.nil?

(secret || []).eachと書くのも良いかもしれませんね。

38
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
33