0

I have a simple code below

    def testarray
      arr_tree = [1,2,3,4,5]
      (1..3).each do |index|
        abcde(arr_tree)
        puts arr_tree[0]
      end
    end

    def abcde(node_tree)
      node_tree[0] += 100
    end

So inside testarray function, I have an array arr_tree which is being passed to a function abcde. I change the value of the array inside abcde function and print array inside testarray I get the changed value here. So the output is

101
201
301

but I was expecting

1
1
1

Please explain why the results are like this? Also how can I achieve my expected result?

akuhn
  • 27,477
  • 2
  • 76
  • 91
asdfkjasdfjk
  • 3,784
  • 18
  • 64
  • 104

2 Answers2

6

Everything is "passed as an object" in Ruby.

And arrays are objects.

If you want to avoid side-effects make a copy of the argument

def abcde(node_tree)
  copy = node_tree.dup
  copy[0] += 100
  copy
end

A note of caution though since you named your variable tree, the dup method makes a shallow copy only. Shallow means that the array is copied but not its elements.

Why did I not say "pass by reference" above?

"Pass by reference" is a concept from another language and does not make sense in the context of Ruby. Technically the native implementation of Ruby uses pass by value, where the value is a pointer to an object structure. However thinking of Ruby as using "pass by value" is misleading since it does not create a copy of data structures before they are passed to a function. Which seems to be what you assumed would happen…

akuhn
  • 27,477
  • 2
  • 76
  • 91
2

Your assumption is wrong: is not , it is strictly , always. You can easily verify this by running this simple test:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value

More precisely, Ruby is (also known as call-by-object and call-by-sharing), which is a special case of pass-by-value, where the value that is passed is always a pointer to a (potentially) shared (potentially) mutable object:

def is_ruby_pass_by_value?(foo)
  foo.replace('More precisely, it is call-by-object-sharing!')
  foo = 'No, Ruby is pass-by-reference.'
end

bar = 'Yes, of course, Ruby *is* pass-by-value!'

is_ruby_pass_by_value?(bar)

p bar
# 'More precisely, it is call-by-object-sharing!'

However, that is actually irrelevant to your question. It has nothing to do with pass-by-reference vs. pass-by-value.

So inside testarray function, I have an array arr_tree which is being passed to a function abcde. I change the value of the array inside abcde function and print array inside testarray I get the changed value here.

You get the changed value because you change the value. It's as simple as that. Ruby is not a pure functional programming language, it does have mutable state. And if you mutate that state, then the old state is gone and only the new state exists.

You wrote yourself:

I change the value of the array

Well, if you change the array, the array changes! There is only one array in your entire code.

Please explain why the results are like this?

The array changes because you change the array.

Also how can I achieve my expected result?

You can achieve your result by not changing the array:

def testarray
  arr_tree = [1, 2, 3, 4, 5]
  (1..3).each do |index|
    abcde(arr_tree)
    puts arr_tree[0]
  end
end

def abcde(node_tree)
  [node_tree.first + 100, *node_tree.drop(1)]
end
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653