我对在Ruby中动态设置局部变量很感兴趣。不是创建方法、常量或实例变量。
例如:
args[:a] = 1
args.each_pair do |k,v|
Object.make_instance_var k,v
end
puts a
> 1
我想要局部变量,因为这个方法存储在一个模型中,我不想破坏全局或对象空间。
我对在Ruby中动态设置局部变量很感兴趣。不是创建方法、常量或实例变量。
例如:
args[:a] = 1
args.each_pair do |k,v|
Object.make_instance_var k,v
end
puts a
> 1
我想要局部变量,因为这个方法存储在一个模型中,我不想破坏全局或对象空间。
补充一下给未来读者的信息,从ruby 2.1.0开始,您可以使用binding.local_variable_get
和binding.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
p a #=> 2
p c #=> NameError
end
正如文档所述,它的行为类似于
binding.eval("#{symbol} = #{obj}")
binding.eval("#{symbol}")
b.local_variable_set(:b, 3)
会覆盖之前存储在 b
中的绑定。将绑定存储在另一个变量中可以解决这个问题。 - Kelvin这里的问题在于每个 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']
当然,在这个例子中,它只是使用字符串作为键复制了原始哈希表。 我假设实际的用例更加复杂。
eval
不是理想的选择,因为并不是所有东西都可以轻松地序列化为字符串。 - max pleaner有趣的是,您可以更改本地变量,但无法设置它:
def test
x=3
eval('x=7;')
puts x
end
测试 => 7
def test
eval('x=7;')
puts x
end
测试 => NameError:未定义 main:Object的本地变量或方法'x'
这是Dorkus Prime的代码有效的唯一原因。
我建议您使用哈希表(但请继续阅读其他备选方案)。
为什么?
允许任意命名参数会导致代码极不稳定。
假设您有一个方法foo
,您想要接受这些理论上的命名参数。
情况如下:
被调用的方法(foo
)需要调用一个不带参数的私有方法(我们称之为bar
)。如果您向foo
传递了一个您想要存储在本地变量bar
中的参数,它将掩盖bar
方法。解决方法是在调用bar
时使用显式括号。
假设foo
的代码分配了一个本地变量。但是,调用者决定传递一个与该本地变量同名的参数。赋值语句将覆盖该参数。
基本上,方法的调用者绝不能改变方法的逻辑。
备选方案
一种中间立场的备选方案涉及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
args
中的值,有什么特别的原因吗? - dnch