在Ruby中动态设置局部变量

All*_*ate 25 ruby metaprogramming local-variables

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

所以类似于:

args[:a] = 1
args.each_pair do |k,v|
  Object.make_instance_var k,v
end
puts a
> 1
Run Code Online (Sandbox Code Playgroud)

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

Geo*_*roy 25

作为未来读者的附加信息,从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
  p a #=> 2
  p c #=> NameError
end
Run Code Online (Sandbox Code Playgroud)

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

binding.eval("#{symbol} = #{obj}")
binding.eval("#{symbol}")
Run Code Online (Sandbox Code Playgroud)


Dor*_*ime 13

这里的问题是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
Run Code Online (Sandbox Code Playgroud)

产生这个:

1
2
undefined local variable or method `a' for main:Object (NameError)
Run Code Online (Sandbox Code Playgroud)

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

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']
Run Code Online (Sandbox Code Playgroud)

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

  • 你说的没错.基本上我想要一种无痛的方法来传递命名参数,然后在方法中使用它们.太糟糕了,eval技巧没有得到它,我想任何选项都会有悲惨的表现. (2认同)

Ann*_*001 10

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

def test
  x=3
  eval('x=7;')
  puts x
end
Run Code Online (Sandbox Code Playgroud)

test => 7

def test
  eval('x=7;')
  puts x
end
Run Code Online (Sandbox Code Playgroud)

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

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


Kel*_*vin 6

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

为什么?

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

假设您有一个方法foo要接受这些理论命名参数.

场景:

  1. 被调用的method(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
Run Code Online (Sandbox Code Playgroud)

请注意,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
Run Code Online (Sandbox Code Playgroud)