2011-07-31 22 views
2

this问题我发现了一个关于Java中final变量范围的有趣细节。我不太了解Java,但我认为final与Ruby中的常量相同。Ruby中循环的常量和作用域

在C++中,这是可能的:

for(int i = 0; i < 5; ++i){ 
    const int c = i * 5; 
    std::cout << c << std::endl; 
} 

试图在循环过程中更改数值是不可能的,但并为您提供了一个编译时错误。

我很好奇,想看看红宝石将如何处理这个问题,开始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 

所以我的问题:是否有办法在循环开始时为每个循环迭代初始化一个常量而不创建警告消息?它可能有一些真实世界的用例,当我想根据迭代器变量进行计算,然后确保结果不会改变为剩余的循环。

没有你需要每一天,但我只是好奇。

回答

2

似乎并没有严格等同于Java的最终的红宝石。但是,你可以使用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 
4

总之,没有。但有几件事情你应该知道。正如你已经看到的,它们并不是一成不变的。他们可以被重新分配,只是一个警告。另外,您不能在方法内分配常量。

但还要多,他们不强加有关对象的任何他们持有:你可以发生变异没有问题的对象,即使没有一个警告:

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鼓励(但并不妨碍)重新分配,并冻结对象防止mutatio ñ。这些都不能帮助你,因为冻结数字毫无意义,因为它们已经是不变的了。

2

我不认为这是一个常数。毕竟,你使用这个名称来连续引用不同的值。一个真正的CONSTANT在整个程序的整个生命周期中都是不变的。至少,这是使用完全大写的名字的惯例。

在某些语言中,您可以确保(有时默认情况下)该值不会更改。 Ruby不是这样一种语言。即使真正的常数也可以改变。

你有什么只是一个局部变量,恰好在循环开始时计算一次。你应该给它一个普通的名字。如果你想确定它没有改变,你需要在编码时注意,编写测试来验证循环是否符合你的期望,并且可能让别人查看代码。

+0

我认为你是对的,它不适合Ruby哲学。虽然我认为它不只是一个局部变量。我认为,在强类型的情况下,这种'合约'的可靠性不能在一定范围内改变变量内容是合理的。 –