Константы и область видимости в циклах с Ruby - PullRequest
2 голосов
/ 31 июля 2011

В этом вопросе я нашел интересную деталь о области действия переменной final в Java. Я недостаточно хорошо знаю 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 ]

4 голосов
/ 31 июля 2011

Короче, нет. Но есть пара вещей, о которых вы должны знать.

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 препятствует (но не предотвращает) переназначению, а замораживание объектов предотвращает мутацию. Ничто из этого не может вам помочь, поскольку замораживание чисел не имеет смысла, поскольку они уже неизменны.

2 голосов
/ 31 июля 2011

Я не думаю, что константа подходит для этого. В конце концов, вы используете имя для ссылки на различные значения подряд. Истинная КОНСТАНТА будет постоянной на протяжении всей жизни программы. По крайней мере, это соглашение об использовании полностью заглавных букв.

В некоторых языках вы можете гарантировать, иногда по умолчанию, что значение не изменится. Руби не такой язык. Даже истинные константы могут быть изменены.

То, что у вас есть, это просто локальная переменная, которая рассчитывается один раз в начале цикла. Вы должны дать ему обычное имя. Если вы хотите быть уверенным, что это не изменится, вам следует уделить внимание при кодировании, написать тесты, чтобы убедиться, что цикл выполняет то, что вы ожидаете, и, возможно, попросить кого-нибудь просмотреть код.

2 голосов
/ 31 июля 2011

Кажется, что нет строгого эквивалента финалу 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
...