提问人:jlstr 提问时间:10/7/2011 最后编辑:ROMANIA_engineerjlstr 更新时间:10/17/2017 访问量:10969
如果在 Ruby 中一切都是 Object,为什么这不起作用?
Why doesn't this work if in Ruby everything is an Object?
问:
考虑到在 Ruby 编程语言中,一切都被称为对象,我安全地假设将参数传递给方法是通过引用完成的。然而,下面的这个小例子让我感到困惑:
$string = "String"
def changer(s)
s = 1
end
changer($string)
puts $string.class
String
=> nil
正如你所看到的,原始对象没有被修改,我想知道为什么,以及我怎样才能完成所需的行为,即。获取方法以实际更改其参数引用的对象。
答:
Ruby 将值传递给函数,这些值是对对象的引用。在函数中,您将重新分配给另一个值,在本例中是对 的引用。它不会修改原始对象。s
1
你的方法不是在改变传入的对象,而是在改变所指的内容。s
评论
def foo(bar) bar = 'reference' end; baz = 'value'; foo(baz); puts "Ruby is pass-by-#{baz}"
因为两者都是对同一对象的引用,所以字符串“String”。但是,当您赋值给 时,您不会更改对象“String”,而是让它引用一个新对象。$string
s
s
1
评论
Ruby 的工作方式是按值传递和按引用传递的组合。事实上,Ruby 使用带有引用的传递值。
您可以在以下线程中阅读更多内容:
一些值得注意的引述:
绝对正确: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 如此优雅的原因之一。
和
当您将参数传递到方法中时,您正在传递一个 指向引用的变量。在某种程度上,它是 按值传递和按引用传递。我的意思是,你通过 变量的值,但是 variable 始终是对对象的引用。
两者之间的区别:
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 对象。
方法是更改引用还是引用对象取决于类类型和方法实现。
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"
字符串是一种常见情况,其中大部分操作都适用于克隆,而不是在自身实例上。 出于这个原因,一些方法都有一个 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
评论
赋值不会将值绑定到对象,而是将对象引用绑定到标识符。参数传递的工作方式相同。
当您进入函数的主体时,世界如下所示:
+---+ +----------+
| s |----------------->| "String" |
+---+ +----------+
^
+-------+ |
|$string|--------------------+
+-------+
代码
s = 1
让世界看起来像
+---+ +---+ +----------+
| s |------>| 1 | | "String" |
+---+ +---+ +----------+
^
+-------+ |
|$string|--------------------+
+-------+
赋值语法操作变量,而不是对象。
像许多类似的语言(Java、C#、Python)一样,ruby 是按值传递的,其中值通常是引用。
若要操作字符串对象,可以在字符串上使用方法,例如 .这种事情将反映在方法之外,因为它操纵对象本身。s.upcase!
评论
实际上,大多数托管编程语言,如 java、c#......几乎所有的都不通过引用......
它们都通过值传递引用......这意味着他们做了另一个指向同一对象的引用......为它分配一个新值不会改变原始引用的值......只是 s 指向......
此外,字符串在大多数语言中是不可变的,这意味着您在创建后无法更改值。它们必须被重新创建为新的......因此,您永远不会看到实际字符串的任何变化......
评论