【问题标题】:How does Ruby return two values?Ruby 如何返回两个值?
【发布时间】:2015-02-25 17:21:25
【问题描述】:

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

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

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

【问题讨论】:

  • 技术上 Ruby 不会返回两个值。它可以返回一个数组,然后分配给两个变量。

标签: ruby


【解决方案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],这和你上面的例子一样。
  • @hauleth 很好的观察。我应该明确指出 1,2 本身是无效的,但 return 1,2[1,2] 中的任何一个都有效。
【解决方案2】:

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

但是,它确实支持并行分配。如果您在作业的右侧有多个对象,则这些对象将被收集到 Array

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

如果在赋值左侧有多个“目标”(变量或 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 来自方法或 nextbreak 来自块时,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 工作的原因。它实际上yields 一个单个两个元素Array 的键和值到块,但我们通常写

some_hash.each {|k, v| }

而不是

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

【讨论】:

    【解决方案3】:

    tadman 和 Jörg W Mittag 比我更了解 Ruby,他们的答案没有错,但我认为他们没有回答 OP 想知道的问题。我认为这个问题并不清楚。据我了解,OP 想问的与返回多个值无关。


    真正的问题是,当你想切换两个变量ab 的值时(或者如原问题中的数组中的两个位置),为什么不需要使用时间变量@ 987654323@点赞:

    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 的描述可能与此间接相关,但这不是主要问题。

    【讨论】:

      【解决方案4】:

      如果您只有几个值,数组是一个不错的选择。如果您想要多个返回值而不必知道(并被)结果的顺序,另一种方法是返回一个包含您想要的任何命名值的 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
      

      【讨论】:

        【解决方案5】:

        按照某些人的建议创建哈希肯定比数组好,因为数组索引可能会造成混淆。当需要在某个索引处返回一个附加属性时,我们需要对所有使用返回值的地方进行更改。

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

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

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

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

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

        【讨论】:

          猜你喜欢
          • 2011-01-01
          • 1970-01-01
          • 2015-10-25
          • 1970-01-01
          • 2021-05-12
          • 2010-11-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多