阅读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"]}"
答案 0 :(得分:1)
Ruby是按值传递,但值是对象的引用。
在您的第一个实验中,baz
是对字符串"value"
的引用。当您致电bar
时,baz
会初始化为foo
的副本(即参考文件的副本)。然后,您使用对字符串bar
的引用覆盖"reference"
。由于bar
是副本,因此覆盖它不会更改baz
。
在您的第二个实验中,baz
再次引用字符串"value"
,bar
在您调用baz
时初始化为foo
的副本}。这次你不会覆盖bar
,而是在上面调用一个方法。虽然bar
是baz
的副本,但它们引用相同的对象(字符串"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并对其进行评论。