【问题标题】:Scope of method defined inside another method在另一个方法中定义的方法范围
【发布时间】:2015-01-01 15:59:30
【问题描述】:

考虑以下代码:

class Emotion
  def confused?
    def confused?
      'yes'
    end
    'no'
  end
end

am_i = Emotion.new

am_i.confused? # => "no"
am_i.confused? # => "yes"

5.times.map { Emotion.new.confused? } # => ["yes", "yes", "yes", "yes", "yes"]

解释:

  1. 第一次运行 confused? 返回 "no"重新定义自身。
  2. 第二次运行confused?会调用返回"yes"的新方法。

虽然上述功能很清楚,但我不确定这两种方法是如何定义到相同的范围的。

我问的原因是因为:

  • 不能在方法内部和外部以类似的方式访问变量;例如:如果我在方法内部和外部定义@variable,则它具有不同的含义。
  • 我了解到方法定义更改selfconfused? 方法的内部定义与外部定义相比,不会有不同的范围吗?
  • def 如何实际确定在哪里定义方法?

【问题讨论】:

  • Ruby 没有嵌套方法。当您第一次调用该方法时,它会执行其主体并定义第二个方法。下次调用该方法时,您会得到不同的结果,因为内部方法已定义,并且具有优先权。正如前面评论中提到的,Emotion.new.confused 将始终返回“no”。
  • 我在 ruby​​ 2.2.0 上实际测试过这个loop { p Emotion.new.confused? } 打印出"no",然后是"yes" 的流。

标签: ruby metaprogramming


【解决方案1】:

变量不能在方法内部和外部以类似的方式访问;例如:如果我在方法内部和外部定义@variable,它具有不同的含义。

使用@x = y语法设置实例变量时,实例变量设置为self

我了解到方法定义会改变 self.会不会是混淆的内部定义?方法与外部定义的作用域不同?

范围可以改变,但在这种情况下它不会改变。方法中self 的值始终是调用该方法的对象。在您的两个confused? 方法定义中,selfEmotion 的一个实例。

def 如何实际确定在哪里定义方法?

这个问题的答案(至少在 MRI 中)实际上有点不直观。每个范围都有一个对“默认定义”对象的引用,该对象不一定与self 相关,并且始终是Module 的一个实例。 def 在该范围内将为默认定义者定义一个方法。在 ruby​​ 代码中获取默认定义的最简单的方法是:

eval("def __m; end; m = method(:__m); undef __m; m.owner")

例如,在 ruby​​ 程序的顶层运行它会返回Object。因此,顶级作用域的默认定义者是Object

这里有一些代码有望回答您关于范围的问题:

# `self` is the toplevel object
# default definee is `Object`

class X
  # `self` is X
  # default definee is also X

  @a = 1 # defines instance variable @a for `X`

  def y # defines method 'y' on X

    # `self` is an instance of X
    # default definee is X

    @b = 2 # defines instance variable @b for an instance of `X`

    def z # defines method 'z' on X

      # `self` is still an instance of X
      # default definee is still X

      @c = 3 # defines instance variable @c for an instance of `X`

    end
  end

  class << self
    # `self` is the metaclass of X
    # default definee is also the metaclass of X

    @d = 4 # defines instance variable @d for the metaclass of X
  end
end

请务必记住,方法始终存储在Module的实例中,而实例变量可以存储在任何对象中(不包括@987654335之类的原语@ 或布尔值或整数)。

【讨论】:

  • 小尼特。 Ruby 中没有原始类型。一切都是对象,包括 nil、布尔值和整数。
  • @Fred:“原始”是指不存储在内存中的对象(因此不能有实例变量或元类)。它们仍然是对象,但缺少这些属性。尝试这样做,你会得到一个错误:nil.instance_eval { @xyz = 123 }
  • 我不知道默认定义的概念!谢谢,会阅读更多。正如您所提到的,它肯定不直观,几乎没有任何元编程资源提及它。
猜你喜欢
  • 2014-09-16
  • 1970-01-01
  • 2012-05-11
  • 2012-05-13
  • 1970-01-01
  • 1970-01-01
  • 2013-05-20
  • 2021-11-30
  • 2019-03-20
相关资源
最近更新 更多