【问题标题】:How to access and assign a string value as a string name dynamically in Ruby如何在 Ruby 中动态访问和分配字符串值作为字符串名称
【发布时间】:2015-03-09 05:23:47
【问题描述】:

在 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 中,什么是等价的?例如,我已经尝试过,但没有一个项目按预期工作;请注意,这是在非类结构中使用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]to_sym;我基本上希望有些事情会奏效,但似乎没有任何效果。我应该怎么做?

【问题讨论】:

  • 我确定您使用它的目的是什么,但这似乎不是一个人应该在 Ruby 中做的事情 - 可能有一种更强大的方法可以用一点点重新设计。
  • 这就是发明字典和哈希映射的目的——通过另一个(可变的)键来引用一个值。
  • @JavaNut13 虽然我很欣赏你所说的,但现实是我无法重构我没有创建的整个代码库,所以我必须简单地使用我拥有的代码库并以我最好的方式处理它能够。如果客户认为重构值得花时间/精力,我会考虑沿着这条路走下去。

标签: ruby string variable-assignment


【解决方案1】:

您可以使用Kernel#binding 获取当前的Binding 对象,然后使用Binding#local_variable_getBinding#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"

【讨论】:

    【解决方案2】:

    更红宝石的方式(所以没有“危险”的评估)

    # 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
    

    【讨论】:

    • 不幸的是,绑定仅在 ruby​​ 2.1 及更高版本中可用
    • 我已经发布了an answer,展示了如何调用方法以方便您使用。
    • “一种更红的方式(所以没有“危险的”评估)” 如果您可以控制发送给它的数据,eval 并不危险;就我而言,eval 永远不会有风险,因为数据是隔离/控制的。也就是说,如果您认为local_variable_geteval 更“危险”,请阅读手册页,其中明确指出local_variable_get 只是binding.eval("#{symbol}") 的一种便捷方法。一个 6 个,另一个 1/2 个。 ruby-doc.org/core-2.1.5/…
    【解决方案3】:

    您可以使用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
    

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

    【讨论】:

    • 所以这个答案在访问字符串数据方面确实有效,但我该如何重新分配它?我刚刚编辑了我的问题以删除echoputs。我基本上想做eval(value) = do_something eval(value)。怎么样?
    • Eval 只是返回由相关 ruby​​ 代码重新调整的值,我会在一分钟内更新我的答案
    • 您能否编辑您的答案以获得执行此操作所需的实际代码,而不是回答 cmets?
    • @JakeGould 完成,只是想向您说明 eval 的作用
    • @JakeGould 很高兴为您提供帮助,希望您会爱上 Ruby =)
    猜你喜欢
    • 2015-08-08
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多