无论何时在数组中交换值,都要确保将其中一个值存储在引用变量中。但是我发现 Ruby 可以返回两个值,也可以自动交换两个值。比如说,
array = [1, 3, 5 , 6 ,7] array[0], array[1] = array[1] , array[0] #=> [3, 1]
我想知道 Ruby 是怎么做到的。
与其他语言不同,Ruby 中任何方法调用的返回值都是 一直都是对象。这是可能的,因为与 Ruby 中的所有内容一样,nil本身也是一个对象。
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中:
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方法
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
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的一个 single两元素 Array的键和值的块,但我们通常写
Hash#each
yield
some_hash.each {|k, v| }
而不是
some_hash.each {|(k, v)| }
Tadman 和 Jörg W Mittag 比我更了解 Ruby,他们的回答没有错,但我不认为他们回答了 OP 想知道的问题。我认为这个问题不是很清楚。在我的理解中,OP 想要问的问题与返回多个值无关。
真正的问题是,当你想要切换两个变量 a和 b的值(或者在一个数组中的两个位置) ,为什么没有必要使用时间变量 temp,比如:
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。
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。与使用散列相比,它的优势在于易于访问。
OpenStruct
例子: computer = OpenStruct.new(ram: '4GB')
computer = OpenStruct.new(ram: '4GB')
有多种方法可以访问 ram的值
ram
computer[:ram]
computer['ram']
computer.ram
参考文章: https://medium.com/rubycademy/openstruct-in-ruby-ab6ba3aff9a4