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

时间:2020-03-05 18:52:03  来源:igfitidea点击:

我不确定在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'的实例吗?这些对象都是数组,因此到目前为止我收到的答案似乎无效(除了重新设计整个过程之外)

解决方案

回答

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

简单的电话:

object.instance_variables

或者

self.instance_variables

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

回答

在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

回答

Kernel :: local_variables,但我不确定这是否适用于方法的本地var,而且我不知道我们可以通过某种方式来操纵它,以达到我们希望达到的目的。

回答

好的,它也可以在实例方法中工作,并且根据特定要求(我们在注释中提出的要求),我们可以执行以下操作:

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

只需将" Fixnum"替换为特定类型检查即可。

回答

看来我们正在尝试解决更容易解决的问题。

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

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

回答

不能,我们需要返回到绘图板上并重新设计解决方案。

回答

我们需要重新设计解决方案。即使可以(不能),问题也没有合理的答案。

想象一下一个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"以保持评估从引发异常...

回答

Foo只是保存指向数据的指针的位置。数据不知道它指向什么。在Smalltalk系统中,我们可以要求VM提供指向对象的所有指针,但这只会使我们获得包含foo变量的对象,而不是foo本身。在Ruby中没有真正的方法来引用可用商品。正如一个答案所提到的,我们可以在数据中放置一个标记,以引用其来源或者类似标记,但通常这并不是解决大多数问题的好方法。我们可以首先使用哈希来接收值,也可以使用哈希来传递给循环,以便我们知道用于验证目的的参数名称,如DBR的答案。

回答

对于问题,最接近实际答案的方法是使用Enumerable方法each_with_index而不是每个方法,因此:

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_with_index都返回它们在其上操作的数组。

块中还有一些关于范围的内容,值得注意的是:如果我们在块外定义了变量,则该变量将在其中可用。换句话说,我们可以直接在块内部引用foo,bar和baz。相反,情况并非如此:我们在块内首次创建的变量将无法在块外使用。

最后,do / end语法是多行代码块的首选,但这只是样式问题,尽管它在任何最近年份的红宝石代码中都是通用的。