通过引用传递或通过复制传递 - Ruby 模块 [重复]
Posted
技术标签:
【中文标题】通过引用传递或通过复制传递 - Ruby 模块 [重复]【英文标题】:Pass by reference or pass by copy - Ruby Modules [duplicate] 【发布时间】:2013-11-21 10:40:42 【问题描述】:我不确定将对象传递给模块方法时会发生什么。对象是通过引用还是通过副本传递的?就像在这个例子中一样:
module SampleModule
def self.testing(o)
o.test
end
end
class SampleClass
def initialize(a)
@a = a
end
def test
@a = @a + 1
end
end
sample_object = SampleClass.new(2)
3.times do
SampleModule.testing(sample_object)
end
p sample_object # => #<SampleClass:somehexvalue @a=5>
似乎是通过引用。这个真的不清楚。
【问题讨论】:
【参考方案1】: Ruby 中的所有 变量都是对对象的引用。您不能像在 C、C++ 或 Perl 中那样选择“按值传递”与“按引用传递”。 Ruby 实际上强制按值传递,否则没有其他选择。但是,发送的值始终是对对象的引用。这有点像使用 C 或 C++,其中所有成员变量都是指针,或者使用 Perl,您必须始终使用引用,即使使用简单的标量也是如此。
我认为正是这种变量与对象数据的分离让您感到困惑。
几点:
变量分配从不覆盖可能指向同一对象的其他变量。这几乎就是按值传递的定义。但是,这并不能满足您对对象内容也受到保护的期望。
实例变量和容器中的项目(例如在Array
s 和String
s 中)是单独的变量,如果您发送一个容器,您可以直接更改其内容,因为您发送了对容器的引用,其中包含 same 变量作为其内容。我想这就是你所说的“似乎是传递引用”的意思
一些类 - 包括那些代表数字的类和Symbol
- 是不可变的,即没有数字4
的就地更改方法。但从概念上讲,您仍在将单个对象 4
的引用传递给例程(在底层,为了提高效率,Ruby 会将值 4
简单地编码在变量的内存分配中,但这是一个实现细节 - 值在这种情况下也是“指针”)。
使用SampleModule
使接近 到您似乎正在寻找的“按值传递”语义的最简单方法是clone
例程开始处的参数.请注意,这实际上并不会导致 Ruby 更改调用语义,只是在这种情况下,您可以从方法外部获得您似乎想要的安全假设(方法内的参数发生的任何事情都保留在方法内):
module SampleModule
def self.testing(o)
o = o.clone
o.test
end
end
从技术上讲,这应该是一个通用的深层克隆,但并不需要让您的示例接近按值传递。您可以调用SampleModule.testing( any_var_or_expression )
并知道无论any_var_or_expression
在您的代码的其余部分中,关联的对象都不会被更改。
【讨论】:
也许你可以澄清o.clone
不是按值传递,而是创建一个复制克隆对象实例变量的新对象并返回它 ;这意味着新对象的实例变量引用了克隆对象的各个实例变量,这是与“按值传递”概念的关键区别
@ProgNOMmers - 是的,我想我的意思是使用“深度克隆”,而我的示例仅因为属性是数字而有效。使用深层克隆,原则上您更接近“按值传递”的行为(就像您对方法内的参数所做的任何事情对调用者都不重要),尽管实际上当您拥有深层引用树时,它不是首先是这样一个干净的概念。
这是我在 Ruby 中怀念的一件事:没有一种明确的方法可以对对象进行深度复制;在大多数情况下,您必须自己实现它。
Ruby 是按值传递的。无需“引用”它。总是。没有例外。没有如果。没有但是。如果你不相信我,你可以自己检查一下:def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#baz" # Ruby is pass-by-value
是的。一个重要的补充是 assignment 运算符 =
所做的。它是 Ruby 中为数不多的改变变量的东西之一。因此,当您添加foo = 'No, Ruby is pass-by-reference.'
时,您会创建一个新的String
对象并将foo
指向它——此时您有两个引用,每个引用指向一个单独的对象。 但是将该行更改为 foo.replace('No, Ruby is pass-by-reference.')
更改对象 - 而不是变量 - 你会得到与你的 OP 匹配的相反行为。【参考方案2】:
如果你真的想了解词汇,Ruby 通过值传递对(可变)对象的引用:
def pass_it(obj)
obj = 'by reference'
end
def mutate_it(obj)
obj << ' mutated'
end
str = 'by value'
pass_it(str)
mutate_it(str)
puts str # by value mutated
您可以使用dup
或clone
(请注意,两者都执行浅拷贝)和freeze
来解决可能由此产生的问题。
【讨论】:
【参考方案3】:Ruby 中的一切都是通过引用传递的:
class Test
attr_reader :a
def initialize(a)
@a = a
end
end
s = "foo"
o = Test.new(s)
o.a << "bar"
o.a #=> "foobar"
s #=> "foobar"
o.a.equal? s #=> true
在您的代码中,将对象传递给模块方法这一事实并没有改变任何东西; sample_object
已经是对新对象 SampleClass.new(2)
的引用
【讨论】:
-1。这完全是错误的。 Ruby 是按值传递的。总是。没有例外。没有如果。没有但是。如果你不相信我,你可以自己检查一下:def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#baz" # Ruby is pass-by-value
@JörgWMittag ,我开了个玩笑,但否决了答案不好...以上是关于通过引用传递或通过复制传递 - Ruby 模块 [重复]的主要内容,如果未能解决你的问题,请参考以下文章