代码之家  ›  专栏  ›  技术社区  ›  Chris Bunch

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

  •  24
  • Chris Bunch  · 技术社区  · 18 年前

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

    foo = ["goo", "baz"]
    

    编辑:以下是我基本上想要做的。我正在编写一个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”的实例吗?这些对象都是数组,所以到目前为止我收到的答案似乎不起作用(除了重新设计整个过程) dbr's answer

    11 回复  |  直到 9 年前
        1
  •  40
  •   glenn mcdonald    18 年前

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

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

        2
  •  28
  •   Orion Edwards    13 年前

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

    想象一个get_name方法。

    a = 1
    get_name(a)
    

    每个人都可能同意这应该返回“a”

    b = a
    get_name(b)
    

    [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解决这个问题。

        3
  •  8
  •   dbr    18 年前

    似乎你在试图解决一个更容易解决的问题。。

    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
    
        4
  •  5
  •   sergtk    18 年前

    好的,它在实例方法中也起作用,并且,根据您的特定需求(您在注释中提出的需求),您可以这样做:

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

    替换 Fixnum 与您的具体类型检查。

        5
  •  2
  •   Tim Post    18 年前

    我不知道如何获得局部变量名。但是,您可以使用 instance_variables

    object.instance_variables
    

    或

    self.instance_variables
    

        6
  •  2
  •   Community Mohan Dere    9 年前

    建立在 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
    
        7
  •  2
  •   sergtk    18 年前

    有 Kernel::local_variables

        8
  •  2
  •   Boris Stitnicky    13 年前

    好问题。我完全理解你的动机。让我首先指出,有一些特殊的对象,在某些情况下,它们知道它们所分配的变量。这些特殊物体是例如。 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
    

    幸运地 I have written a gem y_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
    

    搜索所有名称空间中的所有常量 has promised const_assigned hook .

        9
  •  1
  •   pauliephonic    18 年前

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

        10
  •  1
  •   Michael Latta    18 年前

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

        11
  •  1
  •   Greg Borenstein    18 年前

    对于您的问题,最接近真实答案的方法是使用可枚举方法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。反之亦然:在块内首次创建的变量在块外将不可用。

    推荐文章