在Ruby中动态设置局部变量

我有兴趣在Ruby中动态设置局部变量。 不创建方法,常量或实例变量。

所以类似于:

args[:a] = 1 args.each_pair do |k,v| Object.make_instance_var k,v end puts a > 1 

我特别想要局部变量,因为所讨论的方法存在于模型中,我不想污染全局或对象空间。

这里的问题是each_pair中的块具有不同的范围。 其中分配的任何局部变量只能在其中访问。 例如,这个:

 args = {} args[:a] = 1 args[:b] = 2 args.each_pair do |k,v| key = k.to_s eval('key = v') eval('puts key') end puts a 

产生这个:

 1 2 undefined local variable or method `a' for main:Object (NameError) 

为了解决这个问题,您可以创建一个本地哈希,为此哈希分配密钥,并在那里访问它们,如下所示:

 args = {} args[:a] = 1 args[:b] = 2 localHash = {} args.each_pair do |k,v| key = k.to_s localHash[key] = v end puts localHash['a'] puts localHash['b'] 

当然,在这个例子中,它只是用键的字符串复制原始哈希。 我假设实际的用例更复杂。

作为未来读者的附加信息,从ruby 2.1.0开始,您可以使用binding.local_variable_getbinding.local_variable_set

 def foo a = 1 b = binding b.local_variable_set(:a, 2) # set existing local variable `a' b.local_variable_set(:c, 3) # create new local variable `c' # `c' exists only in binding. b.local_variable_get(:a) #=> 2 b.local_variable_get(:c) #=> 3 pa #=> 2 pc #=> NameError end 

正如文档中所述 ,它是一种类似的行为

 binding.eval("#{symbol} = #{obj}") binding.eval("#{symbol}") 

有趣的是,您可以更改局部变量但不能设置它:

 def test x=3 eval('x=7;') puts x end 

test => 7

 def test eval('x=7;') puts x end 

test => NameError:未定义的局部变量或main:Object的方法`x’

这是Dorkus Prime代码工作的唯一原因。

我建议你使用哈希(但继续阅读其他替代方案)。

为什么?

允许任意命名参数会导致代码极不稳定。

假设你有一个方法foo ,你想接受这些理论上的命名参数。

场景:

  1. 被调用的方法( foo )需要调用一个不带参数的私有方法(让我们称之为bar )。 如果你将一个参数传递给foo ,你想要存储在局部变量bar ,它将掩盖bar方法。 解决方法是在调用bar时使用明确的括号。

  2. 假设foo的代码分配了一个局部变量。 但随后调用者决定传入一个与该局部变量同名的arg。 分配将破坏论证。

基本上,方法的调用者必须永远不能改变方法的逻辑。

备择方案

另一个中间立场涉及OpenStruct 。 它比使用哈希更少打字。

 require 'ostruct' os = OpenStruct.new(:a => 1, :b => 2) os.a # => 1 os.a = 2 # settable os.foo # => nil 

请注意, OpenStruct允许您访问不存在的成员 – 它将返回nil 。 如果您想要更严格的版本,请改用Struct

这将创建一个匿名类,然后实例化该类。

 h = {:a=>1, :b=>2} obj = Struct.new(* h.keys).new(* h.values) obj.a # => 1 obj.a = 2 # settable obj.foo # NoMethodError 

因为你不想要常数

 args = {} args[:a] = 1 args[:b] = 2 args.each_pair{|k,v|eval "@#{k}=#{v};"} puts @b 

2

您可能会发现此方法很有趣(在正确的上下文中评估变量)

 fn="b*b" vars="" args.each_pair{|k,v|vars+="#{k}=#{v};"} eval vars + fn 

4