Продолжая использовать наш сайт, вы даете согласие на обработку файлов cookie, которые обеспечивают правильную работу сайта. Благодаря им мы улучшаем сайт!
Принять и закрыть

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.

Читать: Программирование на языке Ruby - Хэл Фултон на бесплатной онлайн библиотеке Э-Лит


Помоги проекту - поделись книгой:

str2 = "Goodbye!"

def str.spell

 self.split(/./).join("-")

end

str.spell # "H-e-l-l-o-,- -w-o-r-l-d-!"

str2.spell # Ошибка!

Имейте в виду, что метод определяется для объекта, а не для переменной. Теоретически с помощью синглетных методов можно было бы создать систему объектов на базе прототипов. Это менее распространенная форма ООП без классов[5]. Основной структурный механизм в ней состоит в конструировании нового объекта путем использования существующего в качестве образца; новый объект ведет себя как старый за исключением тех особенностей, которые были переопределены. Тем самым можно строить системы на основе прототипов, а не наследования. Хотя у нас нет опыта в этой области, мы полагаем, что создание такой системы позволило бы полнее раскрыть возможности Ruby.

1.4. Динамические аспекты Ruby

Ruby — динамический язык в том смысле, что объекты и классы можно изменять во время выполнения. Ruby позволяет конструировать и интерпретировать фрагменты кода в ходе выполнения статически написанной программы. В нем есть хитроумный API отражения, с помощью которого программа может получать информацию о себе самой. Это позволяет сравнительно легко создавать отладчики, профилировщики и другие подобные инструменты, а также применять нетривиальные способы кодирования.

Наверное, это самая трудная тема для программиста, приступающего к изучению Ruby. В данном разделе мы вкратце рассмотрим некоторые следствия, вытекающие из динамической природы языка.

1.4.1. Кодирование во время выполнения

Мы уже упоминали директивы load и require. Важно понимать, что это не встроенные предложения и не управляющие конструкции; на самом деле это методы. Поэтому их можно вызывать, передавая переменные или выражения как параметры, в том числе условно. Сравните с директивой #include в языках С и C++, которая обрабатывается во время компиляции.

Код можно строить и интерпретировать по частям. В качестве несколько искусственного примера рассмотрим приведенный ниже метод calculate и вызывающий его код:

def calculate(op1, operator, op2)

 string = op1.to_s + operator + op2.to_s

 # Предполагается, что operator - строка; построим длинную

 # строку, состоящую из оператора и операндов.

 eval(string)                    # Вычисляем и возвращаем значение.

end

@alpha = 25

@beta = 12

puts calculate(2, "+",2)         # Печатается 4

puts calculate(5, "*", "@alpha") # Печатается 125

puts calculate("@beta", "**", 3) # Печатается 1728

Вот та же идея, доведенная чуть ли не до абсурда: программа запрашивает у пользователя имя метода и одну строку кода. Затем этот метод определяется и вызывается:

puts "Имя метода: "

meth_name = gets

puts "Строка кода: "

code = gets

string = %[def #{meth_name}\n #{code}\n end] # Строим строку.

eval(string) # Определяем метод.

eval(meth_name) # Вызываем метод.

Зачастую необходимо написать программу, которая могла бы работать на разных платформах или при разных условиях, но при этом сохранить общий набор исходных текстов. Для этого в языке С применяются директивы #ifdef, но в Ruby все определения исполняются. Не существует такого понятия, как «этап компиляции»; все конструкции динамические, а не статические. Поэтому для принятия решения такого рода мы можем просто вычислить условие во время выполнения:

if platform == Windows

 action1

elsif platform == Linux

 action2

else

 default_action

end

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

if platform == Windows

 def my_action

  action1

 end

 elsif platform == Linux

 def my_action

  action2

 end

 else

 def my_action

  default_action

 end

end

Таким способом мы достигаем желаемого результата, но условие вычисляется только один раз. Когда программа вызовет метод my_action, он уже будет правильно определен.

1.4.2. Отражение

В языках Smalltalk, LISP и Java реализована (с разной степенью полноты) идея рефлексивного программирования — активная среда может опрашивать структуру объектов и расширять либо модифицировать их во время выполнения.

В языке Ruby имеется развитая поддержка отражения, но все же он не заходит так далеко, как Smalltalk, где даже управляющие конструкции являются объектами. В Ruby управляющие конструкции и блоки не представляют собой объекты. (Объект Proc можно использовать для того, чтобы представить блок в виде объекта, но управляющие конструкции объектами не бывают никогда.)

Для определения того, используется ли идентификатор с данным именем, служит ключевое слово defined? (обратите внимание на вопросительный знак в конце слова):

if defined? some_var

 puts "some_var = #{some_var}"

else

 puts "Переменная some_var неизвестна."

end

Аналогично метод respond_to? выясняет, может ли объект отвечать на вызов указанного метода (то есть определен ли данный метод для данного объекта). Метод respond_to? определен в классе Object.

В Ruby запрос информации о типе во время выполнения поддерживается очень полно. Тип или класс объекта можно определить, воспользовавшись методом type (из класса Object). Метод is_a? сообщает, принадлежит ли объект некоторому классу (включая и его суперклассы); синонимом служит имя kind_of?. Например:

puts "abc".class "" # Печатается String

puts 345.class # Печатается Fixnum

rover = Dog.new

print rover.class # Печатается Dog

if rover.is_a? Dog

 puts "Конечно, является."

end

if rover.kind_of? Dog

 puts "Да, все еще собака."

end

if rover.is_a? Animal

 puts "Да, он к тому же и животное."

end

Можно получить полный список всех методов, которые можно вызвать для данного объекта. Для этого предназначен метод methods из класса Object. Имеются также его варианты private_instance_methods, public_instance_methods и т.д.

Аналогично можно узнать, какие переменные класса или экземпляра ассоциированы с данным объектом. По самой природе ООП в перечни методов и переменных включаются те, что определены как в классе самого объекта, так и во всех его суперклассах. В классе Module имеется метод constants, позволяющий получить список всех констант, определенных в модуле.

В классе Module есть метод ancestors, возвращающий список модулей, включенных в данный модуль. В этот список входит и сам данный модуль, то есть список, возвращаемый вызовом Mod.ancestors, содержит по крайней мере элемент Mod. В этот список входят не только родительские классы (отобранные в силу наследования), но и «родительские» модули (отобранные в силу включения).

В классе Object есть метод superclass, который возвращает суперкласс объекта или nil. Не имеет суперкласса лишь класс Object, и, значит, только для него может быть возвращен nil.

Модуль ObjectSpace применяется для получения доступа к любому «живому» объекту. Метод _idtoref преобразует идентификатор объекта в ссылку на него; можно считать, что это операция, обратная той, что выполняет двоеточие в начале имени. В модуле ObjectSpace есть также итератор each_object, который перебирает все существующие в данный момент объекты, включая и те, о которых иным образом узнать невозможно. (Напомним, что некоторые неизменяемые объекты небольшого размера, например принадлежащие классам Fixnum, NilClass, TrueClass и FalseClass, не хранятся в куче из соображений оптимизации.)

1.4.3. Отсутствующие методы

При вызове метода (myobject.mymethod) Ruby ищет поименованный метод в следующем порядке:

1. Синглетные методы, определенные для объекта myobject.

2. Методы, определенные в классе объекта myobject.

3. Методы, определенные в предках класса объекта myobject.

Если найти метод mymethod не удается, Ruby ищет метод с именем method_missing. Если он определен, то ему передается имя отсутствующего метода (в виде символа) и все переданные ему параметры. Этот механизм можно применять для динамической обработки неизвестных сообщений, посланных во время выполнения.

1.4.4 Сборка мусора



Поделиться книгой:

На главную
Назад