【问题标题】:Constant Assignment Bug in Ruby?Ruby 中的常量赋值错误?
【发布时间】:2010-06-09 23:47:32
【问题描述】:

我们在 Ruby 中发现了一些看起来很奇怪的代码,我想知道是否有人可以解释一下:

$ irb
irb(main):001:0> APPLE = 'aaa'
=> "aaa"
irb(main):002:0> banana = APPLE
=> "aaa"
irb(main):003:0> banana << 'bbb'
=> "aaabbb"
irb(main):004:0> banana
=> "aaabbb"
irb(main):005:0> APPLE
=> "aaabbb"

明白了吗?常量被附加到局部变量的同时。

已知行为?预期?

【问题讨论】:

标签: ruby


【解决方案1】:

已知行为。常量并不意味着你不能修改它所引用的对象,只是如果你将它分配给不同的对象,它会给你一个警告(而且只是一个警告)。

简而言之,ruby 常量不是。

注意:此行为列在 answer 中,以“应警告新手注意哪些 Ruby 陷阱?”值得一读。

【讨论】:

  • 它们基本上是恒定的引用。 Java 的工作方式基本相同。
  • @Benjamin Oakes:是的,完全正确。不仅是 Java,而且基本上是地球上的任何编程语言。尽管在 Ruby 中,它们甚至不是那么恒定:重新分配它们只会产生警告,而不是错误。
  • @Jörg W Mittag:非常正确。如果有人知道在重新分配常量时强制异常的方法,我会非常感兴趣。不过,冻结对象的功能非常有用。
  • 我在上一条评论中提出了一个问题:stackoverflow.com/questions/3023617/…
【解决方案2】:

明白了吗?常量被附加到局部变量的同时。

不,它没有附加到,局部变量也没有。

常量和局部变量都引用的单个对象被附加到,但是常量和局部变量都没有改变。您不能在 Ruby 中修改或更改变量或常量(至少不是以您的问题暗示的方式),您可以唯一更改的是对象。

可以对变量或常量做的唯一两件事是取消引用并分配给它们。

这里的常数是一个红鲱鱼,它与给出的例子完全无关。唯一相关的是整个示例中只有 一个 单个对象。可以使用两个不同的名称访问该单个对象。如果对象改变了,那么对象就改变了。时期。它并没有神秘地将自己一分为二。您使用哪个名称来查看更改的对象并不重要。反正只有一个对象。

这与在任何其他编程语言中的工作方式完全相同:如果您在 Python、Java、C#、C++、C、Lisp、Smalltalk、JavaScript、PHP、Perl 或其他语言中对可变对象有多个引用,那么无论使用什么引用,对该对象的任何更改都将是可见的,即使其中一些引用是 finalconst 或该特定语言所称的任何内容。

这就是共享可变状态的工作原理,也是共享可变状态不好的众多原因之一。

在 Ruby 中,您通常可以对任何对象调用 freeze 方法以使其不可变。然而,再一次,您在这里修改了 object,所以任何其他引用该对象的人都会突然发现该对象已变为不可变。因此,为了安全起见,您需要首先通过调用dup 来复制对象。但是当然,这也是不够的,如果你想到一个数组,例如:如果你dup这个数组,你会得到一个不同的array,但是里面的对象 该数组与原始数组中的数组相同。如果你freeze数组,那么你就不能再修改数组,但是数组in中的对象很可能仍然是可变的:

ORIG = ['Hello']
CLONE = ORIG.dup.freeze
CLONE[0] << ', World!'
CLONE # => ['Hello, World!']

这对你来说是共享的可变状态。摆脱这种疯狂的唯一方法是放弃共享状态(例如 Actor 编程:如果没有其他人可以看到它,那么它改变的频率或时间并不重要)或可变状态(即函数式编程:如果它永远不会改变,那么有多少人看到它并不重要)。

原始示例中的两个变量之一实际上是常数这一事实与问题完全无关。 Ruby 中的变量和常量有两个主要区别:它们有不同的查找规则,如果常量被多次分配,它们会产生警告。但是在这个例子中,查找规则是无关紧要的,并且常量只分配了一次,所以在这种情况下,变量和常量之间确实没有区别。

【讨论】:

  • 你可以在 ruby​​ 中尊重变量/常量,还是只能将它们分配给 nil?
  • 另外,如果一个对象被创建并且它没有被分配给任何东西,它是否存在直到它被垃圾收集?
【解决方案3】:

如果您希望常量不可更改,您可以冻结它们:

>> APPLE = 'aaa'
=> "aaa"
>> banana = APPLE
=> "aaa"
>> APPLE.freeze
=> "aaa"
>> banana.frozen?
=> true
>> banana << 'bbb'
TypeError: can't modify frozen string
    from (irb):5:in `<<'
    from (irb):5

【讨论】:

  • 不,你不能在 Ruby 中冻结常量,只能冻结对象。
  • @Benjamin:我认为 Jörg 的意思是你只冻结常量引用的对象,而不是常量本身。
  • @Andrew:啊,我误解了 Jörg 写的内容。你说的对。
【解决方案4】:

Ruby 中的常量不是“常量”。您也可以使用任何其他名称;除非您尝试更改指针的地址,否则将它们全部大写不会改变任何关于解释器的对象。

如果你这样看,行为是显而易见的和必要的; Apple 是一个指向字符串对象的指针,banana 也是如此。然后编辑香蕉指向的对象。 Apple 指向同一个对象,因此更改也反映在此处。

【讨论】:

  • 访问常量的规则与访问变量的规则不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-11
  • 2015-09-19
  • 1970-01-01
  • 2020-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多