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

考虑到在Ruby编程语言中,所有东西都被认为是一个Object,我可以放心地认为将参数传递给方法是通过引用完成的。 然而,下面这个小例子使我感到困惑:

$string = "String"

def changer(s)
  s = 1
end

changer($string)

puts $string.class
String
 => nil

正如你可以看到原始的Object没有被修改,我想知道为什么 ,以及我如何完成所需的行为,即。 让方法实际更改由其参数引用的对象。


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如此优雅的原因之一。

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

    和...之间的不同:

    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,它不会修改原始对象。 相反,它会创建一个包含修改的字符串的新实例。 当您将该对象分配给'a'变量时,您将更改'a'的值作为对该新String实例的引用。 但是,'str'的值仍然包含对原始(未修改的)字符串对象的引用。

    方法是否更改引用或引用的对象取决于类的类型和方法的实现。

    string = "hello"
    
    def changer(str)
      str = "hi"
    end
    
    changer(string)
    puts string
    # => "hello"
    

    string不会更改,因为string上的分配将替换引用,而不是引用的值。 我想修改字符串到位,你需要用String#replace

    string = "hello"
    
    def changer(str)
      str.replace "hi"
    end
    
    changer(string)
    puts string
    # => "hi"
    

    字符串是一种常见的情况,其中大部分操作在克隆上工作,而不在自身实例上工作。 出于这个原因,有几种方法具有执行相同操作的爆炸版本。

    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! 。 这种事情会在方法之外反映,因为它操纵对象本身。


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

    链接地址: http://www.djcxy.com/p/20655.html

    上一篇: Why doesn't this work if in Ruby everything is an Object?

    下一篇: c++