Rubyは2つの値をどのように返しますか?


94

配列の値を交換するときはいつでも、参照変数に値の1つを保存したことを確認します。しかし、Rubyは2つの値を返すだけでなく、2つの値を自動的に交換できることがわかりました。例えば、

array = [1, 3, 5 , 6 ,7]
array[0], array[1] = array[1] , array[0] #=> [3, 1] 

Rubyがこれをどのように行うのか疑問に思いました。


9
技術的には、Rubyは2つの値を返しません。これは、2つの変数に割り当てられる1つの配列を返すことができます。
Charles Caldwell

回答:


164

他の言語とは異なり、Rubyのメソッド呼び出しの戻り値は常にオブジェクトです。これは、Rubyのすべてと同様に、nilそれ自体がオブジェクトであるために可能です。

あなたが見る3つの基本的なパターンがあります。特定の値を返さない:

def nothing
end

nothing
# => nil

特異値を返す:

def single
  1
end

x = single
# => 1

これは、他のプログラミング言語に期待することと一致しています。

複数の戻り値を処理する場合、状況は少し異なります。これらは明示的に指定する必要があります:

def multiple
  return 1, 2
end

x = multiple
# => [ 1, 2 ]
x
# => [ 1, 2 ]

複数の値を返す呼び出しを行う場合、それらを独立した変数に分割できます。

x, y = multiple
# => [ 1, 2 ]
x
# => 1
y
# => 2

この戦略は、あなたが話している種類の置換にも有効です:

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

8
明示的に配列[1, 2]を返すことができ、これは上記の例と同じように機能します。
Hauleth 2015

6
@hauleth良い観察。私は1,2それ自体は無効ですが、return 1,2またはのどちらかが無効であることを明確にすべき[1,2]でした。
tadman 2015

50

いいえ、Rubyは実際には2つのオブジェクトを返すことをサポートしていません。(ところで、変数ではなくオブジェクトを返します。より正確には、オブジェクトへのポインタを返します。)

ただし、並列割り当てはサポートされています。割り当ての右側に複数のオブジェクトがある場合、オブジェクトは次のように収集されますArray

foo = 1, 2, 3
# is the same as
foo = [1, 2, 3]

割り当ての左側に複数の「ターゲット」(変数またはセッターメソッド)がある場合、変数Arrayは右側のの要素にバインドされます。

a, b, c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary[2]

右側である場合はないArray、それが用いたものに変換されるto_ary方法を

a, b, c = not_an_ary
# is the same as
ary = not_an_ary.to_ary
a = ary[0]
b = ary[1]
c = ary[2]

そして、2つを組み合わせると、

a, b, c = d, e, f
# is the same as
ary = [d, e, f]
a = ary[0]
b = ary[1]
c = ary[2]

これに関連するのは、割り当ての左側にあるスプラット演算子です。それは、「右側にあるの残りの要素をすべて取る」ことを意味しますArray

a, b, *c = ary
# is the same as
a = ary[0]
b = ary[1]
c = ary.drop(2) # i.e. the rest of the Array

そして最後に重要なことですが、並列割り当ては括弧を使用してネストできます:

a, (b, c), d = ary
# is the same as
a = ary[0]
b, c = ary[1]
d = ary[2]
# which is the same as
a = ary[0]
b = ary[1][0]
c = ary[1][1]
d = ary[2]

あなたの場合はreturnこの方法からかnextまたはbreakブロックから、Rubyはこの種-の割り当ての右側のように扱いますので、

return 1, 2
next 1, 2
break 1, 2
# is the same as
return [1, 2]
next [1, 2]
break [1, 2]

ちなみに、これはメソッドとブロックのパラメーターリストでも機能します(メソッドはより厳密で、ブロックはそれほど厳密ではありません)。

def foo(a, (b, c), d) p a, b, c, d end

bar {|a, (b, c), d| p a, b, c, d }

「それほど厳密でない」ブロックは、たとえば、機能するものHash#eachです。また、実際yieldのsa シングル 2要素Arrayブロックへのキーと値のが、我々は通常、書き込み

some_hash.each {|k, v| }

の代わりに

some_hash.each {|(k, v)| }

16

tadmanとJörgW Mittagは私よりもRubyをよく知っていて、答えは間違っていませんが、OPが知りたかったことに答えているとは思いません。質問ははっきりしていなかったと思います。私の理解では、OPが求めたかったことは、複数の値を返すこととは関係ありません。


次の2つの変数の値を切り替えたいとき本当の問題は、あるab、なぜ一時的な変数を使用する必要はありません(元の質問のように、配列または2つの位置を)tempのように:

a, b = :foo, :bar
temp = a
a = b
b = temp

しかし、次のように直接行うことができます:

a, b = :foo, :bar
a, b = b, a

その答えは、多重代入では、右側全体が左側全体の代入の前に評価され、それが1つずつ実行されるわけではないということです。したがって、a, b = b, aと同等ではありませんa = b; b = a

代入の前に最初に右辺全体を評価することは、両側=の項の数が異なる場合の調整に続く必要性であり、JörgW Mittagの説明はそれに間接的に関連している可能性がありますが、それは主要な問題ではありません。


8

値が少ない場合は、配列が適切なオプションです。結果の順序を知る必要がなく(そして混乱することなく)複数の戻り値が必要な場合は、別の方法として、必要な名前付き値を含むハッシュを返すこともできます。

例えば

def make_hash
  x = 1
  y = 2
  {x: x, y: y}
end

hash = make_hash
# => {:x=>1, :y=>2}
hash[:x]
# => 1
hash[:y]
# => 2
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.