Идиоматический ruby ​​для временных переменных внутри метода

В методе я использую i и j как временные переменные при вычислении других переменных. Какой идиоматический способ избавиться от i и j, когда они больше не нужны? Должен ли я использовать блоки для этой цели?

i = positions.first
while nucleotide_at_position(i-1) == nucleotide_at_position(i)
  raise "Assumption violated" if i == 1
  i -= 1
end
first_nucleotide_position = i
j = positions.last
while nucleotide_at_position(j+1) == nucleotide_at_position(j)
  raise "Assumption violated" if j == sequence.length
  j += 1
end
last_nucleotide_position = j

Предыстория: я хотел бы избавиться от i и j, когда они больше не нужны, чтобы они не использовались каким-либо другим кодом в методе. Дает моему коду меньше шансов ошибиться. Я не знаю название концепции - это "инкапсуляция"? Самые близкие понятия, которые я могу придумать, это (предупреждение: ссылки на TV Tropes - не посещать во время работы) Пистолет Чехова или YouHaveOutlivedYourUsefulness.

Другой альтернативой может быть размещение кода в их собственных методах, но это может ухудшить читабельность.


person Andrew Grimm    schedule 20.05.2010    source источник


Ответы (5)


Ruby (как и JS) не создает новую область для каждого блока по умолчанию (как это делают C++ и т. д.). Однако в Ruby 1.9 вы можете попробовать:

last_nucleotide_position = nil
proc { |;i, j|
  i = positions.first
  while nucleotide_at_position(i-1) == nucleotide_at_position(i)
    raise "Assumption violated" if i == 1
    i -= 1
  end
  first_nucleotide_position = i
  j = positions.last
  while nucleotide_at_position(j+1) == nucleotide_at_position(j)
    raise "Assumption violated" if j == sequence.length
    j += 1
  end
  last_nucleotide_position = j
}.call()

См. Как сделать локальные переменные блока по умолчанию в ruby ​​1.9?. Любые переменные, которые вы хотите использовать вне блока, должны быть определены заранее (например, last_нуклеотид_позиция).

FM прав в том, что отдельный метод может быть более читабельным.

person Matthew Flaschen    schedule 20.05.2010

Что заставляет вас думать, что разделение кода на несколько методов повредит читабельности? По моему опыту, разделение даже небольших или средних фрагментов кода на несколько методов может значительно улучшить читабельность.

person dbyrne    schedule 20.05.2010

Я думаю, что термин, который вы ищете, - это переменная область действия - другими словами, вы ищете способы ограничить область действия i и j. Но вам не нужно беспокоиться об этом. Настоящая проблема требует создания отдельных методов — независимо от соображений области действия.

Это улучшит читабельность, потому что позволит читателю вникать в код, начиная с высокого уровня, а затем углубляться только по мере необходимости. Это также улучшит тестируемость, потому что ваши небольшие методы будут делать ровно одну вещь.

def calc_first_nucleotide_position(po)
  i = po.first
  while nucleotide_at_position(i-1) == nucleotide_at_position(i)
    raise "Assumption violated" if i == 1
    i -= 1
  end
  i
end

# etc...

first_nucleotide_position = calc_first_nucleotide_position(positions)
last_nucleotide_position  = calc_last_nucleotide_position(positions)

# etc...
person FMc    schedule 20.05.2010

Вы ищете Ruby-эквивалент специального оператора Lisp let. Ruby не поддерживает его из коробки, но вы можете очень легко взломать его, и в результате получится такой синтаксис:

x = 10
scope { |x|
    x = 30
}
puts x #=> 10

см.: http://banisterfiend.wordpress.com/2010/01/07/controlling-object-scope-in-ruby-1-9/

person horseyguy    schedule 20.05.2010

Если все, что вам нужно, это предотвратить распространение новых переменных на остальную часть вашей программы, вы можете заключить свой код в блок, используя 1.times. Любые новые переменные, которые вы создаете внутри блока, будут уничтожены, когда вы закроете блок. Просто имейте в виду, что любые изменения, которые вы вносите в уже существующие переменные, останутся после закрытия блока.

y = 20
1.times do
  # put your code in here
  i = 1
  puts x = y # => 20, because y is available from outside the block
  y = 'new value' # We can change the value of y but our changes will 
    # propagate to outside the block since y was defined before we opened
    # the block.
end

defined? i # => nil, i is lost when you close the block
defined? x # => nil, x is also local to the block
puts y # => 'new value'
person morri    schedule 13.11.2012