循环中的常量和范围与Ruby

时间:2011-07-31 12:08:01

标签: ruby scope constants

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

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

你每天都不需要,但我只是好奇。

3 个答案:

答案 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不是这样的语言。即使是真正的常数也可以改变。

你所拥有的只是一个在循环开始时计算一次的局部变量。你应该给它一个普通的名字。如果你想确保它没有改变,你需要在编码时注意,编写测试以验证循环是否符合你的预期,并且可能让某人审查代码。