Рубрика:
Разработка /
Особенности языка
|
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|
ИВАН ШИХАЛЕВ, фрилансер, специализируется на веб-разработке и Ruby, shikhalev@gmail.com
Блоки и контекст в Ruby, или Что стоит за идентификатором в данном окружении
Давайте разберемся с программным контекстом в Ruby: какие переменные и другие объекты доступны в конкретном месте программы, и как интерпретатор их ищет? Что обозначает конкретный идентификатор, откуда он берется? Почему отсюда, а не оттуда? И чему, наконец, в этом трижды перекинутом блоке будет равен self?
В программировании, неважно, на каком языке, есть такое понятие «контекст выполнения» – если мы не работаем исключительно с глобальными переменными, важно понимать, какие локальные объекты доступны и задействованы в каждой конкретной точке программы. Это достаточно просто для понимания, хотя и важно, в случае объектно-ориентированных языков, дизайн которых направлен на то, чтобы максимально изолироваться от глобального окружения и работать внутри одного объекта. И несколько сложнее, но еще более важно, в случаях, когда язык поддерживает замыкания – по сути, вынесение кода вместе с его контекстом в другое место.
На самом деле никакой особой магии (по крайней мере в случае Ruby) тут нет, и правила, определяющие работу с контекстом, довольно просты, а главное – логичны. Однако их надо знать и понимать очень четко, поскольку вариантов использования много, а кроме того, в языке есть способы переопределить поведение по умолчанию. Кроме того, блоки, образующие замыкания, в Ruby очень удобны и используются постоянно. При этом переменные не требуют отдельного объявления (подобного var в других языках), а определяются в момент инициализации – первого присваивания значения. Все это может привести к недопониманию и кажущейся неоднозначности.
Из чего состоит контекст?
В Ruby в любой точке программы мы имеем доступ к трем слоям контекста: локальный контекст, контекст объекта и глобальный. Рассмотрим их, так сказать, сверху вниз – от глобального к локальному.
В глобальном контексте, строго говоря, находятся только глобальные переменные – это те, имена которых начинаются с символа «$». Однако мы же можем обращаться к другим элементам – константам, методам, – находясь в глобальном окружении – непосредственно в тексте исходного файла вне всяких class и def? Можем, но только потому, что на самом деле находимся в неявном безымянном методе неявного объекта main. А «глобальные» константы и методы на самом деле принадлежат классу Object, к которому относится и main (поскольку от этого класса наследуются все остальные его элементы и доступны в любом контексте). Строго говоря, начиная с Ruby 1.9 это не совсем так – существует класс BasicObject, являющийся не наследником, а предком Object. Если мы для каких-то целей «унаследуемся» непосредственно от него, то внезапно обнаружим, что нам очень мало что доступно. Но так делать имеет смысл только в очень специфических задачах, на грани «хака».
Контекст объекта позволяет нам обращаться к его методам и константам класса без указания самого объекта, а также к его переменным экземпляра с префиксом «@» и переменным класса с «@@». Сам же текущий объект мы всегда можем получить посредством ключевого слова «self».
Наконец, локальный контекст – это все локальные переменные, заданные выше по тексту в рамках текущего метода.
Одна из особенностей Ruby – то, что принадлежность идентификатора тому или иному контексту, как правило, можно определить, не просматривая снизу вверх области видимости – глобальные переменные, переменные экземпляра и класса отличаются префиксами, имена констант всегда начинаются с большой буквы, а локальных переменных – с маленькой. Некоторую сумятицу вносят только методы – обладая именами, как у локальных переменных, они принадлежат контексту объекта. Тут действует простое правило: присваивание создает переменную и перекрывает имя метода. Тем не менее к нему по-прежнему можно обратиться посредством «self.(имя)». Стоит заметить, что присваивание всегда создает переменную, даже если у нас ранее определен атрибут, доступный для него. То есть в ситуации [1]:
class Alpha
attr_accessor :alpha
def beta
self.alpha = 1
alpha = 2
end
end
Атрибут после вызова beta будет равен единице, поскольку строчка без self к нему отношения не имеет.
Статью целиком читайте в журнале «Системный администратор», №1-2 за 2014 г. на страницах 111-115.
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|