Параметры, передаваемые по имени как локальные переменные в Ruby

Я пишу постоянно, что я рассматриваю как ненужный код в Ruby при использовании параметров, передаваемых по имени для методов.

Возьмите, например, следующий код:

def my_method(args)
  orange = args[:orange]
  lemon = args[:lemon]
  grapefruit = args[:grapefruit]

  # code that uses 
  # orange, lemon & grapefruit in this format which is way prettier & concise than 
  # args[:orange] args[:lemon] args[:grapefruit]

  puts "my_method variables: #{orange}, #{lemon}, #{grapefruit}" 
end
my_method :orange => "Orange", :grapefruit => "Grapefruit"

То, что мне действительно не нравится приблизительно этот код, - то, что я должен взять args и передать значения в локальные переменные, идущие вразрез с принципами DRY и просто обычно занимающие место в моих методах. И если я не использую локальные переменные и просто обращаюсь ко всем переменным с args [: символ] синтаксис тогда код становится несколько неразборчивым.

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

def my_method_with_eval(args)
  method_binding = binding
  %w{ orange lemon grapefruit}.each { |variable| eval "#{variable} = args[:#{variable}]", method_binding; }

  # code that uses 
  # orange, lemon & grapefruit in this format which is way prettier & concise than 
  # args[:orange] args[:lemon] args[:grapefruit]

  puts "my_method_with_eval variables: #{orange}, #{lemon}, #{grapefruit}" 
end
my_method_with_eval :orange => "Orange", :grapefruit => "Grapefruit"

При выполнении того кода я просто добираюсь

NameError: undefined local variable or method ‘orange’ for main:Object method my_method_with_eval in named_args_to_local_vars at line at top level in named_args_to_local_vars at line 9

Любой получил любые идеи, как я мог упростить это вниз так или иначе так, чтобы я не запускал свои методы параметра, передаваемого по имени с загрузок var=args [: var] код?

Спасибо, Matthew O'Riordan

10
задан MatrixFrog 8 January 2010 в 00:18
поделиться

2 ответа

Я не верю, что есть какой-либо способ сделать это в Ruby (если кто-то придумает такой, сообщите мне, и я обновлю или удалю этот ответ, чтобы отразить его!) - если локальная переменная еще не определена, невозможно динамически определить ее с помощью привязки. Возможно, вы могли бы сделать что-то вроде апельсин, лимон, грейпфрут = nil перед вызовом eval, но вы можете столкнуться с другими проблемами - например, если args [: orange] - это строка «Апельсин», вы в конечном итоге оцените orange = Orange с вашей текущей реализацией.

Вот кое-что, что могло бы сработать, используя класс OpenStruct из стандартной библиотеки (под «может работать» я имею в виду «это зависит от вашего стиля, a.orange лучше, чем args [: orange] "):

require 'ostruct'

def my_method_with_ostruct(args)
  a = OpenStruct.new(args)
  puts "my_method_with_ostruct variables: #{a.orange}, #{a.lemon}, #{a.grapefruit}"
end

Если вам не нужен легкий доступ к какому-либо состоянию или методам на приемнике этого метода, вы можете использовать instance_eval следующим образом.

def my_method_with_instance_eval(args)
  OpenStruct.new(args).instance_eval do
    puts "my_method_with_instance_eval variables: #{orange}, #{lemon}, #{grapefruit}"
  end
end

Вы можете даже сделать что-нибудь хитрое с помощью method_missing (подробнее см. здесь ), чтобы разрешить доступ к «первичному» объекту, но производительность, вероятно, будет невысокой.

В целом, я думаю, что, вероятно, наиболее просто / удобно использовать менее СУХИЕ первоначальное решение, которое вас беспокоило.

6
ответ дан 3 December 2019 в 22:00
поделиться

Я нашел дискуссию по этому поводу на рубин-так-гугле и похоже, что это оптимизация парсера. Локальные переменные уже вычисляются во время выполнения, так что локальные_переменные уже установлены в начале метода.

def meth
  p local_variables
  a = 0
  p local_variables
end
meth
# =>
[:a]
[:a]

Таким образом, Ruby не нужно решать, является ли a методом или локальной переменной, или нет во время выполнения, но можно смело предположить, что это локальная переменная.

(Для сравнения: В Python localals() будет пустой в начале функции.)

.
3
ответ дан 3 December 2019 в 22:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: