【问题标题】:What is the difference of Ruby's Array#to_a methodRuby 的 Array#to_a 方法有什么区别
【发布时间】:2013-08-12 08:24:06
【问题描述】:

例如:

a = [1,2,3,4]
b = a
c = a.to_a
a.insert(0,0)  #=> [0,1,2,3,4]
b              #=> [0,1,2,3,4]
c              #=> [0,1,2,3,4]

为什么数组bc的输出是一样的?如果我想获取数组a 的副本,而不是引用,我应该使用哪种方法?

【问题讨论】:

    标签: ruby arrays


    【解决方案1】:

    为什么数组b和c的输出是一样的?

    因为三个局部变量都引用了同一个对象,如下:

    a = [1,2,3,4]
    b = a
    c = a.to_a
    a.object_id
    # => 72187200
    b.object_id
    # => 72187200
    c.object_id
    # => 72187200
    

    如果我想获取数组 a 的副本,而不是引用的,我应该使用哪种方法?

    然后使用a.dup。这里记录了Object#dup

    a = [1,2,3,4]
    b = a.dup
    c = a.dup
    a.object_id
    # => 82139270
    b.object_id
    # => 82139210
    c.object_id
    # => 82134600
    a.insert(0,0) # => [0, 1, 2, 3, 4]
    b # => [1, 2, 3, 4]
    c # => [1, 2, 3, 4]
    

    Array#to_a 说:返回 self。如果在 Array 的子类上调用,则将接收者转换为 Array 对象。

    所以它不会根据您的需要提供帮助。

    【讨论】:

      【解决方案2】:

      这是因为Array#to_a returns self,所以两个变量都包含对同一个Array对象的引用。为了获得具有相同内容的新数组,您可以使用dupclone (read about the differences between dup and clone):

      a = [1, 2, 3]
      b = a.dup
      a << 4
      a  #=> [1, 2, 3, 4]
      b  #=> [1, 2, 3]
      

      但请注意,相同的对象引用存储在新数组中。这意味着如果你改变对象本身,它们仍然会在两个数组中发生变化:

      a = ['foo', 'bar']
      b = a.dup
      a[0] << 'baz'
      a  #=> ["foobaz", "bar"]
      b  #=> ["foobaz", "bar"]
      

      这是因为dupcloneshallow-copies

      【讨论】:

        【解决方案3】:

        Array#to_a 返回接收者。这就是为什么bc 指的是同一个东西。关于为什么to_a 返回原始数组,原则上,它可以以一种或另一种方式定义,但我猜to_a 的一个用例是将其应用于可能是nil 的变量以确保它变为一个数组。

        some_value.to_a # => `[]` if `some_value` is `nil`
        

        在这种用例中,如果接收器已经是一个数组,则无需将数组替换为另一个数组。这在性能上会更可取。

        【讨论】:

          【解决方案4】:

          原因是变量只是对数据的引用。变量存储在内存中;变量保存它们在内存中所在位置的地址。所以当你这样做时:

          a = b
          

          这两个变量指向同一个内存位置,因此如果你改变ab 也会被改变,因为它是同一个对象。

          有几种方法可以强制 Ruby 创建对象的另一个副本。最流行的是LBg提到的dup方法。但是请注意,它只是创建一个浅拷贝。如果你运行:

          a = ['foo','bar', []]
          b = a.dup
          
          a << 'blah'
          b              #=> ['foo', 'bar', []] as expected but
          
          b[3] << blah
          
          a              #=> ['foobar', 'bar', ['blah]]
          

          原因是数组实际上是一个引用数组,嵌套数组在执行dup时没有重复,所以它们是同一个对象。

          要创建对象的深层副本,您可以使用 Marshall 模块:

          b = Marshal.load(Marshal.dump(a))
          

          但是,通常您实际上并不需要这样做。此外,某些对象不能被复制(例如符号)。

          【讨论】:

            【解决方案5】:

            你可以这样做

            b = a.dup
            

            旧帖
            如果没有更简单的方法,你可以试试这个

            b = a.map {|x| x}
            

            有效

            1.9.3-p448 :001 > a = [1,2,3]      => [1, 2, 3] 
            1.9.3-p448 :002 > b = a            => [1, 2, 3] 
            1.9.3-p448 :003 > c = a.map{|x| x} => [1, 2, 3] 
            1.9.3-p448 :004 > a<<0             => [1, 2, 3, 0] 
            1.9.3-p448 :005 > b                => [1, 2, 3, 0] 
            1.9.3-p448 :006 > c                => [1, 2, 3] 
            

            但这是一个浅拷贝。

            根据this posta.dup 是更简单的方法。

            【讨论】:

            • 为什么不干脆做b = a.dup
            • @LBg 我自己是 ruby​​ 新手,刚刚也注意到了一个
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-12-03
            • 1970-01-01
            • 2012-04-28
            • 2010-10-11
            • 1970-01-01
            • 1970-01-01
            • 2016-07-01
            相关资源
            最近更新 更多