23

假设我有这个哈希:

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) }

有解决办法吗?或者失败了,一种将实例变量转换为本地变量并删除实例变量而不一个一个地执行它的方法?

4

4 回答 4

8

选项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"

它不是创建局部变量,而是在当前对象的单例类上定义访问器方法,您可以像调用局部变量一样调用它们。

为了使它们更“本地化”,您可以在使用singleton_class.remove_method完这些方法后删除它们。您甚至可以尝试使用相同名称为任何现有的单例类方法起别名,然后再恢复它们。

选项 2

这是我在实际代码中使用的东西。它需要 Ruby On Rails 附带的 ActiveSupport gem,但也可以单独使用。

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"
于 2015-08-14T15:49:10.280 回答
4

由于变量范围,您无法创建局部变量。
如果您在块内创建局部变量,则该变量只能在块本身内访问。
请参阅此问题以获取更多信息。
在 Ruby 中动态设置局部变量

于 2012-11-14T15:59:38.977 回答
4

您可以使用 来执行此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

asfoo在评估结束时超出范围。但是,如果您对变量的所有工作都可以在 eval 的范围内完成,那是完全可行的。

于 2012-11-14T16:10:58.507 回答
0

扩展@antinome 的注释:如果您希望局部变量只存在,因为您想动态访问它们,例如通过在字符串中插入它们,那么OpenStruct+instance_eval提供了这种强大的方式:

require 'ostruct'
o = OpenStruct.new(entry)
o.instance_eval("Directed by #{director}, produced by #{producer}")
o.instance_eval {
  puts "Directed by #{director}"
  puts "Produced by #{producer}"
}
于 2020-06-16T20:52:43.180 回答