Ruby中的常量分配错误?

时间:2010-06-09 23:47:33

标签: ruby

我们在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"

抓住那个?常量被附加到局部变量的同时。

已知行为?预期?

4 个答案:

答案 0 :(得分:5)

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

简而言之,红宝石常数不是。

注意answer中列出了这种行为“应该警告新手应该注意哪些Ruby问题?”这是值得一读的。

答案 1 :(得分:4)

  

抓住那个?常量被附加到局部变量的同时。

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

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

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

这里的常数是红鲱鱼,它与给出的例子完全无关。唯一相关的是整个示例中只有一个单个对象。可以使用两个不同的名称访问该单个对象。如果对象发生更改,则对象会更改。期。它并没有神秘地将自己分成两部分。您用来查看已更改对象的名称无关紧要。无论如何只有一个对象。

这与任何其他编程语言完全相同:如果你有多个对可变对象的引用,比如Python,Java,C#,C ++,C,Lisp,Smalltalk,JavaScript,PHP,Perl等等,然后,无论使用什么引用,对该对象的任何更改都是可见的,即使其中一些引用是finalconst或该特定语言所称的任何引用。

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

在Ruby中,通常可以在任何对象上调用freeze方法使其不可变。但是,再次,您在这里修改对象,因此任何具有该对象引用的人都会突然发现该对象已变为不可变。因此,为了安全起见,您需要先调用dup来复制对象。但是,当然,这还不够,如果你想到一个数组,例如:如果你dup数组,你得到一个不同的数组,但是里面的对象< / em>数组在原始数组中仍然是相同的。如果您freeze数组,则无法再修改数组,但数组中的对象仍可能是可变的:

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

这是你共享的可变状态。逃避这种疯狂的唯一方法就是放弃共享状态(例如,演员编程:如果没有其他人能看到它,那么无论多久或何时改变都无关紧要)或可变状态(即功能编程:如果它永远不会变化,然后有多少人看到它并不重要。

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

答案 2 :(得分:0)

如果希望它们不可变,可以冻结常量:

>> 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

答案 3 :(得分:0)

Ruby中的常量不是“常量”。你不妨使用任何其他名字;除非你试图更改指针的地址,否则将它们放在所有大写字母中并不会改变任何关于对象的解释器。

如果以这种方式看待,行为是显而易见的,必要的; Apple是指向字符串对象的指针,香蕉也是如此。然后编辑banana指向的对象。 Apple指向同一个对象,因此也会反映出这种变化。