为什么这个Ruby方法通过引用传递它的参数

我回答了这个问题并偶然发现了一些奇怪的事情。 Ruby通过值传递参数,但变量本身就是引用。 那么为什么第一种方法似乎通过引用传递它的参数呢?

require 'set' require 'benchmark' def add_item1!(item, list) list << item unless list.include?(item) end def add_item2(item, list) list |= [item] end def add_item3(item, list) set = Set.new(list) set << item list = set.to_a end array1 = [3,2,1,4] add_item1!(5, array1) p array1 # [3, 2, 1, 4, 5] array2 = [3,2,1,4] add_item2(5, array2) p array2 # [3, 2, 1, 4] array3 = [3,2,1,4] add_item3(5, array3) p array3 # [3, 2, 1, 4] 

非混淆的术语是对象共享调用 : 传递原始对象(而不是副本/克隆/副本)。

共享调用的语义与引用调用的不同之处在于函数内函数参数的赋值对调用者不可见

在Ruby中, 重新分配[local]参数对调用者没有影响因为它不使用Call by Reference。

在这个示例代码中,它显然没有Call by Reference语义; 或者第二和第三种情况,它们分配回局部变量但不修改原始对象 ,就像第一种情况一样。

在“较低级别”,实现是[引用参数]的值调用 – 也就是说,内部Ruby使用指针和诸如此类的东西 – 这就是为什么有时使用重载短语“通过引用调用”,经常忘记“按值”部分..并导致这种混乱。


 def add_item1!(item, list) # MUTATES the list object, which is the original object passed # (there is no copy/clone/duplication that occurred) list << item unless list.include?(item) end def add_item2(item, list) # this creates a NEW list and re-assigns it to the parameter # re-assigning to a local parameter does not affect the caller # the original list object is not modified list |= [item] end def add_item3(item, list) set = Set.new(list) set << item # this creates a NEW list from set and re-assigns it to the parameter # re-assigning to a local parameter does not affect the caller # the original list object is not modified list = set.to_a end