通过splat运算符引用块来传递参数

时间:2012-07-31 16:53:19

标签: ruby splat

当使用splat运算符通过引用将参数传递给块时,似乎复制了参数。

我有这个:

def method
  a = [1,2,3]
  yield(*a)
  p a
end

method {|x,y,z| z = 0}
#=> this puts and returns [1, 2, 3] (didn't modified the third argument)

如何通过引用传递这些参数?如果我直接传递数组似乎可行,但splat运算符在这里更实用,直观和可维护。

4 个答案:

答案 0 :(得分:7)

  1. 在Ruby中编写x = value时,您创建了一个新的局部变量x,无论它是否存在(如果存在,名称只是反弹而且原始值保持不变) 。因此,您无法以这种方式就地更改变量。

  2. 整数是不可变的。因此,如果您发送一个整数,则无法更改其值。请注意,您可以更改可变对象(字符串,哈希,数组......):

    def method
      a = [1, 2, "hello"]
      yield(*a)
      p a
    end
    
    method { |x,y,z| z[1] = 'u' }
    # [1, 2, "hullo"]
    
  3. 注意:我试图回答你的问题,现在我的意见是:更新方法或块中的参数会导致错误的代码(你再也没有referential transparency)。返回新值并让调用者自行更新变量。

答案 1 :(得分:2)

这里的问题是=标志。它使局部变量z被分配给另一个对象。

以字符串为例:

def method
  a = ['a', 'b', 'c']
  yield(*a)
  p a
end

method { |x,y,z| z.upcase! }   # => ["a", "b", "C"]

这清楚地表明z与数组的第三个对象相同。

这里的另一点是你的例子是数字。 Fixnums有固定的ID;因此,您无法在保持相同的对象ID时更改数字。要更改Fixnums,您必须使用=为变量分配一个新数字,而不是像inc!这样的自我更改方法(此类方法在Fixnums上不存在)。

答案 2 :(得分:0)

是...数组包含对象的 链接 。在使用yield(*a)的代码中,然后在块中使用变量,这些变量指向数组中 的对象。现在查找代码示例:

daz@daz-pc:~/projects/experiments$ irb
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a.object_id
=> 3
irb(main):003:0> a = 2
=> 2
irb(main):004:0> a.object_id
=> 5

所以在块中你没有 更改 旧对象,你只需创建另一个对象并将其设置为变量。但是数组包含指向旧对象的链接。

查看调试内容:

def m
  a = [1, 2]
  p a[0].object_id
  yield(*a)
  p a[0].object_id
end

m { |a, b| p a.object_id; a = 0; p a.object_id }

输出:

3
3
1
3

答案 3 :(得分:0)

  

如何通过引用传递这些参数?

你不能在Ruby中通过引用传递参数。 Ruby是按值传递的。总是。没有例外,没有ifs,没有buts。

  

如果我直接传递数组似乎有效

我非常怀疑。你根本无法通过Ruby中的引用传递参数。周期。