- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Программирование на языке Ruby - Хэл Фултон
Шрифт:
Интервал:
Закладка:
Проблема в том, что продолжение — «дорогая» операция. Необходимо сохранить состояние и потратить заметное время на переключение контекста. Если производительность для вас критична, прибегайте к продолжениям с осторожностью.
11.2.6. Хранение кода в виде объекта
Неудивительно, что Ruby предлагает несколько вариантов хранения фрагмента кода в виде объекта. В этом разделе мы рассмотрим объекты Proc, Method и UnboundMethod.
Встроенный класс Proc позволяет обернуть блок в объект. Объекты Proc, как и блоки, являются замыканиями, то есть запоминают контекст, в котором были определены.
myproc = Proc.new { |a| puts "Параметр равен #{а}" }
myproc.call(99) # Параметр равен 99
Кроме того, Ruby автоматически создает объект Proc, когда метод, последний параметр которого помечен амперсандом, вызывается с блоком в качестве параметра:
def take_block(x, &block)
puts block.class
x.times {|i| block[i, i*i] }
end
take_block(3) { |n,s| puts "#{n} в квадрате равно #{s}" }
В этом примере демонстрируется также применение квадратных скобок как синонима метода call. Вот что выводится в результате исполнения:
Proc
0 в квадрате 0
1 в квадрате 1
2 в квадрате 4
Объект Proc можно передавать методу, который ожидает блок, предварив имя знаком &:
myproc = proc { |n| print n, "... " }
(1..3).each(&myproc) # 1... 2... 3...
Ruby позволяет также превратить метод в объект. Исторически для этого применяется метод Object#method, который создает объект класса Method как замыкание в конкретном объекте.
str = "cat"
meth = str.method(:length)
a = meth.call # 3 (длина "cat")
str << "erpillar"
b = meth.call # 11 (длина "caterpillar")
str = "dog"
# Обратите внимание на следующий вызов! Переменная str теперь ссылается
# на новый объект ("dog"), но meth по-прежнему связан со старым объектом.
с = meth.call # 11 (длина "caterpillar")
Начиная с версии Ruby 1.6.2, можно также применять метод Module#instance_method для создания объектов UnboundMethod. С их помощью представляется метод, ассоциированный с классом, а не с конкретным объектом. Прежде чем вызывать объект UnboundMethod, нужно связать его с каким-то объектом. Результатом операции связывания является объект Method, который можно вызывать как обычно:
umeth = String.instance_method(:length)
m1 = umeth.bind("cat")
m1.call # 3
m2 = umeth.bind("caterpillar")
m2.call # 11
Явное связывание делает объект UnboundMethod интуитивно более понятным, чем Method.
11.2.7. Как работает включение модулей?
Когда модуль включается в класс, Ruby на самом деле создает прокси-класс, являющийся непосредственным родителем данного класса. Возможно, вам это покажется интуитивно очевидным, возможно, нет. Все методы включаемого модуля «маскируются» методами, определенными в классе.
module MyMod
def meth
"из модуля"
end
end
class ParentClass
def meth
"из родителя"
end
end
class ChildClass < ParentClass
include MyMod
def meth
"из потомка"
end
end
x = ChildClass.new p
p x.meth # Из потомка.
Выглядит это как настоящее наследование: все, что потомок переопределил, становится действующим определением вне зависимости от того, вызывается ли include до или после переопределения.
Вот похожий пример, в котором метод потомка вызывает super, а не просто возвращает строку. Как вы думаете, что будет возвращено?
# Модуль MyMod и класс ParentClass не изменились.
class ChildClass < ParentClass
include MyMod
def meth
"Из потомка: super = " + super
end
end
x = ChildClass.new
p x.meth # Из потомка: super = из модуля
Отсюда видно, что модуль действительно является новым родителем класса. А что если мы точно также вызовем super из модуля?
module MyMod
def meth
"Из модуля: super = " + super
end
end
# ParentClass не изменился.
class ChildClass < ParentClass
include MyMod
def meth
"Из потомка: super = " + super
end
end
x = ChildClass.new
p x.meth # Из потомка: super = из модуля: super = из родителя.
Метод meth, определенный в модуле MyMod, может вызвать super только потому, что в суперклассе (точнее, хотя бы в одном из его предков) действительно есть метод meth. А что произошло бы, вызови мы этот метод при других обстоятельствах?
module MyMod
def meth
"Из модуля: super = " + super
end
end
class Foo include MyMod
end
x = Foo.new
x.meth
При выполнении этого кода мы получили бы ошибку NoMethodError (или обращение к методу method_missing, если бы таковой существовал).
11.2.8. Опознание параметров, заданных по умолчанию
В 2004 году Ян Макдональд (Ian Macdonald) задал в списке рассылки вопрос: «Можно ли узнать, был ли параметр задан вызывающей программой или взято значение по умолчанию?» Вопрос интересный. Не каждый день он возникает, но от того не менее интересен.
Было предложено по меньшей мере три решения. Самое удачное и простое нашел Нобу Накада (Nobu Nakada). Оно приведено ниже:
def meth(a, b=(flag=true; 345))
puts "b равно #{b}, a flag равно #{flag.inspect}"
end
meth(123) # b равно 345, a flag равно true
meth(123,345) # b равно 345, a flag равно nil
meth(123,456) # b равно 456, a flag равно nil
Как видим, этот подход работает даже, если вызывающая программа явно указала значение параметра, совпадающее с подразумеваемым по умолчанию. Трюк становится очевидным, едва вы его увидите: выражение в скобках устанавливает локальную переменную flag в true, а затем возвращает значение по умолчанию 345. Это дань могуществу Ruby.
11.2.9. Делегирование или перенаправление
В Ruby есть две библиотеки, которые предлагают решение задачи о делегировании или перенаправлении вызовов методов другому объекту. Они называются delegate и forwardable; мы рассмотрим обе.
Библиотека delegate предлагает три способа решения задачи. Класс SimpleDelegator полезен, когда объект, которому делегируется управление (делегат), может изменяться на протяжении времени жизни делегирующего объекта. Чтобы выбрать объект-делегат, используется метод __setobj__.
Однако мне этот способ представляется слишком примитивным. Поскольку я не думаю, что это существенно лучше, чем то же самое, сделанное вручную, задерживаться на классе SimpleDelegator не стану.
Метод верхнего уровня DelegateClass принимает в качестве параметра класс, которому делегируется управление. Затем он создает новый класс, которому мы можем унаследовать. Вот пример создания класса Queue, который делегирует объекту Array:
require 'delegate'
class MyQueue < DelegateClass(Array)
def initialize(arg=[])
super(arg)
end
alias_method :enqueue, :push
alias_method :dequeue, :shift
end
mq = MyQueue.new
mq.enqueue(123)
mq.enqueue(234)
p mq.dequeue # 123
p mq.dequeue # 234
Можно также унаследовать класс Delegator и реализовать метод __getobj__; именно таким образом реализован класс SimpleDelegator. При этом мы получаем больший контроль над делегированием.
Но если вам необходим больший контроль, то, вероятно, вы все равно осуществляете делегирование на уровне отдельных методов, а не класса в целом. Тогда лучше воспользоваться библиотекой forwardable. Вернемся к примеру очереди:
require 'forwardable'
class MyQueue
extend Forwardable
def initialize(obj=[])
@queue = obj # Делегировать этому объекту.
end
def_delegator :@queue, :push, :enqueue
def_delegator :@queue, :shift, :dequeue
def_delegators :@queue, :clear, :empty?, :length, :size, :<<
# Прочий код...
end
Как видно из этого примера, метод def_delegator ассоциирует вызов метода (скажем, enqueue) с объектом-делегатом @queue и одним из методов этого объекта (push). Иными словами, когда мы вызываем метод enqueue для объекта MyQueue, производится делегирование методу push объекта @queue (который обычно является массивом).

