【问题标题】:Variable name from Self in RubyRuby 中来自 Self 的变量名
【发布时间】:2012-06-10 18:14:04
【问题描述】:

我有一个变量,我们称之为@foo。我希望它是一个字符串,所以我打电话给@foo.downcase。有时它为零,我会得到这个错误:

NoMethodError: undefined method `downcase' for nil:NilClass
    from (irb):4
    from /Users/schneems/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>

我想做的是写一些代码告诉我nil实际上是@foo

NoMethodError: undefined method `downcase' on variable @foo, variable is a nil:NilClass
    from (irb):4
    from /Users/schneems/.rvm/rubies/ruby-1.9.2-p290/bin/irb:16:in `<main>

要做这样的事情,我需要以编程方式获取变量的名称。

问题:是否可以通过 ruby​​ 编程方式获取变量的名称?

我正在寻找能产生如下输出的东西:

@foo.magical_variable_method    #=> '@foo'
bar.magical_variable_method     #=> 'bar'
AweSome.magical_variable_method #=> 'AweSome'
$wat.magical_variable_method    #=> '$wat'

我不想要变量的值,也不关心它是否是nil。我想要变量的人类可读名称。是否有可能以编程方式获取变量的名称?

【问题讨论】:

  • 你能再澄清一下你想要什么吗?您是否正在寻找一种方法来查看@foo 是否为nil,或者寻找一种能够返回所有当前为nil 的变量的方法?或者完全是别的什么?
  • 我更新了这个问题,希望能更清楚一点,谢谢你的提问。
  • 在没有宏的语言中可能会非常困难。换成 Lisp 怎么样?
  • 确实有可能我拼凑了一个潜在的解决方案gist.github.com/2907252,尽管如果您尝试使该代码可重用,事情会变得很棘手,因为这样做需要在方法中使用变量。也许有一些 ruby​​ bindingfu 的人可以解决这个问题。

标签: ruby


【解决方案1】:

你想要的有点乱。

这是完成此任务的正确方法:

@foo && @foo.downcase

或者你可以使用 rails 库:

require 'active_support'

@foo.try(:downcase)

【讨论】:

  • 这根本不能回答我的问题,这就是如何防止在不存在的变量上调用方法。我想从名为@foo的变量中以某种方式输出“@foo”的字符串,如果它被命名为@bar,我想要名为“@bar”的变量。
【解决方案2】:

目前没有任何方法可以做到这一点。

变量只是引用,而不是对象本身。如果你使用点符号来调用一个方法,比如downcase,那么这个方法是在一个对象上操作,而不是一个变量。您示例中的对象是单例nil;如果nil 在一个地方被分配了一个属性来命名它被分配到的变量,那么相同的属性将适用于所有nils。

更一般地说,一个对象可能有许多指向它的变量/引用,因此没有很好的方法来确定应该将哪个变量名保存在对象中。 然而,Ruby 中有一些特殊的处理方法可以将一个类分配给一个常量;在这种情况下,类对象确实记住它分配给的第一个常量的名称,例如:

$ irb
1.9.3p194 :001 > Foo = Class.new do
1.9.3p194 :002 >   attr_accessor :foo
1.9.3p194 :003?> end
 => Foo 
1.9.3p194 :004 > Bar = Foo
 => Foo 
1.9.3p194 :005 > Foo.name
 => "Foo" 
1.9.3p194 :006 > Bar.name
 => "Foo" 

【讨论】:

  • 想一想,但是,您会认为实现将能够将您发送消息的对象的变量的名称保存在内存中 - 只是对象本身不会保存该信息。我敢打赌,在这种情况下,解析器知道变量名,但实际上发送消息的 Ruby 部分却不知道;但必须深入研究来源才能找出答案。
  • 通常不会保留局部变量。它们只是通过索引寻址到局部变量数组中。一些实现还进行了优化,它们将少量实例变量直接保存在对象头中的数组中,这些实例变量也只是按数字索引。当然,优化编译器通常会完全消除变量。
【解决方案3】:

您可以使用 instance_variable_get(:@foo) 从符号中获取实例变量 这会给你带来价值。

但你可以这样做:

puts "I'm gonna call @foo now, people"
some_obj.instance_variable_get(:@foo)

你也可以为你想看的 attrs 做一个 method_missing 钩子。不要为 @foo 创建访问器方法并在 method_missing 中捕获调用并将其转发给一些通用实现,例如 instance_variable_get

未经测试的尝试:

def method_missing(method_name, *args)
  super(method_name, *args) unless watched_attributes.include?(method_name)
  attr = ":@#{method_name.to_str}"
  log.debug "Calling watched attribute #{attr}"
  val = instance_variable_get(method_name)
  log.debug "#{attr} was nil omg!" unless val
  val
end

【讨论】:

    【解决方案4】:

    编辑: 最好的方法(参见 mu 的评论)可能是确保您使用的是字符串:

    @foo.to_s.downcase
    

    您无需获取“名称”即可执行此操作。您可以只使用条件:

    # Downcased if @foo's a string, empty string otherwise:
    (String === @foo)? @foo.downcase : ''
    

    或者,如果您要在很多地方执行此操作,并且不想将每个都包装在有条件的猴子补丁 NilClass 并添加一个虚拟的 downcase 方法:

    class NilClass
        def downcase
            return ''
        end
    end
    

    希望有帮助!

    【讨论】:

    • 猴子修补NilClass 是疯狂和自找麻烦。如果您想将nil 转换为空字符串,那么您应该改为@foo.to_s.downcase,这样阅读代码的人至少会得到一个提示,即@foo 有一些特别之处。
    • @muistooshort - 我想说在这种情况下它比平常更安全,因为很明显downcase 应该返回什么。同意这是一条危险的道路,尽管如此......无论如何,to_s 绝对是最好的,我已经忘记了它(dur)。现在修好了。干杯!
    • 我想从名为@foo的变量中以某种方式输出“@foo”的字符串,如果它被命名为@bar,我想要名为“@bar”的变量。我熟悉如何防止方法错误,我想要的是 ruby​​ 编程的实际变量名称,正如问题所述。
    猜你喜欢
    • 2018-04-01
    • 2018-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多