Подтвердить что ты не робот

Рубины превращают значения внутри хеша в локальные переменные

Скажем, у меня есть этот хеш:

entry = {"director"=>"Chris Nolan", "prducer"=>"Sum Duk", "writer"=>"Saad Bakk"}

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

director = "Chris Nolan"
producer = "Sum Duk"
...

Используя цикл, а не:

director = entry["director"]

Поскольку существует множество значений, и я не хочу делать их индивидуально.

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

entry.each_pair { |k, v| instance_variable_set("@#{k}", v) }

Есть ли решение? Или, если это не так, способ превратить переменную экземпляра в локальную и удалить экземпляр без него один за другим?

4b9b3361

Ответ 1

Вы не можете создавать локальные переменные из-за переменной scope.
Если вы создадите локальную переменную внутри блока, переменная будет доступна только внутри самого блока.
Пожалуйста, обратитесь к этому вопросу для получения дополнительной информации.
Динамически задавать локальные переменные в Ruby

Ответ 2

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

Например, это будет работать:

vals = {
  "foo" => "bar",
  "baz" => "qux"
}

eval <<-EOF
  #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
  puts foo
EOF

Однако это не будет:

vals = {
  "foo" => "bar",
  "baz" => "qux"
}

eval <<-EOF
  #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
EOF

puts foo

как foo выходит за пределы области действия в конце eval. Однако, если вся ваша работа над переменными может быть выполнена внутри области eval, это вполне выполнимо.

Ответ 3

Вариант 1

Я не могу рекомендовать это, кроме забавы, но в основном это действует после:

entry.each |k, v|
  singleton_class.send(:attr_accessor, k)
  send("#{k}=", v)
end

director                        # => "Chris Nolan"
self.director = "Wes Anderson"  # Unfortunately, must use self for assignment
director                        # => "Wes Anderson"

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

Чтобы сделать их более "локальными", вы можете использовать singleton_class.remove_method, чтобы удалить эти методы, когда закончите с ними. Вы могли бы даже попытаться выполнить псевдоним любых существующих одноуровневых методов класса с тем же именем и впоследствии восстановить их.

Вариант 2

Это то, что я использую в реальном коде. Для этого требуется массив ActiveSupport, который поставляется с Ruby On Rails, но также может использоваться сам по себе.

director, producer, writer = entry.values_at('director', 'producer', 'writer')

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

director, producer, writer = entry.values  # Not so good, IMO.

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

Примечание

Если ваша цель - просто сократить ввод текста, вот подход, для которого требуется только два символа для доступа к переменной, чем если бы они были истинными локальными переменными:

e = OpenStruct.new(entry)
e.director     # => "Chris Nolan"