如果在 Ruby 中一切都是对象,为啥这不起作用?

Posted

技术标签:

【中文标题】如果在 Ruby 中一切都是对象,为啥这不起作用?【英文标题】:Why doesn't this work if in Ruby everything is an Object?如果在 Ruby 中一切都是对象,为什么这不起作用? 【发布时间】:2011-12-03 01:20:02 【问题描述】:

考虑到在 Ruby 编程语言中,一切都被称为对象,我有把握地假设向方法传递参数是通过引用完成的。然而,下面这个小例子让我感到困惑:

$string = "String"

def changer(s)
  s = 1
end

changer($string)

puts $string.class
String
 => nil

如您所见,原始对象没有被修改,我想知道为什么,以及如何实现所需的行为,即。 获取方法以实际更改其参数引用的对象。

【问题讨论】:

我认为参数传递与面向对象没有任何关系。它与此正交。如果您想讨论它,inout 和 in 参数之间的区别,例如在 Corba 中更合适。 【参考方案1】:

Ruby 将值传递给函数,这些值是对对象的引用。在您的函数中,您将s 重新分配给另一个值,在本例中是对1 的引用。它不会修改原始对象。

你的方法没有改变传入的对象,你改变的是s所指的对象。

【讨论】:

"Ruby 没有任何传递值的概念。"在这里说:***.com/questions/1872110/…。我开始相信 Ruby 是按值传递的,你能说明如何从方法中完成引用对象的修改吗? @user766388:是的,Ruby 是按值传递。没有如果。没有但是。没有例外。如果您想知道 Ruby(或任何其他语言)是 pass-by-reference 还是 pass-by-value,请尝试一下:def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#baz"【参考方案2】:

因为$strings 都是对同一个对象的引用,即字符串“String”。但是,当您将s 分配给1 时,您不会更改对象“String”,而是使其引用一个新对象。

【讨论】:

好的,你所描述的听起来很像价值传递,我说的对吗?另一方面,我怎样才能使该方法真正修改原始对象(“String”)? @用户号按值传递将复制字符串。这里引用是重复的,所以它是按值传递引用... 你可以用破坏性操作修改字符串,但你永远不能把它变成另一种类型的对象。【参考方案3】:

Ruby 的工作方式是值传递和引用传递的结合。事实上,Ruby 使用带引用的值传递。

您可以在以下主题中阅读更多内容:

Pass by reference or pass by value Pass by reference?

一些值得注意的引语:

绝对正确:Ruby 使用按值传递 - 带引用。

irb(main):004:0> def foo(x) x = 10 end
=> nil
irb(main):005:0> def bar; x = 20; foo(x); x end
=> nil
irb(main):006:0> bar
=> 20
irb(main):007:0>

没有标准的方法(即除了涉及 eval 和 元编程魔术)使调用范围内的变量指向 另一个对象。而且,顺便说一句,这与 变量是指。 Ruby 中的即时对象与 其余的(例如,与 Java 中的 POD 不同)和来自 Ruby 语言角度你看不出任何区别(除了 性能也许)。这也是 Ruby 如此优雅的原因之一。

当你将一个参数传递给一个方法时,你传递的是一个 指向引用的变量。在某种程度上,它是一个组合 按值传递和按引用传递。我的意思是,你通过 方法中变量的值,但是 变量是始终对对象的引用。

两者的区别:

def my_method( a )
  a.gsub!( /foo/, 'ruby' )
end

str = 'foo is awesome'
my_method( str )            #=> 'ruby is awesome'
str                                    #=> 'ruby is awesome'

和:

def your_method( a )
  a = a.gsub( /foo/, 'ruby' )
end

str = 'foo is awesome'
my_method( str )            #=> 'ruby is awesome'
str                                    #=> 'foo is awesome'

是在#my_method 中,您正在调用#gsub!改变对象 (a) 到位。由于“str”变量(在方法范围之外)和 'a' 变量(在方法范围内)都有一个“值”,即 对同一对象的引用,反映对该对象的更改 在调用方法后的“str”变量中。在#your_method 中,您 调用#gsub,它 修改原始对象。取而代之的是 创建一个包含修改的 String 的新实例。什么时候 您将该对象分配给“a”变量,您正在更改值 'a' 是对该新 String 实例的引用。然而 “str”的值仍然包含对原始的引用(未修改) 字符串对象。

方法改变引用还是被引用对象取决于类类型和方法实现。

string = "hello"

def changer(str)
  str = "hi"
end

changer(string)
puts string
# => "hello"

string 不会更改,因为对字符串的赋值替换了引用,而不是引用的值。 我想在原地修改字符串,需要使用String#replace

string = "hello"

def changer(str)
  str.replace "hi"
end

changer(string)
puts string
# => "hi"

String 是一种常见情况,其中大部分操作适用于克隆,而不是 self 实例。 出于这个原因,几个方法都有一个 bang 版本,可以在适当的位置执行相同的操作。

str1 = "hello"
str2 = "hello"

str1.gsub("h", "H")
str2.gsub!("h", "H")

puts str1
# => "hello"
puts str2
# => "Hello"

最后,要回答您最初的问题,您不能更改字符串。您只能为其分配新值或将字符串包装到不同的可变对象中并替换内部引用。

$wrapper = Struct.new(:string).new
$wrapper.string = "String"

def changer(w)
  w.string = 1
end

changer($wrapper)

puts $wrapper.string
# => 1

【讨论】:

很好的解释。长但绝对非常值得。感谢您提供包装器的代码示例,因为您已经提供了它,我将奖励您的作为已接受的答案。干得好,非常感谢西蒙妮。很棒的一课。 绝对没有传值和传引用的组合,Ruby only 使用传值(就像 Java、C 等) .术语“按引用传递”与对象引用无关。另见this SO question【参考方案4】:

赋值不会将值绑定到对象,它会将对象引用绑定到标识符。参数传递的工作方式相同。

当你进入函数体时,世界是这样的:

 +---+                  +----------+
 | s |----------------->| "String" |
 +---+                  +----------+
                              ^
 +-------+                    |
 |$string|--------------------+
 +-------+

代码

 s = 1

让世界看起来像

 +---+       +---+      +----------+
 | s |------>| 1 |      | "String" |
 +---+       +---+      +----------+
                              ^
 +-------+                    |
 |$string|--------------------+
 +-------+

赋值语法操作的是变量,而不是对象。

与许多类似的语言(Java、C#、Python)一样,ruby 是按值传递的,其中值最常被引用。

要操作字符串对象,可以对字符串使用方法,例如s.upcase!。当它操纵对象本身时,这种事情将反映在方法之外。

【讨论】:

谢谢。视觉辅助总是很棒,我想我现在明白了这部分。但是为了论证......我怎样才能让该方法修改引用的对象(“String”)?【参考方案5】:

实际上,大多数托管编程语言,如 java 、 c#... almsot 都不是通过引用传递...

它们都按值传递引用...这意味着它们会创建另一个指向同一个对象的引用...为其分配新值不会改变原始引用的值...只是什么指向...

此外,字符串在大多数语言中是不可变的,这意味着您在创建后无法更改值..它们必须重新创建为新的...所以您永远不会看到实际字符串中的任何更改...

【讨论】:

关于如何修改引用的对象,在大多数托管编程语言中,您无法修改字符串...您可以通过将其包装在类、列表、映射等其他东西中来修改它。任何东西,以便您更改被引用对象内属性的值而不是引用本身 我认为在这里谈论字符串不变性是不合适的,主要是因为我尝试对自己的类做同样的事情,即。在方法中为“Dog”对象更改“Cat”对象,结果是一样的(方法不会更改它)。 我的意思是改变 cat 对象内部的东西......不是完全改变 cat 到别的东西......由于上述原因,这不起作用......如果你想通过 cat到一个函数并将其更改为 dog.. 你将不得不将它包装在其他对象中。 我对字符串不变性的意思是,在大多数语言中,直接更改字符串总是会创建一个新字符串,例如,您不能就地修改第二个字符的值。您必须使用新值创建一个新字符串...我这么说是因为您问如何更改实际字符串...所以答案是字符串不会改变。 实际上字符串在 Ruby 中是可变的

以上是关于如果在 Ruby 中一切都是对象,为啥这不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

外部、内部和没有联系或者为啥这不起作用?

如何做一个简单的缩放动画,为啥这不起作用?

为啥 to_proc 在 Ruby 改进中不起作用?

为啥有时这不起作用?

为啥这不起作用?爪哇

Windows 上的 Python 管道:为啥这不起作用?