Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 如何动态创建局部变量?_Ruby - Fatal编程技术网

Ruby 如何动态创建局部变量?

Ruby 如何动态创建局部变量?,ruby,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. 您不能在Ruby 1.9+中动态创建局部变量(您可以通过以下方式在Ruby 1.8中创建): 它们可用于评估代码本身,但: eval 'foo = "bar";

我有一个变量
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.

您不能在Ruby 1.9+中动态创建局部变量(您可以通过以下方式在Ruby 1.8中创建):

它们可用于评估代码本身,但:

eval 'foo = "bar"; foo + "baz"'
#=> "barbaz"
添加了Ruby 2.1,但也无法创建新的局部变量:

binding.local_variable_set :foo, 'bar'
foo # NameError: undefined local variable or method `foo' for main:Object
如果不修改Ruby本身,则无法更改此行为。另一种方法是考虑将数据存储在另一个数据结构中,例如散列,而不是许多局部变量:

hash = {}
hash[:my_var] = :foo

请注意,
eval
local\u variable\u set
都允许重新分配现有的局部变量:

foo = nil
eval 'foo = "bar"'
foo  #=> "bar"
binding.local_variable_set :foo, 'baz'
foo  #=> "baz"

尽管如其他人所指出的,您无法在Ruby中动态创建局部变量,但您可以使用以下方法在一定程度上模拟这种行为:

hash_of_variables = {var1: "Value 1", var2: "Value 2"}

hash_of_variables.each do |var, val|
  define_method(var) do
    instance_variable_get("@__#{var}")
  end
  instance_variable_set("@__#{var}", val)
end

puts var1
puts var2
var1 = var2.upcase
puts var1
印刷品:

Value 1
Value 2
VALUE 2
有些库将此技术与
instance\u exec
相结合,以公开块内的局部变量:

def with_vars(vars_hash, &block)
  scope = Object.new
  vars_hash.each do |var, val|
    scope.send(:define_singleton_method, var) do
      scope.instance_variable_get("@__#{var}")
    end
    scope.instance_variable_set("@__#{var}", val)
  end
  scope.instance_exec(&block)
end

with_vars(a: 1, b:2) do
  puts a + b
end
印刷品:3

请注意,抽象绝不是完美的:

with_vars(a: 1, b:2) do
  a = a + 1
  puts a
end
结果为:
未定义nil:NilClass的方法“+”。这是因为
a=
定义了一个实际的局部变量,初始化为
nil
,它优先于方法
a
。然后调用
a.+(1)
,并且
nil
没有
+
方法,因此引发错误


因此,虽然此方法对于模拟只读局部变量非常有用,但当您尝试在块内重新分配变量时,它并不总是很好地工作。

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

b = binding
b.local_variable_set :gaga, 5
b.eval "gaga"
=> 5
有趣的是,每次调用
binding
都会给您一个新的绑定。所以,您需要获得感兴趣的绑定的句柄,然后在设置所需变量后在其上下文中进行求值

这有什么用?例如,我想评估ERB,如果您可以使用
而不是
或类似的东西,那么编写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)
现在您有了一个只包含所需变量的绑定。您可以使用它对ERB或其他(可能是第三方)可信的代码进行更好的受控评估(这不是任何类型的沙盒)。这就像定义一个接口

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

module MyRooTModule
  class Work
    def my_instance_method
      ...
    end
    def not_so_clean_binding
      binding
    end
  end
  class SomeOtherClass
  end
end

现在,我的
my_对象。not_so_clean_绑定
将允许代码在
my_对象
对象上调用
#my_实例_方法
。同样,您可以使用此绑定而不是
MyRootModule::SomeOtherClass.new
在代码中调用例如
SomeOtherClass.new
。因此,在创建绑定时,有时需要更多的考虑,而不仅仅是局部变量。HTH

其他人的说法是,您不能在本地上下文中动态声明true变量。但是,您可以使用对象属性实现类似的功能,而且由于在Ruby世界中,所有东西都是对象(甚至是主上下文),因此您可以轻松地使用新属性扩展这些对象。对于corse,此操作可以动态完成。让我们检查一下这种方法

首先,让我们看一下
irb
的主要作用域

> self
=> main
> self.class
=> Object
> self.class.ancestors
=> [Object, Kernel, BasicObject]
正如您现在看到的,
main
确实是一个对象。对象可以具有与变量具有相同属性的属性。通常,在声明新类时,我们会使用
attr\u accessor
方法,但是
main
已经是一个实例化对象,因此我们不能直接声明新属性。这里模块混入前来救援

variable_name = 'foo'
variable_value = 'bar'

variable_module = Module.new do
  attr_accessor variable_name.to_sym
end

include variable_module

instance_variable_set("@#{variable_name}", variable_value)

p foo # "bar"

self.foo = 'bad'

p foo # "baz"

self.class.ancestors
# [Object, #<Module:0x007f86cc073aa0>, Kernel, BasicObject]

“some_name”
some_name
是不同的,一个是对象,另一个是局部变量..true。我想在Struct.new期间使用
var
的值作为左操作数。也许您正在考虑Perl或PHP之类的东西,其中
$x='y'
$$x='z'
的意思与
$y='z'
相同。Ruby中没有特定的等价物,因为变量引用本身并不存在。为什么不将变量存储在散列中呢<代码>h={};h[:some_name]=结构新(:name);a=h[:某个名称].new('blah')
请阅读最后一段。如果您认为需要动态创建或读取变量,特别是局部变量,这通常表明您没有使用正确的数据结构。Ruby 2.1引入了局部变量集、局部变量get和局部变量defined?在BindingTechnology上,您可以创建局部变量,但它们是
eval
上下文的局部变量。您无法将它们推送到父上下文。感谢提供本地变量集信息!
variable_name = 'foo'
variable_value = 'bar'

variable_module = Module.new do
  attr_accessor variable_name.to_sym
end

include variable_module

instance_variable_set("@#{variable_name}", variable_value)

p foo # "bar"

self.foo = 'bad'

p foo # "baz"

self.class.ancestors
# [Object, #<Module:0x007f86cc073aa0>, Kernel, BasicObject]
require 'metaxa'

include Metaxa

introduce :foo, with_value: 'foo'

puts foo == 'foo' # true
puts foo === get(:foo) # true

set :foo, 'foobar'

puts foo == 'foobar' # true
puts foo === get(:foo) # true

self.foo = 'foobarbaz'

puts foo == 'foobarbaz' # true
puts foo === get(:foo) # true