Ruby 如何返回两个值?

无论何时在数组中交换值,都要确保将其中一个值存储在引用变量中。但是我发现 Ruby 可以返回两个值,也可以自动交换两个值。比如说,

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

我想知道 Ruby 是怎么做到的。

119931 次浏览

与其他语言不同,Ruby 中任何方法调用的返回值都是 一直都是对象。这是可能的,因为与 Ruby 中的所有内容一样,nil本身也是一个对象。

你会看到三种基本模式,不返回特定值:

def nothing
end


nothing
# => nil

返回一个单数值:

def single
1
end


x = single
# => 1

This is in line with what you'd expect from other programming languages.

Things get a bit different when dealing with multiple return values. These need to be specified explicitly:

def multiple
return 1, 2
end


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

When making a call that returns multiple values, you can break them out into independent variables:

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

不 Ruby 实际上不支持返回两个对象。(顺便说一下: 你返回的是对象,而不是变量。更准确地说,是返回指向对象的指针。)

但是,它支持并行分配。如果在赋值的右边有多个对象,那么这些对象将被收集到 Array中:

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

如果在赋值的左边有多个“ target”(变量或 setter 方法) ,变量就会绑定到右边的 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的一个 single两元素 Array的键和值的块,但我们通常写

some_hash.each {|k, v| }

而不是

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

Tadman 和 Jörg W Mittag 比我更了解 Ruby,他们的回答没有错,但我不认为他们回答了 OP 想知道的问题。我认为这个问题不是很清楚。在我的理解中,OP 想要问的问题与返回多个值无关。


真正的问题是,当你想要切换两个变量 ab的值(或者在一个数组中的两个位置) ,为什么没有必要使用时间变量 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

=的两侧有不同数量的术语时,在分配之前首先评估整个右边是一个必要的调整,Jörg W Mittag 的描述可能与此间接相关,但这不是主要问题。

如果只有几个值,数组是一个很好的选择。如果您想要多个返回值,而不需要知道(并且不会被)结果的顺序混淆,那么另一种方法是返回一个 Hash,其中包含您想要的任何命名值。

例如:。

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

按照一些人的建议创建哈希绝对比数组好,因为数组索引可能会让人感到困惑。当需要在某个索引处返回附加属性时,我们需要对返回值与 array 一起使用的所有位置进行更改。

另一种更好的方法是使用 OpenStruct。与使用散列相比,它的优势在于易于访问。

例子: computer = OpenStruct.new(ram: '4GB')

有多种方法可以访问 ram的值

  • 作为符号键: computer[:ram]
  • 作为字符串键: computer['ram']
  • 作为属性(访问器方法) : computer.ram

参考文章: https://medium.com/rubycademy/openstruct-in-ruby-ab6ba3aff9a4