简体   繁体   中英

Why this Ruby method passes it's argument by reference

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]

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM