在此Ruby示例中了解值与引用的传递

时间:2016-08-31 13:27:14

标签: ruby

阅读Is ruby pass by reference or value?之后,我学到了很多东西,但是在阅读之前我留下了比我更多的问题(我认为这很好)。

考虑以下示例

def foo(bar) 
  bar = 'reference' 
end
baz = 'value' 
foo(baz)
puts "Ruby is pass-by-#{baz}"

输出 Ruby is pass-by-value

以下是我尝试剖析其工作原理的方法:

首先,在全局范围baz中的值为value

现在foo接受一个参数,无论你传递给它,都在local级别。

因此,当我们通过baz时,另一个baz等于reference,但这是本地级,因此,当我们将其置于全局级别时,它会打印value

现在考虑另一个例子

def foo(bar)
  bar.replace 'reference' 
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"

输出

Ruby is pass-by-reference

如果我上面说的是真的,.replace方法会改变全局baz吗?我能正确地解释这个吗?请随意指出我的尝试中的任何错误,我不知道如果我在正确的轨道上。

谢谢!

修改

更多魔术

def my_foo(a_hash)
  a_hash["test"]="reference"
end;

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"

5 个答案:

答案 0 :(得分:1)

Ruby是按值传递,但值是对象的引用。

在您的第一个实验中,baz是对字符串"value"的引用。当您致电bar时,baz会初始化为foo的副本(即参考文件的副本)。然后,您使用对字符串bar的引用覆盖"reference"。由于bar是副本,因此覆盖它不会更改baz

在您的第二个实验中,baz再次引用字符串"value"bar在您调用baz时初始化为foo的副本}。这次你不会覆盖bar,而是在上面调用一个方法。虽然barbaz的副本,但它们引用相同的对象(字符串"value")。调用该方法会更改该对象的状态。然后,您在to_s上调用baz(间接地,将其替换为"Ruby is pass-by-#{baz}"),to_s返回新状态。

你的第三个实验很像第二个实验。在该方法中,您更改引用副本引用的对象的状态,然后,在方法外部,您通过原始引用读回新状态。

答案 1 :(得分:1)

非常有趣。

object_id一起玩,你会看到红宝石正在做的事情:

def foo(bar)
  puts bar.object_id
  bar = 'reference'
  puts bar.object_id
end

baz = 'value'
puts baz.object_id
foo(baz)

<强>输出

> baz = 'value'
=> "value"

> puts baz.object_id
70241392845040

> foo(baz)
70241392845040
70241392866940

在本地分配bar = 'reference'之后,局部变量bar将引用另一个对象,因此它不会更改原始对象。

在某些情况下,它似乎会成为您对象的dup

答案 2 :(得分:1)

也许这有助于理解它:

x = 'ab'
x.object_id
=> 70287848748000 # a place in memory

x = 'cd'
x.object_id
=> 70287848695760 # other place in memory (other object)

x.replace('xy')
x.object_id
=> 70287848695760 # the same place in memory (the same object)

答案 3 :(得分:0)

它实际上与将参数传递给方法无关。我从您的示例中提取了重要部分:

baz = 'value'
bar = baz
bar = 'reference'
puts baz
bar = baz
bar.replace 'reference'
puts baz

您可能会将变量视为指针。当您使用=时,您将变量指向不同的值,并且原始值保持不变,并且可以通过指向它的其他变量来访问。但是当您使用replace时,您可以更改变量指向的字符串的内容。

答案 4 :(得分:0)

在第一种情况下,您使用bar = 'reference'创建新对象。在第二个.replace中更改您应用它的对象。您可以通过.object_id方法确保这一点。例如:

def foo_eql(bar)
  bar = 'reference'
  puts bar.object_id
  bar
end

def foo_replace(bar)
  bar.replace 'reference'
  puts bar.object_id
  bar
end

baz = 'value'
puts baz.object_id #Here you will get original baz object_id
res1 = foo_eql(baz) #Here you will get printed new object_id
res2 = foo_replace(baz) #Here you will get printed original baz object_id
puts "foo_eql: Ruby is pass-by-#{res1}"
=> foo_eql: Ruby is pass-by-reference
puts "foo_replace: Ruby is pass-by-#{res2}"
=> foo_replace: Ruby is pass-by-reference

所以根本就没有魔法。在使用哈希的示例中,您不会创建新的哈希对象,而是修改现有哈希对象。但是您可以使用以下方法创建新的:

def my_foo(a_hash)
  a_hash = a_hash.merge({"test" => "reference"})
end
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"

基本上,您以“”方式将引用传递给对象。为了更好地理解,请检查this post并对其进行评论。