Why this Ruby method passes it's argument by reference

落爺英雄遲暮 提交于 2019-12-11 02:21:20

问题


I answered on this question and stumbled on something strange. Ruby passes its arguments by value, the variables itself are references though. So why the first methods seems to pass its parameter by reference ?

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]

回答1:


The non-confusing term is Call by Object-Sharing: the original object (and not a copy/clone/duplicate) is passed.

The semantics of call by sharing differ from call by reference in that assignments to function arguments within the function aren't visible to the caller

In Ruby re-assigning [local] parameters has no effect on the caller as it does not use Call by Reference.

In this example code it clearly does not have Call by Reference semantics; or the 2nd and 3rd cases, which assign back to the local variable but otherwise do not modify the original object, would work like the 1st.

On a 'lower level' the implementation is Call by Value [of a Reference] - that is, internally Ruby uses pointers and whatnot - which is why sometimes the overloaded phrase "call by reference" is used, often forgetting the "by value" part .. and leading to this sort of confusion.


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


来源:https://stackoverflow.com/questions/31506596/why-this-ruby-method-passes-its-argument-by-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!