2017-01-18 1 views
-1

У меня есть простой код нижеRuby: массив передается по ссылке в функцию?

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 

Так внутри testarray функции, у меня есть массив arr_tree который передается в функцию abcde. Я изменяю значение массива внутри функции abcde и массива печати внутри testarray Здесь я получил измененное значение. Таким образом, выход

101 
201 
301 

, но я ожидал

1 
1 
1 

Пожалуйста, объясните, почему результаты, как это? Также как я могу достичь ожидаемого результата?

ответ

4

Все «передано как объект» в Ruby.

И массивы - это объекты.

Если вы хотите, чтобы избежать побочных эффектов сделать копию аргумента

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

нотка предостережения, хотя, так как вы назвали свою переменную tree, метод dup только делает неполную копию. «Мелко» означает, что массив скопирован, но не его элементы.

Почему я не сказал «пройти по ссылке» выше?

«Передача по ссылке» - это концепция с другого языка и не имеет смысла в контексте Ruby. Технически встроенная реализация Ruby использует pass by value, где значение является указателем на структуру объекта. Однако думать о Ruby как использовании «pass by value» вводит в заблуждение, поскольку оно не создает копию структур данных до того, как они будут переданы функции. Который, кажется, что вы предположить, что произойдет ...

+0

«пройти по ссылке» означает, что _assigning_ другое значение переменной будет также изменить переменную в области видимости вызывающего абонента, например, 'node_tree = []'. Это то, что Ruby не позволяет. – Stefan

+0

Извините, но это неправильно. Ruby не проходит по ссылке. –

+1

Когда вы говорите «прохождение по ссылке» в Ruby, это означает, что вы пытаетесь применить концепцию C++ на неправильном языке. –

0

Ваше предположение неверно: является не, это строго , всегда. Вы можете легко проверить, запустив этот простой тест:

def foo(bar) 
    bar = 'reference' 
end 

baz = 'value' 

foo(baz) 

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

Более точно, Рубин (также известный как вызова по объекту и вызова по обмену), который является частным случаем из передачи по значению, где значение, который передается всегда указатель на (потенциально) совместно (потенциально) изменяемый объект:

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!' 

Однако это действие не имеет значения для вашего вопроса. Это не имеет никакого отношения к пропущенным ссылкам против переходящий значение.

Так внутри testarray функции, у меня есть массив arr_tree который передается в функцию abcde. Я изменяю значение массива внутри функции abcde и массива печати внутри testarray Здесь я получил измененное значение.

Вы получаете измененное значение, потому что вы изменяете значение. Это так просто. Ruby не является чистым функциональным языком программирования, он делает имеет изменяемое состояние. И если вы мутируете это состояние, тогда старое состояние исчезнет, ​​и существует только новое состояние.

Вы писали себя:

изменить значение массива

Ну, если изменить массив, изменения массива! В вашем коде есть только один массив.

Пожалуйста, объясните, почему результаты выглядят следующим образом?

Массив изменяется, потому что вы меняете массив.

Также как я могу достичь ожидаемого результата?

Вы можете добиться результата, не изменяя массив:

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