Ruby如何返回两个值?


94

每当交换数组中的值时,请确保将值之一存储在引用变量中。但是我发现Ruby可以返回两个值以及自动交换两个值。例如,

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

我想知道Ruby是如何做到的。


9
从技术上讲,Ruby不返回两个值。它可以返回一个数组,该数组又分配给两个变量。
Charles Caldwell

Answers:


164

与其他语言不同,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

8
您可以显式返回一个数组[1, 2],其工作原理与上述示例相同。
Hauleth

6
@hauleth一个很好的观察。我应该明确指出1,2,它本身是无效的,但要么有效,return 1,2要么[1,2]有效。
tadman

50

不,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从一个方法或nextbreak从块,红宝石将把这种-像一个分配的右手边,所以

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工作成功的原因。它实际上yieldSA 两个元素Array键和值来块的,但我们平时写

some_hash.each {|k, v| }

代替

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

16

tadman和JörgW 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ö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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.