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

发布于 2024-12-08 16:47:46 字数 306 浏览 0 评论 0原文

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

$string = "String"

def changer(s)
  s = 1
end

changer($string)

puts $string.class
String
 => nil

正如你所看到的原始对象没有被修改,我想知道为什么,而且,我怎样才能完成所需的行为,即。 获取实际更改其参数引用的对象的方法。

Considering that in the Ruby programming language everything is said to be an Object, I safely assumed that passing arguments to methods are done by reference. However this little example below puzzles me:

$string = "String"

def changer(s)
  s = 1
end

changer($string)

puts $string.class
String
 => nil

As you can see the original Object wasn't modified, I wish to know why, and also, how could I accomplish the desired behavior ie. Getting the method to actually change the object referenced by its argument.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

冷清清 2024-12-15 16:47:46

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

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

一些值得注意的引用:

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

irb(主):004:0> def foo(x) x = 10 结束
=>零
irb(主):005:0>定义栏; x = 20; foo(x); x 结束
=>零
irb(主):006:0>酒吧
=> 20
irb(主):007:0>

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

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

两者的区别:

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

str = 'foo 太棒了'
my_method( str ) #=> “红宝石太棒了”
字符串#=> “红宝石太棒了”

和:

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

str = 'foo 太棒了'
my_method( str ) #=> “红宝石太棒了”
字符串#=> 'foo 太棒了'

在#my_method 中,您正在调用#gsub!这改变了对象
(一)到位。由于 '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"

字符串是一种常见情况,其中大部分操作都在克隆上进行,而不是在自身实例上进行。
因此,有几种方法都有一个 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

The way Ruby works is a combination of pass by value and pass by reference. In fact, Ruby uses pass by value with references.

You can read more in the following threads:

Some notable quotes:

Absolutely right: Ruby uses pass by value - with references.

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>

There is no standard way (i.e. other than involving eval and
metaprogramming magic) to make a variable in a calling scope point to
another object. And, btw, this is independent of the object that the
variable refers to. Immediate objects in Ruby seamlessly integrate with
the rest (different like POD's in Java for example) and from a Ruby
language perspective you don't see any difference (other than
performance maybe). This is one of the reasons why Ruby is so elegant.

and

When you pass an argument into a method, you are passing a
variable that points to a reference. In a way, it's a combination of
pass by value and pass by reference. What I mean is, you pass the
value of the variable in to the method, however the value of the
variable is always a reference to an object.

The difference between:

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

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

and:

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

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

is that in #my_method, you are calling #gsub! which changes the object
(a) in place. Since the 'str' variable (outside the method scope) and
the 'a' variable (inside the method scope) both have a "value" that is
a reference to the same object, the change to that object is reflected
in the 'str' variable after the method is called. In #your_method, you
call #gsub which does not modify the original object. Instead it
creates a new instance of String that contains the modifications. When
you assign that object to the 'a' variable, you are changing the value
of 'a' to be a reference to that new String instance. However, the
value of 'str' still contains a reference to the original (unmodified)
string object.

Whether a method changes the reference or the referenced object depends on the class type and method implementation.

string = "hello"

def changer(str)
  str = "hi"
end

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

string is not changed because the assignment on strings replaces the reference, not the referenced value.
I you want to modify the string in place, you need to use String#replace.

string = "hello"

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

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

String is a common case where the most part of operations works on clones, not on the self instance.
For this reason, several methods have a bang version that executes the same operation in place.

str1 = "hello"
str2 = "hello"

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

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

Finally, to answer your original question, you cannot change a String. You can only assign a new value to it or wrap the string into a different mutable object and replace the internal reference.

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

def changer(w)
  w.string = 1
end

changer($wrapper)

puts $wrapper.string
# => 1
寂寞清仓 2024-12-15 16:47:46

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

当您进入函数体时,世界看起来像这样:

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

代码

 s = 1

使世界看起来像

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

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

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

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

Assignment does not bind values to objects, it binds object references to identifiers. Argument passing works the same way.

When you enter the body of the function, the world looks like this:

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

The code

 s = 1

makes the world look like

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

The assignment syntax manipulates variables, not objects.

Like many similar languages (Java, C#, Python) ruby is pass-by-value, where the values are most often references.

To manipulate the string object, you can use a method on the string, such as s.upcase!. This sort of thing will be reflected outside of the method as it manipulate the object itself.

终止放荡 2024-12-15 16:47:46

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

Because both $string and s are references to the same object, the string "String". However, when you assign s to 1, you don't change the object "String", you make it reference a new object.

霊感 2024-12-15 16:47:46

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

您的方法不会更改传入的对象,您正在更改 s 引用的内容。

Ruby passes values around to functions, and these values are references to objects. In your function you are reassigning s to another value, in this case a reference to 1. It does not modify the original object.

Your method isn't changing the object passed in, you're changing what s refers to.

落墨 2024-12-15 16:47:46

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

它们都按值传递引用...这意味着它们创建另一个指向同一对象的引用...分配它具有新值不会更改原始引用的值...只是 s 指向的内容...

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

Actually, most managed programming languages like java , c#... almsot all do not pass by reference...

They all pass the reference by value ... that means they make another reference that point to the same object ... assigning it with a new value won't change the value of the original reference ... just what s points to ...

Besides, strings are immutable in most langs, meaning that you cannot change the value after being created.. they have to be recreated as new ones... so you will never see any change in the actual string ...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文