【问题标题】:Pass by reference or pass by copy - Ruby Modules [duplicate]通过引用传递或通过复制传递 - Ruby 模块 [重复]
【发布时间】: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>

似乎是通过引用。这个真的不清楚。

【问题讨论】:

    标签: ruby


    【解决方案1】:
    Ruby 中的

    所有 变量都是对对象的引用。您不能像在 C、C++ 或 Perl 中那样选择“按值传递”与“按引用传递”。 Ruby 实际上强制按值传递,否则没有其他选择。但是,发送的值始终是对对象的引用。这有点像使用 C 或 C++,其中所有成员变量都是指针,或者使用 Perl,您必须始终使用引用,即使使用简单的标量也是如此。

    我认为正是这种变量与对象数据的分离让您感到困惑。

    几点:

    • 变量分配从不覆盖可能指向同一对象的其他变量。这几乎就是按值传递的定义。但是,这并不能满足您对对象内容也受到保护的期望。

    • 实例变量和容器中的项目(例如在Arrays 和Strings 中)是单独的变量,如果您发送一个容器,您可以直接更改其内容,因为您发送了对容器的引用,其中包含 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
    

    您可以使用dupclone(请注意,两者都执行浅拷贝)和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 ,我开了个玩笑,但否决了答案不好...
      猜你喜欢
      • 1970-01-01
      • 2011-04-07
      • 2018-02-11
      • 2017-08-12
      • 1970-01-01
      • 2013-09-10
      • 2012-06-03
      • 2019-09-01
      • 1970-01-01
      相关资源
      最近更新 更多