Программирование на языке Ruby
Программирование на языке Ruby читать книгу онлайн
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
class Martian < IntelligentLife @@home_planet = "Mars" #...endНо это работать не будет. Вызов
Terran.home_planet"Earth""Mars"Можно было бы вынести определение переменной класса в базовый класс, но тогда перестали бы работать определенные нами методы класса! Можно было исправить и это, перенеся определения в дочерние классы, однако тем самым губится первоначальная идея, ведь таким образом объявляются отдельные классы без какой бы то ни было «параметризации».
Мы предлагаем другое решение. Отложим вычисление переменной класса до момента выполнения, воспользовавшись методом
class_evalclass IntelligentLife def IntelligentLife.home_planet class_eval("@@home_planet") end def IntelligentLife.home_planet=(x) class_eval("@@home_planet = #{x}") end # ...endclass Terran < IntelligentLife @@home_planet = "Earth" # ...endclass Martian < IntelligentLife @@home_planet = "Mars" # ...endputs Terran.home_planet # Earthputs Martian.home_planet # MarsНе стоит и говорить, что механизм наследования здесь по-прежнему работает. Все методы и переменные экземпляра, определенные в классе
IntelligentLifeTerranMartianВ листинге 11.14 предложено, наверное, наилучшее решение. В нем используются только переменные экземпляра, а от переменных класса мы вообще отказались.
class IntelligentLife class << self attr_accessor :home_planet end # ...endclass Terran < IntelligentLife self.home_planet = "Earth" #...endclass Martian < IntelligentLife self.home_planet = "Mars" #...endputs Terran.home_planet # Earthputs Martian.home_planet # MarsЗдесь мы открываем синглетный класс и определяем метод доступа
home_planetВ качестве небольшого усовершенствования добавим еще вызов метода
privateprivate :home_planet=Сделав метод установки закрытым, мы запретили изменять значение вне иерархии данного класса. Как всегда,
privateЕсть и другие способы решения этой задачи. Проявите воображение.
11.2.5. Использование продолжений для реализации генератора
Одно из самых трудных для понимания средств Ruby — продолжение (continuation). Это структурированный способ выполнить нелокальный переход и возврат. В объекте продолжения хранятся адрес возврата и контекст выполнения. В каком-то смысле это аналог функций
setjmplongjmpМетод
callccKernelContinuationВ классе
ContinuationcallcallссcallcccallСчитайте, что продолжение — что-то вроде операции «сохранить игру» в классических «бродилках». Вы сохраняете игру в точке, где все спокойно, а потом пробуете выполнить нечто потенциально опасное. Если эксперимент заканчивается гибелью, то вы восстанавливаете сохраненное состояние игры и пробуете пойти другим путем.
Самый лучший способ разобраться в продолжениях — посмотреть фильм «Беги, Лола, беги».
Есть несколько хороших примеров того, как пользоваться продолжениями. Самые лучшие предложил Джим Вайрих (Jim Weirich). Ниже показано, как Джим реализовал «генератор» после дискуссии еще с одним программистом на Ruby, Хью Сассе (Hugh Sasse).
Идея генератора навеяна методом
suspendБиблиотека
generator
