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 = *[]
となり、secret
もguess
もnil
となります。
secret
がnil
だと、次の行で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
と書くのも良いかもしれませんね。