每当交换数组中的值时,请确保将值之一存储在引用变量中。但是我发现Ruby可以返回两个值以及自动交换两个值。例如,
array = [1, 3, 5 , 6 ,7]
array[0], array[1] = array[1] , array[0] #=> [3, 1]
我想知道Ruby是如何做到的。
Answers:
与其他语言不同,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
从块,红宝石将把这种-像一个分配的右手边,所以
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 单两个元素Array
键和值来块的,但我们平时写
some_hash.each {|k, v| }
代替
some_hash.each {|(k, v)| }
tadman和JörgW Mittag比我更了解Ruby,他们的回答也没有错,但是我认为他们并没有回答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
。
分配之前,首先要评估整个右手边,这是因为当两个边=
都有不同数量的术语时,需要进行调整,而JörgW Mittag的描述可能与之间接相关,但这不是主要问题。