Что делает в Ruby присваивание верхнего уровня?

У меня есть следующий код на верхнем уровне файла .rb:

class Times
    def initialize(n)
        @n = n               
    end
    def each()        
        (1..@n).each {yield}
    end
end
three_times = Times.new(3)

def f()
    Times.new(3).each {puts 'Test'}
end
f()

Это работает и печатает «Test» три раза, как и ожидалось. Однако, если я заменю Times.new(3) в f на three_times, то есть three_times.each {puts 'Test'}, я получаю сообщение об ошибке:

`f': undefined local variable or method `three_times' for main:Object (NameError)

Почему это не работает? Почему Times доступен из f, но не three_times?

В более общем смысле, что именно делает присваивание на верхнем уровне (например, three_times = Times.new(3))?


person user200783    schedule 16.09.2018    source источник


Ответы (4)


Почему это не работает? Почему Times доступен из f, но не three_times?

Переменные, имя которых начинается со строчной буквы, являются локальными переменными. Локальные переменные являются локальными для области, в которой они определены (поэтому они называются локальными переменными).

Переменные, имя которых начинается с заглавной буквы, являются константами. Константы сначала просматриваются в области действия констант по умолчанию, затем лексически наружу, а затем динамически вверх по наследству.

В более общем смысле, что именно делает присваивание на верхнем уровне (например, three_times = Times.new(3))?

Ничего особенного. Он делает то же самое, что и присваивание в любом другом месте. В этом случае это:

  1. Разыменовывает переменную (константу) Times, назовем этот объект o1.
  2. Оценивает буквальное целочисленное выражение 3, назовем полученный объект o2.
  3. Отправляет сообщение new на o1, передавая o2 в качестве аргумента. Назовем ответ на это сообщение send o3.
  4. Связывает o3 с локальной переменной с именем three_times.

Как видите, здесь нет ничего, что было бы каким-то образом специфично для области действия скрипта или верхнего уровня.

person Jörg W Mittag    schedule 16.09.2018

Это потому, что он ищет локальную переменную с именем «three_times». Если вы хотите сделать "three_times" "верхним уровнем" или "глобальным", добавьте перед именем переменной $, чтобы получилось "$three_times".

person Alex Couch    schedule 16.09.2018

Так как

  • three_times — локальная переменная
  • локальные переменные доступны только внутри определенной области
  • def в ruby ​​создает новую область видимости

Поэтому, когда f вызывается, он не видит и не имеет доступа к three_times

Чтобы получить доступ к three_times, измените его либо на глобальную переменную $three_times, либо на переменную экземпляра @three_times.

Причина, по которой вы можете ссылаться на класс Times, заключается в том, что это константа, а ruby ​​выполняет отдельный процесс поиска констант.


Обход проблемы с def

Вы также можете получить доступ к локальной переменной, используя блок для определения вашего метода, который обходит всю проблему с воротами области видимости. Я делаю это иногда при написании rake-задач, но редко делаю это вне скриптов.

three_times = Times.new(3)
define_method :foo do
  three_times.each { puts 'Tests'}
end
foo
person Austio    schedule 16.09.2018

Ваш код у меня работает, ошибок нет. Вы можете успешно позвонить f()

person Sergio Rivas    schedule 16.09.2018
comment
Как уже упоминалось, код перестает работать, если я заменю Times.new(3) в f на three_times. - person user200783; 16.09.2018