Подтвердить что ты не робот

Ruby - Лексический объем против наследования

Это продолжение этого исходного вопроса SO: Использование "::" вместо "модуля..." для пространства имен Ruby

В исходном SO-вопросе вот представленный сценарий, который у меня все еще не понятен:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

class Foo::Bar
  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123

Может ли кто-нибудь объяснить, почему первый вызов возвращает 555 и почему второй вызов возвращает 123?

4b9b3361

Ответ 1

Вы можете представить каждый вид module Something, class Something или def something в качестве "шлюза" в новую область. Когда Ruby ищет определение имени, на которое он ссылается, он сначала просматривает текущую область (метод, класс или модуль), и если он не найден там, он будет возвращаться через каждый из них, содержащий "шлюз", и искать область есть.

В вашем примере метод baz определяется как

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

Поэтому, когда вы пытаетесь определить значение FOO, сначала проверяется класс Bar, и поскольку Bar не содержит FOO, поиск перемещается по "class Bar шлюзу" в FOO, который является областью хранения. FOO содержит константу FOO (555), поэтому это результат, который вы видите.

Метод glorf определяется как:

class Foo::Bar
  def glorf
    puts FOO
  end
end

Здесь "шлюз" class Foo::Bar, поэтому, когда FOO не находится внутри Bar, "шлюз" проходит через модуль FOO и прямо на верхний уровень, где есть еще один FOO ( 123), что и отображается.

Обратите внимание, что использование class Foo::Bar создает один "шлюз", пропускающий область FOO, но module Foo; class Bar ... открывает два отдельных "шлюза"

Ответ 2

ничего себе, большой вопрос. Лучший ответ, который я могу придумать, заключается в том, что вы используете модуль для определения пространства имен.

Проверьте это:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end

    def glorf3
      puts ::FOO
    end
  end
end

class Foo::Bar
  def glorf2
    puts Foo::FOO
  end

  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123
puts Foo::Bar.new.glorf2  # -> 555
puts Foo::Bar.new.glorf3  # -> 123

Итак, я думаю, что когда вы определяете:

module Foo
  FOO = 555
end

вы создаете FOO в пространстве имен FOO. Поэтому, когда вы используете его здесь:

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

вы находитесь в пространстве имен FOO. Однако, когда вы ссылаетесь на него в:

class Foo::Bar
  def glorf
    puts FOO
  end
end

FOO поступает из пространства имен по умолчанию (как показано на рисунке ::FOO).

Ответ 3

первый вызов:

puts Foo::Bar.new.baz    # -> 555

выводит результат вызова метода baz экземпляра класса Foo:: Bar

обратите внимание, что определение Foo:: Bar # baz на самом деле является закрытием на FOO. Следуя правилам рубиновой области:

  • FOO выполняется в Foo:: Bar (класс, а не экземпляр), он не найден,
  • FOO выполняется в охватывающей области Foo (потому что мы находимся в пределах определения модуля) и находится там (555)

второй вызов:

puts Foo::Bar.new.glorf  # -> 123

выводит результат вызова метода glorf экземпляра класса Foo:: Bar

обратите внимание, что определение Foo:: Bar # glorf также является закрытием на FOO, но если мы будем следовать правилам области рубинов, вы заметите, что значение закрыто на этот раз :: FOO (область верхнего уровня FOO) следующим образом:

  • FOO выполняется в Foo:: Bar (пространстве имен класса, а не экземпляре), он не найден
  • FOO выполняется в охватывающей области ( "верхний уровень" ) и находится там (123)

Ответ 4

glorf - это метод класса Foo, в => [Foo, Module, Object, Kernel, BasicObject]

внутри этой области (то есть в стандартном/основном модуле), FOO назначается 123

модуль Foo определяется как

module Foo
  FOO = 555
  class Bar
    def baz
      puts FOO
    end
  end
end

где метод baz принадлежит классу Bar в модуле Foo => [Bar, Foo, Object, Kernel, BasicObject]

и в этой области FOO было назначено 555