在this问题中,我发现了一个关于Java中final
变量范围的有趣细节。我不太了解Java,但我认为final
与Ruby中的常量相同。
在C ++中,这是可能的:
for(int i = 0; i < 5; ++i){
const int c = i * 5;
std::cout << c << std::endl;
}
虽然无法在循环期间尝试更改值,但会出现编译时错误。
我很想知道Ruby会如何处理这个问题,启动了irb并编写了这段代码来测试它:
5.times do |x|
XPI = x * Math::PI
puts x
end
结果是
0.0
(irb):27: warning: already initialized constant XPI
3.141592653589793
(irb):27: warning: already initialized constant XPI
6.283185307179586
(irb):27: warning: already initialized constant XPI
9.42477796076938
(irb):27: warning: already initialized constant XPI
12.566370614359172
=> 5
所以我的问题:有没有办法在循环开始时为每个循环迭代初始化而不创建警告消息?它可能有一些真实世界的用例,当我想根据迭代器变量进行计算然后确保时,结果不会因剩余的循环而改变。
你每天都不需要,但我只是好奇。
答案 0 :(得分:4)
简而言之,没有。但是你应该注意几件事情。
正如您所见, CONSTS
并非不变。他们可以重新分配,只需要一个警告。此外,您无法在方法中分配常量。
但更重要的是,他们并没有强加任何关于他们持有的对象:即使没有警告,你也可以突变对象没有问题:
STRING_CONST = 'foo'
#=>'foo'
STRING_CONST << 'bar'
#=>'foobar'
但是,有Object#freeze
方法会在您尝试更改冻结对象时导致异常上升:
CONST_STRING = 'foo'
#=> "foo"
CONST_STRING.freeze
CONST_STRING << 'bar'
RuntimeError: can't modify frozen string
from (irb):12
from /usr/bin/irb:12:in `<main>'
因此,简而言之,使用CONSTANTS
阻止(但不会阻止)重新分配,冻结对象可以防止变异。这些都不能帮助你,因为冻结数字没有意义,因为它们已经是不可变的。
答案 1 :(得分:2)
似乎没有严格的等同于Java的ruby中的最终版本。但是,您可以使用remove_const
(这是Module
中的私有方法)来消除循环结束时的常量(和警告):
5.times do |x|
XPI = x * Math::PI;
puts x;
Object.instance_eval{ remove_const :XPI };
# this would work, too: Object.send(:remove_const, :XPI);
end
答案 2 :(得分:2)
我不认为常数是适合的。毕竟,您使用该名称连续引用不同的值。真正的CONSTANT在程序的整个生命周期中都是恒定的。至少,这是使用完全大写名称的惯例。
在某些语言中,您可以确保(有时默认情况下)该值不会更改。 Ruby不是这样的语言。即使是真正的常数也可以改变。
你所拥有的只是一个在循环开始时计算一次的局部变量。你应该给它一个普通的名字。如果你想确保它没有改变,你需要在编码时注意,编写测试以验证循环是否符合你的预期,并且可能让某人审查代码。