24

我不完全确定这在 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 dbr's answer

4

11 回答 11

40

如果你把你的问题转过来怎么办?与其尝试从变量中获取名称,不如从名称中获取变量:

["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从抛出异常...

于 2008-09-15T14:41:51.843 回答
28

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

想象一个 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 解决这个问题。

于 2008-09-14T23:43:22.643 回答
8

看来您正在尝试解决一个更容易解决的问题..

为什么不将数据存储在哈希中?如果你这样做..

data_container = {'foo' => ['goo', 'baz']}

..然后获得'foo'的名字是完全微不足道的。

也就是说,您没有为问题提供任何背景信息,因此您可能无法做到这一点。

[编辑]澄清后,我看到了问题,但我认为这不是问题.. 使用 [foo, bar, bla],相当于说['content 1', 'content 2', '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
于 2008-09-12T12:53:08.900 回答
5

好的,它也可以在实例方法中工作,并且根据您的特定要求(您在评论中提出的要求),您可以这样做:

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

只需替换Fixnum为您的特定类型检查即可。

于 2008-09-12T12:35:59.003 回答
2

我不知道有什么方法可以获取局部变量名。但是,您可以使用该instance_variables方法,这将返回对象中所有实例变量名称的数组。

简单调用:

object.instance_variables

或者

self.instance_variables

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

于 2008-09-12T08:17:38.330 回答
2

建立在joshmsmoore上,这样的事情可能会做到:

# 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
于 2008-09-12T11:35:16.403 回答
2

Kernel::local_variables,但我不确定这是否适用于方法的本地变量,而且我不知道您是否可以以某种方式操作它以执行您希望实现的操作。

于 2008-09-12T11:53:16.330 回答
2

好问题。我完全理解你的动机。让我首先指出,有某些特殊对象,在某些情况下,知道它们被分配到的变量。这些特殊对象例如。Module实例,Class实例和Struct实例:

Dog = Class.new
Dog.name # Dog

问题是,这仅在执行赋值的变量是常量时才有效。(我们都知道 Ruby 常量只不过是情绪敏感的变量。)因此:

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

幸运的是,我写了一个 gemy_support/name_magic,它会为你解决这个问题:

 # 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

不幸的是,这不适用于数组文字,因为它们是在内部构造的,没有使用依赖的#new方法。NameMagic因此,要实现您想要的,您将必须继承子类Array

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

我通过搜索当前 Ruby 对象空间的所有命名空间中的所有常量来实现常量魔术模仿行为。这浪费了几分之一秒,但由于只执行一次搜索,一旦对象找出它的名称,就没有性能损失。未来,Ruby 核心团队已经承诺const_assignedhook

于 2013-06-10T17:28:39.413 回答
1

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

于 2008-09-12T15:17:52.970 回答
1

Foo 只是一个保存指向数据的指针的位置。数据不知道它指向什么。在 Smalltalk 系统中,您可以向 VM 询问指向对象的所有指针,但这只会让您获得包含 foo 变量的对象,而不是 foo 本身。在 Ruby 中没有真正的方法来引用变量。正如一个答案所提到的,您仍然可以在数据中放置一个标签来引用它来自哪里等,但通常这不是解决大多数问题的好方法。您可以首先使用散列来接收值,或者使用散列传递给循环,以便您知道用于验证目的的参数名称,如 DBR 的答案。

于 2008-09-15T20:17:49.867 回答
1

最接近您问题的真正答案的是使用 Enumerable 方法 each_with_index 而不是 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

我从您传递给 each/each_with_index 的块中删除了 return 语句,因为它没有做/没有任何意义。each 和 each_with_index 都返回它们正在操作的数组。

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

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

于 2008-09-15T23:22:59.750 回答