Всякий раз, когда я меняю местами значения в массиве, я убеждаюсь, что сохранил одно из значений в ссылочной переменной. Но я обнаружил, что Ruby может возвращать два значения, а также автоматически менять местами два значения. Например,
array = [1, 3, 5 , 6 ,7]
array[0], array[1] = array[1] , array[0] #=> [3, 1]
Мне было интересно, как это делает Руби.
Ответы:
В отличие от других языков, возвращаемое значение любого вызова метода в Ruby всегда является объектом. Это возможно, потому что, как и все в Ruby,
nil
само по себе является объектом.Вы увидите три основных шаблона. Не возвращая конкретного значения:
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
источник
[1, 2]
и это будет работать так же, как и в приведенных выше примерах.1,2
сам по себе недействителен, но либо работает,return 1,2
либо[1,2]
работает.Нет, на самом деле Ruby не поддерживает возврат двух объектов. (Кстати: вы возвращаете объекты, а не переменные. Точнее, вы возвращаете указатели на объекты.)
Однако он поддерживает параллельное присвоение. Если у вас есть более одного объекта в правой части назначения, объекты собираются в
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]
И если мы сложим их вместе, мы получим
a, b, c = d, e, f # is the same as ary = [d, e, f] a = ary[0] b = ary[1] c = ary[2]
С этим связан оператор splat в левой части присваивания. Это означает "взять все оставшиеся элементы в
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
са одного из двух элементовArray
ключа и значения в блок, но мы обычно пишемsome_hash.each {|k, v| }
вместо того
some_hash.each {|(k, v)| }
источник
Тадман и Йорг В. Миттаг знают Руби лучше меня, и их ответы не ошибочны, но я не думаю, что они отвечают на то, что хотел знать OP. Я думаю, что вопрос был непонятен. Насколько я понимаю, то, что OP хотел спросить, не имеет ничего общего с возвратом нескольких значений.
Настоящий вопрос заключается в том, что когда вы хотите переключить значения двух переменных
a
иb
(или двух позиций в массиве, как в исходном вопросе), почему нет необходимости использовать временную переменную,temp
например:a, b = :foo, :bar temp = a a = b b = temp
но это можно сделать напрямую, например:
a, b = :foo, :bar a, b = b, a
Ответ заключается в том, что при множественном присваивании вся правая часть оценивается до назначения всей левой части, и это не выполняется по одному. Так
a, b = b, a
не эквивалентноa = b; b = a
.Первая оценка всей правой части перед назначением является необходимостью, которая следует из корректировки, когда обе стороны
=
имеют разное количество терминов, и описание Йорга Миттага может быть косвенно связано с этим, но это не главная проблема.источник
Массивы - хороший вариант, если у вас всего несколько значений. Если вам нужно несколько возвращаемых значений без необходимости знать (и вас смущает) порядок результатов, альтернативой может быть возврат хеша, который содержит любые именованные значения, которые вы хотите.
например
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
источник