如何动态创建局部变量?

Bal*_*ala 27 ruby

我有一个变量var = "some_name",我想创建一个新对象并将其分配给some_name.我该怎么做?例如

var = "some_name"
some_name = Struct.new(:name) # I need this
a = some_name.new('blah') # so that I can do this.
Run Code Online (Sandbox Code Playgroud)

And*_*all 35

你不能在Ruby 1.9+中动态创建局部变量(你可以在Ruby 1.8中eval):

eval 'foo = "bar"'
foo  # NameError: undefined local variable or method `foo' for main:Object
Run Code Online (Sandbox Code Playgroud)

它们可以在eval-ed代码中使用,但是:

eval 'foo = "bar"; foo + "baz"'
#=> "barbaz"
Run Code Online (Sandbox Code Playgroud)

添加了Ruby 2.1 local_variable_set,但是无法创建新的局部变量:

binding.local_variable_set :foo, 'bar'
foo # NameError: undefined local variable or method `foo' for main:Object
Run Code Online (Sandbox Code Playgroud)

如果不修改Ruby本身,就无法更改此行为.另一种方法是考虑将数据存储在另一个数据结构中,例如Hash,而不是许多局部变量:

hash = {}
hash[:my_var] = :foo
Run Code Online (Sandbox Code Playgroud)

请注意这两个evallocal_variable_set 允许重新分配现有的本地变量:

foo = nil
eval 'foo = "bar"'
foo  #=> "bar"
binding.local_variable_set :foo, 'baz'
foo  #=> "baz"
Run Code Online (Sandbox Code Playgroud)

  • 请阅读最后一段.如果您认为需要动态创建或读取变量,尤其是局部变量,则通常表明您没有使用正确的数据结构. (3认同)
  • Ruby 2.1引入了local_variable_set,local_variable_get和local_variable_defined?在绑定 (2认同)
  • 从技术上讲,您可以创建局部变量,但它们是`eval`上下文的本地变量.您无法将它们推送到父上下文. (2认同)

ako*_*nov 5

说到ruby 2.2.x,你确实无法在当前上下文/绑定中以编程方式创建局部变量.但是你可以在一些特定的绑定中设置变量.

b = binding
b.local_variable_set :gaga, 5
b.eval "gaga"
=> 5
Run Code Online (Sandbox Code Playgroud)

有趣的是,binding每次调用都会给你一个新的绑定.因此,您需要获得您感兴趣的绑定的句柄,然后在设置了所需的变量后在其上下文中进行评估.

这有用吗?例如,我想评估ERB,如果你可以使用<%= myvar %>代替<%= opts[:myvar] %>或类似的东西,写ERB会更好.

要创建一个新的绑定,我正在使用模块方法(我确定有人会纠正我如何正确地调用它,在java中我称之为静态方法)以获得与特定变量集的干净绑定:

module M
  def self.clean_binding
    binding
  end

  def self.binding_from_hash(**vars)
    b = self.clean_binding
    vars.each do |k, v|
      b.local_variable_set k.to_sym, v
    end
    return b
  end
end
my_nice_binding = M.binding_from_hash(a: 5, **other_opts)
Run Code Online (Sandbox Code Playgroud)

现在你只有一个只有所需变量的绑定.您可以使用它来更好地控制ERB或其他(可能是第三方)可信代码的评估(这不是任何类型的沙箱).这就像定义一个接口.

更新:关于绑定的一些附加说明.创建它们的位置也会影响方法和常量分辨率的可用性.在上面的例子中,我创建了一个相当干净的绑定.但是如果我想提供某些对象的实例方法,我可以通过类似的方法创建一个绑定,但是在该对象的类中.例如

module MyRooTModule
  class Work
    def my_instance_method
      ...
    end
    def not_so_clean_binding
      binding
    end
  end
  class SomeOtherClass
  end
end
Run Code Online (Sandbox Code Playgroud)

现在我my_object.not_so_clean_binding可以让代码来调用#my_instance_methodmy_object对象.以同样的方式,您可以SomeOtherClass.new使用此绑定而不是代码在代码中调用MyRootModule::SomeOtherClass.new.因此,在创建绑定时,有时需要更多考虑而不仅仅是局部变量.HTH