如何在 Ruby 中动态访问和分配字符串值作为字符串名称

How to access and assign a string value as a string name dynamically in Ruby

在 PHP 中,我可以通过设置类似 $variable_name = fooecho $$variable_name 的内容来动态访问变量,然后 echo foo 的值。但我不清楚如何在 Ruby 中执行此操作。在 PHP 我可以做这样的事情;在数组中分配变量名并遍历它们:

# Set string values.
$value_one = 'a one';
$value_two = 'and a two';
$value_three = 'and a three';

# Set array of variable names.
$process_items_array = array('value_one', 'value_two', 'value_three');

# Roll through the values.
foreach ($process_items_array as $value) {
  $$value = do_something($$value) . '<br />';
}

# A simple function for example’s sake.
function do_something ($value = null) {
  return strtoupper($value);
}

但在 Ruby 中,什么是等效的?例如,我已经尝试过这个并且 none 的项目按预期工作;请注意,这是通过在非 class 结构中使用 self. 引用而指出的伪代码:

# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';

# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']

# Roll through the values.
process_items_array.each { |value|
  self.send("#{value}").to_sym = do_something value.try(self.send("#{value}").to_sym)
}

# A simple function for example’s sake.
def do_something value
  value = value.try(:strip)
  value = nil if value.blank?
  value.upcase
end

请注意我尝试使用 instance_variable_get, [send][2] and to_sym;我基本上希望有些东西会起作用,但似乎什么都不起作用。我应该怎么做?

你可以用 eval:

# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';

# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']

# Roll through the values.
process_items_array.each do |variable_name|

  # Output value
  puts eval(variable_name)

  # Reassign variable
  new_value = 'foo'
  eval("#{variable_name} = new_value")
end

正如评论中所说,您可能希望将数据存储在哈希中。此外,您需要动态创建或读取变量的代码闻起来真的很糟糕。

更 ruby 的方式(所以没有 "dangerous" eval)

# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';

# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']

# Roll through the values.
process_items_array.each do |variable_name|

  # Output value
  puts binding.local_variable_get(variable_name.to_sym)

  # Reassign variable
  binding.local_variable_set(variable_name.to_sym, 'foo')
end

puts value_one, value_two, value_three
# foo
# foo
# foo

您可以使用Kernel#binding to get the current Binding object and then Binding#local_variable_get and Binding#local_variable_set获取和设置局部变量:

# Set string values.
value_one = 'a one';
value_two = 'and a two';
value_three = 'and a three';

# Set array of variable names.
process_items_array = ['value_one', 'value_two', 'value_three']

# Roll through the values.
process_items_array.each { |value|
  binding.local_variable_set(value.to_sym, do_something(binding.local_variable_get(value).to_sym)
}

# A simple function for example’s sake.
def do_something value
  value = value.try(:strip)
  value = nil if value.blank?
  value.upcase
end

# Show that it works:
p value_one, value_two, value_three
# "A ONE"
# "AND A TWO"
# "AND A THREE"

但是,该代码有点单调:

  • 您不会同时使用分号和换行符作为表达式分隔符,只能使用一个
  • 您将使用 Symbols 来表示变量名称,而不是 Strings
  • 对于多行副作用块,您将使用 do/end 而不是 {/}
  • 您不会在变量名中声明变量引用的对象的类型(例如 foo_array

你还必须在使用之前将 do_something 的定义向上移动,否则你会得到 NoMethodError.

为了更容易重现,我删除了对 active_support 的依赖,这样尝试测试它的人就不必安装额外的 gem,它甚至不需要重现解决方案。

这将是代码的更惯用版本:

# Set string values.
value_one =   'a one'
value_two =   'and a two'
value_three = 'and a three'

# Set array of variable names.
items_to_process = %i[value_one value_two value_three]

# A simple method for example’s sake.
def do_something(value)
  value.upcase
end

b = binding

# Roll through the values.
items_to_process.each do |var|
  b.local_variable_set(var, do_something(b.local_variable_get(var)))
end

# Show that it works:
p value_one, value_two, value_three
# "A ONE"
# "AND A TWO"
# "AND A THREE"