在Ruby中通过名称以编程方式访问变量

在Ruby中通过名称以编程方式访问变量,ruby,variables,Ruby,Variables,我不完全确定这在Ruby中是否可行,但希望有一种简单的方法可以做到这一点。我想声明一个变量,然后找出变量的名称。也就是说,对于这个简单的片段: foo = ["goo", "baz"] 如何获取数组的名称(此处为“foo”)?如果确实可能,这对任何变量(例如标量、哈希等)都有效吗 编辑:以下是我基本上想要做的。我正在编写一个SOAP服务器,它围绕一个包含三个重要变量的类进行封装,验证代码基本上如下所示: [foo, goo, bar].each { |param| if par

我不完全确定这在Ruby中是否可行,但希望有一种简单的方法可以做到这一点。我想声明一个变量,然后找出变量的名称。也就是说,对于这个简单的片段:

foo = ["goo", "baz"]
如何获取数组的名称(此处为“foo”)?如果确实可能,这对任何变量(例如标量、哈希等)都有效吗

编辑:以下是我基本上想要做的。我正在编写一个SOAP服务器,它围绕一个包含三个重要变量的类进行封装,验证代码基本上如下所示:

  [foo, goo, bar].each { |param|
      if param.class != Array
        puts "param_name wasn't an Array. It was a/an #{param.class}"
        return "Error: param_name wasn't an Array"
      end
      }

我的问题是:我可以用foo、goo或bar替换“param_name”的实例吗?这些对象都是数组,所以到目前为止我收到的答案似乎不起作用(除了重新设计整个ala)

我不知道如何获得局部变量名。但是,您可以使用
instance\u variables
方法,这将返回对象中所有实例变量名称的数组

简单呼叫:

object.instance_variables

要获取所有实例变量名的数组。

基于此,类似这样的操作可能会实现:

# Returns the first instance variable whose value == x
# Returns nil if no name maps to the given value
def instance_variable_name_for(x)
  self.instance_variables.find do |var|
    x == self.instance_variable_get(var)
  end
end

Kernel::local_variables
,但我不确定这是否适用于方法的局部变量,而且我不知道您是否可以以这样的方式操纵它,以实现您希望实现的目标。

好的,它也适用于实例方法,并且根据您的特定需求(您在注释中输入的需求),您可以这样做:

local_variables.each do |var|
  puts var if (eval(var).class != Fixnum)
end

只需使用特定的类型检查替换
Fixnum

似乎您正在尝试解决一个更容易解决的问题

为什么不将数据存储在散列中?如果你想

data_container = {'foo' => ['goo', 'baz']}
那么,得到“foo”这个名字就完全是小事一桩了

也就是说,您没有给出任何问题的背景,因此可能有一个原因您不能这样做

[编辑]澄清后,我看到了问题,但我认为这不是问题所在。。与[foo,bar,bla]一样,它相当于说
['content1','content2','etc']
。实际变量名(或者更确切地说,应该是)完全不相关。如果变量的名称很重要,这正是哈希存在的原因

问题不在于迭代[foo,bar]等,而在于SOAP服务器如何重新存储数据和/或您如何尝试使用数据

我想说,解决方案是要么让SOAP服务器返回散列,要么,既然你知道总会有三个元素,你就不能做类似

{"foo" => foo, "goo" => goo, "bar"=>bar}.each do |param_name, param|
      if param.class != Array
        puts "#{param_name} wasn't an Array. It was a/an #{param.class}"
        puts "Error: #{param_name} wasn't an Array"
      end
end

你不能,你需要回到绘图板,重新设计你的解决方案。

你需要重新设计你的解决方案。即使你能做到(你做不到),这个问题也没有一个合理的答案

想象一个get_name方法

a = 1
get_name(a)
每个人都可能同意这应该返回“a”

b = a
get_name(b)
它应该返回“b”或“a”,还是返回一个同时包含这两个元素的数组

[b,a].each do |arg|
  get_name(arg)
end
它应该返回“arg”、“b”还是“a”

def do_stuff( arg )
  get_name(arg)
do
do_stuff(b)
它应该返回'arg'、'b'或'a',或者可能返回它们的数组吗?即使它确实返回了一个数组,顺序是什么?我如何知道如何解释结果


以上所有问题的答案都是“这取决于我当时想要什么样的东西。”我不确定如何为Ruby解决这个问题。

如果你能扭转你的问题怎么办?不要试图从变量中获取名称,而是从名称中获取变量:

["foo", "goo", "bar"].each { |param_name|
  param = eval(param_name)
  if param.class != Array
    puts "#{param_name} wasn't an Array. It was a/an #{param.class}"
    return "Error: #{param_name} wasn't an Array"
  end
  }

如果有一个变量可能根本没有定义(与不是数组相反),您可能希望在“param=…”行的末尾添加“rescue nil”,以防止eval抛出异常…

Foo只是一个保存指向数据的指针的位置。数据不知道指向它的点是什么。在Smalltalk系统中,您可以向VM请求指向某个对象的所有指针,但这只能得到包含foo变量的对象,而不是foo本身。在Ruby中没有真正的方法来引用vaiable。正如一个答案所提到的,您仍然可以在数据中放置一个标记,该标记引用了数据的来源,但通常这并不是解决大多数问题的好方法。您可以首先使用散列来接收值,或者使用散列来传递到循环,以便知道参数名称,以便进行验证,如DBR的答案中所示。

最接近您问题的真实答案是使用可枚举方法each_,而不是each,因此:

my_array = [foo, baz, bar]
my_array.each_with_index do |item, index|
  if item.class != Array
    puts "#{my_array[index]} wasn't an Array. It was a/an #{item.class}"
  end
end
我将return语句从您传递给每个/each_和_索引的块中删除,因为它没有任何作用/意义。每个和每个带有_索引的_都返回它们正在操作的数组

关于块中的作用域还有一点值得注意:如果您在块外定义了一个变量,它将在块内可用。换句话说,您可以直接在块内引用foo、bar和baz。反之亦然:在块内首次创建的变量在块外将不可用


最后,do/end语法是多行代码块的首选语法,但这只是风格问题,尽管它在任何最新版本的ruby代码中都是通用的。

好问题。我完全理解你的动机。让我首先指出,有一些特殊的对象,在某些情况下,它们知道它们所分配的变量。这些特殊对象包括
模块
实例、
实例和
结构
实例:

Dog = Class.new
Dog.name # Dog
问题是,只有当执行赋值的变量是常量时,这种方法才有效。(我们都知道Ruby常量只不过是情绪敏感的变量。)因此:

对象意识到自己被分配了哪些变量,这种行为通常被称为常量魔法(因为它仅限于常量)。但是
Dog = Class.new
Dog.name # Dog
x = Module.new # creating an anonymous module
x.name #=> nil # the module does not know that it has been assigned to x
Animal = x # but will notice once we assign it to a constant
x.name #=> "Animal"
Rover = Dog.new
Rover.name #=> raises NoMethodError
 # first, gem install y_support
require 'y_support/name_magic'

class Cat
  include NameMagic
end
tmp = Cat.new # nameless kitty
tmp.name #=> nil
Josie = tmp # by assigning to a constant, we name the kitty Josie
tmp.name #=> :Josie
require 'y_support/name_magic'
class MyArr < Array
  include NameMagic
end

foo = MyArr.new ["goo", "baz"] # not named yet
foo.name #=> nil
Foo = foo # but assignment to a constant is noticed
foo.name #=> :Foo

# You can even list the instances
MyArr.instances #=> [["goo", "baz"]]
MyArr.instance_names #=> [:Foo]

# Get an instance by name:
MyArr.instance "Foo" #=> ["goo", "baz"]
MyArr.instance :Foo #=> ["goo", "baz"]

# Rename it:
Foo.name = "Quux"
Foo.name #=> :Quux

# Or forget the name again:
MyArr.forget :Quux
Foo.name #=> nil

# In addition, you can name the object upon creation even without assignment
u = MyArr.new [1, 2], name: :Pair
u.name #=> :Pair
v = MyArr.new [1, 2, 3], ɴ: :Trinity
v.name #=> :Trinity