30

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

所以像:

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

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

4

5 回答 5

28

作为未来读者的附加信息,从 ruby​​ 2.1.0 开始,您可以使用binding.local_variable_getand 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}")
于 2015-02-27T14:39:25.550 回答
14

这里的问题是 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']

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

于 2011-02-10T23:32:58.343 回答
10

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

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

测试 => 7

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

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

这是 Dorkus Prime 的代码有效的唯一原因。

于 2013-01-10T19:19:40.790 回答
6

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

为什么?

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

假设您有一个方法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
于 2013-06-28T16:53:57.497 回答
1

因为你不想要常量

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

于 2013-01-10T19:10:34.447 回答