Аннотация:
Данный материал рассчитан на более-менее опытных программистов, уже использующих другие языки программирования, но желающих понять, что же такого особенного в языке Ruby, а тех, кому вольно или невольно приходится изучать Ruby — в качестве еще одного русскоязычного источника информации о Ruby. Несколькими словами данную статью можно охарактеризовать как <глубокое погружение в Ruby для тех, кто не прочитал Programming Ruby>.
12.12.08 22:04: Перенесено модератором из 'Прочее' — der Igel
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Во-первых, в модуле нет конструктора, в котором переменные экземпляра мож-
но было бы должным образом проинициализировать. Именно поэтому приведенный
выше метод Statistis#increment_hits_count содержит первой строкой инициализацию
@hits_count на случай, если эта инициализация еще не была выполнена (в противном
случае попытка добавления к @hits_count единицы приведет к ошибке, т.к. для объекта
nil нет метода сложения с целым числом).
Как оказалось, модули могут иметь собственный метод initialize, который вызывается при создании объекта, если:
* подключивший модуль класс не определяет своего метода initialize или
* подключивший модуль класс передает в своем initialize управление базовым классам через super.
Вот пример, иллюстрирующий данное поведение:
module ModuleWithInitialization
def initialize
puts "ModuleWithInitialization#initialize"end
end
class ClassWithoutOwnInitialize
include ModuleWithInitialization
end
class ClassWithOwnInitializeAndCallingSuper
include ModuleWithInitialization
def initialize
super
end
end
class ClassWithOwnInitializeAndDenySuper
include ModuleWithInitialization
def initialize
end
end
def instantiate_object_of( klass )
puts "instantiating object of #{klass.name}"
klass.new
end
instantiate_object_of( ClassWithoutOwnInitialize )
instantiate_object_of( ClassWithOwnInitializeAndCallingSuper )
instantiate_object_of( ClassWithOwnInitializeAndDenySuper )
что приводит к печати при запуске:
instantiating object of ClassWithoutOwnInitialize
ModuleWithInitialization#initialize
instantiating object of ClassWithOwnInitializeAndCallingSuper
ModuleWithInitialization#initialize
instantiating object of ClassWithOwnInitializeAndDenySuper
Т.е., я очень серьезно ошибся и у модуля есть возможность инициализировать должным образом свои instance variables.
Оправдывает меня лишь то, что в простых случаях, когда класс неявно наследуется от Object-а, в конструкторе класса управление базовому классу через super не передается. И тогда методы initialize у подмешанных модулей вызываться не будут (см. выше случай с ClassWithOwnInitializeAndDenySuper).
Век живи, век учись. А помрешь все равно дураком
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Использование alias_method в сочетании с тем фактом, что в Ruby все методы являются виртуальными, может привести к неожиданным эффектам:
class Base
def helper
puts "Base#helper"
end
def action
helper
end
end
b = Base.new
b.action
class Derived < Base
alias_method :base_helper, :helper
def helper(what)
puts "Derived#helper(#{what})"
end
end
d = Derived.new
d.action
Здесь метод Base#helper вызывается из Base#action, но в производном классе Derived оригинальная версия helper переименовывается в base_helper. Новая же версия Derived#helper имеет другое количество параметров. Поэтому обращение к helper из Base#action приведет к ошибке:
Base#helper
-:7:in ‘helper’: wrong number of arguments (0 for 1) (ArgumentError)
from -:7:in ‘action’
from -:22
Ошибка возникает из-за того, что в Base#action выполняется вызов метода без параметров с именем helper, но в объекте d этот метод уже требует одного параметра. Если бы в Ruby методы были не виртуальными, то в Base#action вызывалась бы версия Base#helper. Однако из-за виртуальности всех методов происходит вызов именно Derived#helper. Поэтому нужно внимательно следить за сигнатурами методов, переопределяемых с помощью alias_method.
Ты же сам и сказал в конце, что все дело в виртуальности. А алиас тут совсем не при чем.
Спасибо за внимательное прочтение статьи
C>Еще неточность, не ошибка, но все-таки. C>
C>Ошибка возникает из-за того, что в Base#action выполняется вызов метода без параметров с именем helper, но в объекте d этот метод уже требует одного параметра. Если бы в Ruby методы были не виртуальными, то в Base#action вызывалась бы версия Base#helper. Однако из-за виртуальности всех методов происходит вызов именно Derived#helper. Поэтому нужно внимательно следить за сигнатурами методов, переопределяемых с помощью alias_method.
C>Ты же сам и сказал в конце, что все дело в виртуальности. А алиас тут совсем не при чем.
Да, ты прав, что в случае обычной виртуальности, без alias_method, был бы точно такой же эффект. Но я специально привел пример с alias_method потому, что в небольшой объеме статьи нужно было показать и виртуальность всех методов, и потенциальные опасности alias_method, и опасность смены прототипа метода. Вот и получился именно такой пример.
Кроме того, для меня, как для C++ программиста было сложно изначально привыкнуть, что вызов любого метода -- это отсылка сообщения и что вызов всегда разрешается в run-time. Поэтому для меня стало не то, чтобы сюрпризом, но все-таки непривычно, что при использовании alias_method при вызове метода из базового типа находится новая версия, а не старая, скрытая с помощью alias_method. Так что это был еще один повод напомнить об этой основополагающей особенности Ruby.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Спасибо за внимательное прочтение статьи
Спасибо за интересную статью
E>Да, ты прав, что в случае обычной виртуальности, без alias_method, был бы точно такой же эффект. Но я специально привел пример с alias_method потому, что в небольшой объеме статьи нужно было показать и виртуальность всех методов, и потенциальные опасности alias_method, и опасность смены прототипа метода. Вот и получился именно такой пример.
E>Кроме того, для меня, как для C++ программиста было сложно изначально привыкнуть, что вызов любого метода -- это отсылка сообщения и что вызов всегда разрешается в run-time. Поэтому для меня стало не то, чтобы сюрпризом, но все-таки непривычно, что при использовании alias_method при вызове метода из базового типа находится новая версия, а не старая, скрытая с помощью alias_method. Так что это был еще один повод напомнить об этой основополагающей особенности Ruby.
Да, это полезно напомнить. Но, к сожалению, в данном примере алиас вообще не используется, он только определяется. Ведь алиас не переименовывает метод, а добавляет еще одно имя, или я не прав?
Ну да ладно.
А вот такой вопрос: где и с какой целью вообще используется Ruby? Рельсы, я так понимаю, используются для небольших веб-приложений.
Я пишу на яве, и с появлением Java 6, где есть поддержка динамических языков, пытаюсь придумать им полезное применение, и все как-то не особо успешно. В твоей статье я нашел одно такое применение: DSL для конфигурации, это может быть очень удобно. А что еще?
Здравствуйте, Cider, Вы писали:
E>>Кроме того, для меня, как для C++ программиста было сложно изначально привыкнуть, что вызов любого метода -- это отсылка сообщения и что вызов всегда разрешается в run-time. Поэтому для меня стало не то, чтобы сюрпризом, но все-таки непривычно, что при использовании alias_method при вызове метода из базового типа находится новая версия, а не старая, скрытая с помощью alias_method. Так что это был еще один повод напомнить об этой основополагающей особенности Ruby.
C>Да, это полезно напомнить. Но, к сожалению, в данном примере алиас вообще не используется, он только определяется. Ведь алиас не переименовывает метод, а добавляет еще одно имя, или я не прав?
Сам alias_method дает методу еще один псевдоним.
Но, если alias_method используется вместе с переопределением метода, тогда происходит именно переименование (поскольку старое имя уже не указывает на старый код).
C>А вот такой вопрос: где и с какой целью вообще используется Ruby? Рельсы, я так понимаю, используются для небольших веб-приложений. C>Я пишу на яве, и с появлением Java 6, где есть поддержка динамических языков, пытаюсь придумать им полезное применение, и все как-то не особо успешно. В твоей статье я нашел одно такое применение: DSL для конфигурации, это может быть очень удобно. А что еще?
). Еще один пример Ruby DSL в качестве make-утилиты -- Rake, только Rake является полным аналогом универсального make, в то время как Mxx_ru затачивается под конкретные типы проектов;
* DSL для генерации конфигурационных файлов системы мониторинга (когда несколько строк на Ruby разворачиваются в десятки конфигурационных файлов с описанием расположения и отображения сотен параметров). Реализован на основе RuCodeGen
;
* инструменты для установки, запуска и контроля за приложениями на серверах;
* инструменты для импорта информации в БД, анализа, архивирования и удаления log- и протокольных файлов;
* инструменты для построения отчетов на основании информации из БД.
Одно время я пользовался таким инструментом, как webgen, который позволяет генерировать статические веб-сайты из простых текстовых описаний (но затем в развитии webgen был длительный застой, из-за которого я перешел на питоновый rest2web). Такие задачки так же, имхо, вполне подходят для языков типа Ruby/Python/Perl.
Вообще, например, в задачах по администрированию чего-нибудь, Ruby очень удобен из-за наличия исходных текстов (так же, как и Python и Perl). Как правило, на каком-нибудь из серверов обязательно обнаруживается что-нибудь нестандартное. Правка и проверка исправлений в Ruby-программке занимает секунды, даже у непрограммистов. Скомпилированный Java или C++ код в таких условиях менее выгоден.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Сам alias_method дает методу еще один псевдоним. E>Но, если alias_method используется вместе с переопределением метода, тогда происходит именно переименование (поскольку старое имя уже не указывает на старый код).
Ага, логично. Хотя в данном примере все это... Ладно, хватит зудеть
E>Вообще об использовании Ruby можно узнать, например, здесь: http://www.ruby-lang.org/en/documentation/success-stories/
E>Я же могу рассказать только о том, как Ruby используется у нас:
.....
Здравствуйте, Cider, Вы писали:
E>>Сам alias_method дает методу еще один псевдоним. E>>Но, если alias_method используется вместе с переопределением метода, тогда происходит именно переименование (поскольку старое имя уже не указывает на старый код).
C>Ага, логично. Хотя в данном примере все это... Ладно, хватит зудеть
Нет, все нормально. Спасибо за акцентирование на этом моменте. Если когда-нибудь я сподоблюсь на развитие RubyEdges, то обязательно учту твои замечания.
E>>Вообще об использовании Ruby можно узнать, например, здесь: http://www.ruby-lang.org/en/documentation/success-stories/
E>>Я же могу рассказать только о том, как Ruby используется у нас: C>.....
C>Еще столько мегабайт мудрости... Буду впитывать.
Собственно, в Ruby меня превлекает именно наличие блоков кода, свободного синтаксиса и принцип того, что все есть объект. Эти возможности превращаю Ruby в отличный инструмент для DSL-лей.
В остальном же Ruby делит нишу с Python-ом и Perl-ом (только пока отстает от них в производительности). Так что области применения Ruby сильно пересекаются с таковыми для Python/Perl и наоборот. А уже выбор между ними обуславливается, во многом, личными эстетическими предпочтениями (имхо).
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Очень интересная статья. Только мне кажется в начале стоило чуть больше внимания уделить синтаксису языка. Лично я большую часть примеров понимал сразу, но это лишь потому, что я уже успел ознакомиться с синтаксисом Руби.
И кстати поправьте нумерацию сносок, а то у вас две сноски под номером "1" и по тексту из-за этого возникает путаница
Здравствуйте, Smasher, Вы писали:
S>Очень интересная статья.
Спасибо за потраченное на ее чтение время и лестный отзыв.
S>Только мне кажется в начале стоило чуть больше внимания уделить синтаксису языка. Лично я большую часть примеров понимал сразу, но это лишь потому, что я уже успел ознакомиться с синтаксисом Руби.
Может быть. Вероятно, злую шутку сыграли два фактора:
* мне, как привыкшему к Ruby программисту, синтаксис показался слишком малозначительной темой;
* не смотря на то, что статья получилась очень большой, в нее все равно что-то из отличительных черт Ruby не попало. Ну нельзя объять необъятное
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Несколько раз штурмовал Programming Ruby, но бросал, даже не дойдя до середины А вот эта статья "проглотилась" буквально за 2 вечера и уже пишу небольшие штучки. Так что спасибо за статью.
Вопросы можно?
Вопрос номер раз. "ri ftools" говорит
Nothing known about ftools
ruby-doc-stdlib у меня выкачан и инсталлирован, но, похоже, stdlib можно смотреть только браузером, так? Из браузера доступ к ri получить можно?
Вопрос номер тфа. Я выкачал некий gem. Как его установить, не имея доступа к сети? Собственно, я понимаю, что самый лёгкий и безболезненный способ это "gem install coolest-ruby-gem-ever", но по вышеуказанным причинам дома я эту магическую команду повторить не могу. Скорее всего, здесь маячит более общая задача — создать на нескольких машинах одинаковое окружение, туда в частности входит одинаковый набор библиотек. Я попытался наивно: 1 в 1 перенёс с одной машины на другую весь каталог ruby, но уже на новой машине "irb -rubygem" выдаёт ошибку. Как ты решаешь задачу синхронизации окружениий?
Вопрос номер тры. Очевидно, что результат выполнения программы состоящей из нескольких файлов зависит от порядка выполнения файлов. Я так понимаю, порядок задаётся с помощью команд reqire и load. Но тогда очень вероятна ситуация, что при большом количестве файлов возникнет зоопарк, как с заголовочными файлами в C++ — добавление, удаление, а то и просто перестановка директив #include может изменить результат компиляции и логику программы. Правда ли, что я сгущаю краски, и на самом деле такой ситуации в Руби не бывает?
Вопрос номер четыре. (Типа REPL в Руби) Как мне в irb загрузить какой-нибудь руби-файл (несколько файлов) и выполнить кусок из него (них)? Несмотря на то, что можно попереключаться в другую консольку для "ri", возможно ли посмотреть справку прямо из irb? Как вылить все введённые в irb команды в файл (вот я формировал-формировал состояние, создавал-создавал объекты, писал-писал классики, как это теперь не потерять)? Что означает приглашение ввода у irb:
irb(main):001:0> irb.help # думал справка выплывет, а произошёл совсем другой эффект
irb#1(main):001:0>
Оно ещё хитро меняется... Что означает этот #1? (Я конечно догадываюсь интуитивно, но хочется точной информации из первых рук). Вообще, у этого irb есть help? "ri IRB::irb" выдаёт какую-то муру, и гугл тоже не блещет, самое лучшее нашлось это: ruby-and-irb
PS1:
Конечно вопросы непосредственно к статье отношения не имеют, в следующий раз создам спец. топик в "ДП", но первая пачка пусть уж здесь лежит.
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>Несколько раз штурмовал Programming Ruby, но бросал, даже не дойдя до середины А вот эта статья "проглотилась" буквально за 2 вечера и уже пишу небольшие штучки. Так что спасибо за статью.
Спасибо за столь лестный отзыв
LCR>Вопросы можно?
Можно. Только сразу хочу предупредить, что где-то с осени прошлого года я практически безвылазно сижу в C++ и Ruby пользуюсь по чуть-чуть и эпизодически. В основном своих подчиненных заставляю им пользоваться
Так что какие-то вещи уже вылетели из памяти.
LCR>Вопрос номер раз. "ri ftools" говорит LCR>
LCR>Nothing known about ftools
LCR>ruby-doc-stdlib у меня выкачан и инсталлирован, но, похоже, stdlib можно смотреть только браузером, так? Из браузера доступ к ri получить можно?
Практически никогда не пользовался ri. Поскольку и достаточно тормознутая штука (поищи такой инструмент: fast-ri, он аналогичен ri, но работает на порядки быстрее и умеет искать по gem-ам; кажется, его анонс я делал в форуме tools) и результатами пользоваться неудобно.
Поэтому я скачал документацию по core-api и stdlib с ruby-doc.org и пользуюсь браузером. В Opera по core-api вообще очень просто поиск делать -- нажимаешь /, затем вводишь слово -- и Opera тебе его автоматом подсвечивает. Плюс к тому и браузера проще копи-пастить и открывать несколько закладок. А еще иногда попадается документация, в которую включены исходные тексты методов -- тогда в браузере кликаешь на ссылочку и исходный текст показывается. Очень удобно.
Так что мой тебе совет -- забудь про ri.
LCR>Вопрос номер тфа. Я выкачал некий gem. Как его установить, не имея доступа к сети? Собственно, я понимаю, что самый лёгкий и безболезненный способ это "gem install coolest-ruby-gem-ever", но по вышеуказанным причинам дома я эту магическую команду повторить не могу.
Здесь все тривиально. Способ номер раз: указание имени файла gem-а. Например, если у меня в текущем каталоге лежит Mxx_ru-1.4.5.gem, то я даю команду:
gem install Mxx_ru-1.4.5.gem
Тогда rubygems сам понимает, что нужно брать локальную копию, а не лезть в интернет.
Способ номер два (должен работать, но не проверял): запуск gem install с опцией -l (--local). Тогда он будет искать gem-ы у тебя на компьютере, а не в централизированном репозитории. Т.е. что-то вроде:
gem install -l Mxx_ru
Вообще, мне часто помогает запуск gem с командой help. Например:
gem help install
выдает справку по процедуре install.
LCR>Скорее всего, здесь маячит более общая задача — создать на нескольких машинах одинаковое окружение, туда в частности входит одинаковый набор библиотек. Я попытался наивно: 1 в 1 перенёс с одной машины на другую весь каталог ruby, но уже на новой машине "irb -rubygem" выдаёт ошибку. Как ты решаешь задачу синхронизации окружениий?
Вообще странно, что простое копирование не помогло. Может быть, не были правильно выставлены переменные окружения PATH и RUBYOPT на второй машине?
LCR>Вопрос номер тры. Очевидно, что результат выполнения программы состоящей из нескольких файлов зависит от порядка выполнения файлов. Я так понимаю, порядок задаётся с помощью команд reqire и load. Но тогда очень вероятна ситуация, что при большом количестве файлов возникнет зоопарк, как с заголовочными файлами в C++ — добавление, удаление, а то и просто перестановка директив #include может изменить результат компиляции и логику программы. Правда ли, что я сгущаю краски, и на самом деле такой ситуации в Руби не бывает?
Теоритически, такая ситуация возможна. На практике я с таким не сталкивался. Может быть из-за того, что таких больших программ на руби еще не создают
Кроме того, обычно через require ты подключаешь библиотеки, которые не выполняют чего-нибудь серьезного сами по себе.
LCR>Вопрос номер четыре. (Типа REPL в Руби) Как мне в irb загрузить какой-нибудь руби-файл (несколько файлов) и выполнить кусок из него (них)? Несмотря на то, что можно попереключаться в другую консольку для "ri", возможно ли посмотреть справку прямо из irb?
Вот в irb я не гуру. Что знаю расскажу, но не более того.
Загрузка выполняется обычным образом: через require или load.
Выполнить часть -- это вряд ли. Разве что вызвать метод из ранее загруженного через require/load файла.
Справку -- в консольном irb не получится. Под Windows я видел такого зверя, как fx-ri (если не ошибаюсь в названии). Там на FOX Toolkit-е (библиотек FxRuby) объединили в одном флаконе ri и irb. Ставилась, кажется, в составе One-Click-Installer. Но работала эта штука пару лет назад весьма неустойчиво.
LCR>Как вылить все введённые в irb команды в файл (вот я формировал-формировал состояние, создавал-создавал объекты, писал-писал классики, как это теперь не потерять)?
Я простого способа не нашел, поэтому вместо irb я использовал vim в возможностью запуска из vim интерпритатора и просмотра результата работы программы.
LCR>Что означает приглашение ввода у irb: LCR>
LCR>irb(main):001:0> irb.help # думал справка выплывет, а произошёл совсем другой эффект
LCR>irb#1(main):001:0>
LCR>
LCR>Оно ещё хитро меняется... Что означает этот #1? (Я конечно догадываюсь интуитивно, но хочется точной информации из первых рук).
А это такая интересная фича irb, как subsessions. Т.е. как бы дочерние процессы irb в той же консоли запускаются. Как раз #1 указывает, что работа идет не в основной сессии, а в первой дочерней. Стартуют новые сессии через команду irb. Поэтому похоже, что irb.help была воспринята как попыка запусить дочернюю сессию, а затем выполнить метод help для результата этой дочерней сессии. Просмотреть список сессий можно через команду jobs, а переключаться между сессиями через fg <номер>.
LCR>Вообще, у этого irb есть help? "ri IRB::irb" выдаёт какую-то муру, и гугл тоже не блещет, самое лучшее нашлось это: ruby-and-irb
Самое лучше описание irb как раз во втором издании Programming Ruby, глава 15. Ее можно просмотреть как справочник.
Кстати, там же есть раздел Extending irb, в котором написано:
Because the things you type to irb are interpreted as Ruby code, you can effectively
extend irb by defining new top-levelmethods. For example, you may want to be able to
look up the documentation for a class or method while in irb. If you add the following to
your .irbrc file, you’ll add amethod called ri, which invokes the external ri command
on its arguments.
The next time you start irb, you’ll be able to use this method to get documentation.
irb(main):001:0> ri Proc
----------------------------------------------------- Class:Proc
Proc objects are blocks of code that have been bound to a set of
local variables. Once bound, the code may be called in different
contexts and still access those variables.
Женя,
E>Поэтому я скачал документацию по core-api и stdlib с ruby-doc.org и пользуюсь браузером. В Opera по core-api вообще очень просто поиск делать -- нажимаешь /, затем вводишь слово -- и Opera тебе его автоматом подсвечивает.
Вот, классно. Я, кстати, тоже как раз Оперу люблю — будет в самый раз.
E>Вообще странно, что простое копирование не помогло. Может быть, не были правильно выставлены переменные окружения PATH и RUBYOPT на второй машине?
PATH точно был настроен. RUBYOPT — ну... нет
Ну остальные вопросы я не цитировал потому что на них ты полностью ответил, даже не знаю, чего спросить ещё
во всяком случае это сильно зависит от компилятора. По идее хороший компилятор должен "сворачивать"
повторяющиеся константы в модуле. Может они в Ruby имеют свою собственную сермяжность но например в JS это так.
По поводу Symbol.
Технически Symbol есть целое число — индекс данной строки в глобальной таблице. Фактически это то что известно под термином String interning. Определение значения Symbol производится в compile time. В runtime они эффективны, сравнение символов например — это сравнение двух целых — очень дешево. Концептуально (для С++ людей) все Symbols попадают в один такой большой enum.
Именно Symbol используются в качестве ключей в таблицах имя/метод классов.
CS>во всяком случае это сильно зависит от компилятора. По идее хороший компилятор должен "сворачивать" CS>повторяющиеся константы в модуле. Может они в Ruby имеют свою собственную сермяжность но например в JS это так.
Я уже не помню JS, может там строки иммутабельные. А вот в Ruby строки мутабельные и значит s2 и s3 могут подвергнуться модификации независимо друг от друга. Какой-нибудь интерпритатор Ruby может применять COW, но все равно это должно означать, что s2 и s3 -- это разные объекты со своими уникальными идентификаторами. Даже если их внутренности в какое-то время ссылаются на одно и то же значение "AAA".
CS>По поводу Symbol.
CS>Технически Symbol есть целое число — индекс данной строки в глобальной таблице. Фактически это то что известно под термином String interning. Определение значения Symbol производится в compile time. В runtime они эффективны, сравнение символов например — это сравнение двух целых — очень дешево. Концептуально (для С++ людей) все Symbols попадают в один такой большой enum. CS>Именно Symbol используются в качестве ключей в таблицах имя/метод классов.
Дело в том, что в Ruby 1.8.*, как мне помнится, не было явной стадии compile time. Где-то я читал, что Ruby даже синтаксический анализ кода не полностью производит. Т.е. какой-то первичный разбор производился, но получались вовсе не инструкции виртуальной машины, а некий полуфабрикат. Который затем уже интерпритировался. Но здесь ты наверняка больший гуру, чем я.
Так вот, возвращаясь к Symbol. Они в Ruby существуют не только в compile-time, но и могут создаваться в run-time (например, через string interpolation). И, опять же где-то я читал, что для Symbol-ов существует в Ruby 1.8.* один большой глобальный Hash-map. Появление нового Symbol-а в программе приводит к пополнению этого Hash-map-а. Более того, данный глобальный hash-map никогда не уменьшается в размерах, т.е. Symbol объекты не уничтожаются сборщиком мусора (по крайней мере в 1.8). Поэтому встречались даже рекомендации не генерировать в процессе работы программы новух Symbol-ов (некоторе фреймворки пытались идентификаторы сессий/запросов представлять в виде Symbol-ов).
Поэтому я рассматриваю Symbol не как целочисленный enum. А как hash, по которому в глобальном hash-map-е находится объект Symbol. Что, в общем-то, почти то же самое, но чуть дороже простого enum-а.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[3]: Ruby-новые грани
От:
Аноним
Дата:
29.07.08 07:26
Оценка:
Здравствуйте, eao197, Вы писали:
CS>>Технически Symbol есть целое число — индекс данной строки в глобальной таблице. Фактически это то что известно под термином String interning. Определение значения Symbol производится в compile time. В runtime они эффективны, сравнение символов например — это сравнение двух целых — очень дешево. Концептуально (для С++ людей) все Symbols попадают в один такой большой enum. CS>>Именно Symbol используются в качестве ключей в таблицах имя/метод классов.
E>Дело в том, что в Ruby 1.8.*, как мне помнится, не было явной стадии compile time. Где-то я читал, что Ruby даже синтаксический анализ кода не полностью производит. Т.е. какой-то первичный разбор производился, но получались вовсе не инструкции виртуальной машины, а некий полуфабрикат. Который затем уже интерпритировался. Но здесь ты наверняка больший гуру, чем я.
E>Так вот, возвращаясь к Symbol. Они в Ruby существуют не только в compile-time, но и могут создаваться в run-time (например, через string interpolation). И, опять же где-то я читал, что для Symbol-ов существует в Ruby 1.8.* один большой глобальный Hash-map. Появление нового Symbol-а в программе приводит к пополнению этого Hash-map-а. Более того, данный глобальный hash-map никогда не уменьшается в размерах, т.е. Symbol объекты не уничтожаются сборщиком мусора (по крайней мере в 1.8). Поэтому встречались даже рекомендации не генерировать в процессе работы программы новух Symbol-ов (некоторе фреймворки пытались идентификаторы сессий/запросов представлять в виде Symbol-ов).
E>Поэтому я рассматриваю Symbol не как целочисленный enum. А как hash, по которому в глобальном hash-map-е находится объект Symbol. Что, в общем-то, почти то же самое, но чуть дороже простого enum-а.
Кажется, на самом деле все есть именно так, как говорит c-smile: Symbol — это псевдообъект типа Fixnum (т.е. существует однозначное отображение Symbol <=> object_id, как и для Fixnum).
c-smile прав, опять же, "...фактически это то что известно под термином String interning" — это понятно хотя бы по тому, что метод преобразования строки в символ называется — String.intern
Здравствуйте, Аноним, Вы писали:
E>>Поэтому я рассматриваю Symbol не как целочисленный enum. А как hash, по которому в глобальном hash-map-е находится объект Symbol. Что, в общем-то, почти то же самое, но чуть дороже простого enum-а.
А>Кажется, на самом деле все есть именно так, как говорит c-smile: Symbol — это псевдообъект типа Fixnum (т.е. существует однозначное отображение Symbol <=> object_id, как и для Fixnum).
А>c-smile прав, опять же, "...фактически это то что известно под термином String interning" — это понятно хотя бы по тому, что метод преобразования строки в символ называется — String.intern
Наверняка в действительности все совсем не так, как на самом деле. Поэтому не буду утверждать, что я прав.
Тем не менее, глобальная хеш-таблица со строковыми значениями Symbol-ов должна присутствовать.
А>//ЗХ
Если ты -- это ЗХ, то рад тебя слышать!
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Так вот, возвращаясь к Symbol. Они в Ruby существуют не только в compile-time, но и могут создаваться в run-time (например, через string interpolation). И, опять же где-то я читал, что для Symbol-ов существует в Ruby 1.8.* один большой глобальный Hash-map. Появление нового Symbol-а в программе приводит к пополнению этого Hash-map-а. Более того, данный глобальный hash-map никогда не уменьшается в размерах, т.е. Symbol объекты не уничтожаются сборщиком мусора (по крайней мере в 1.8). Поэтому встречались даже рекомендации не генерировать в процессе работы программы новух Symbol-ов (некоторе фреймворки пытались идентификаторы сессий/запросов представлять в виде Symbol-ов).
Это может быть hash-map. Может быть и нечто иное как скажем в моем случае используется ternary-trie.
E>Поэтому я рассматриваю Symbol не как целочисленный enum. А как hash, по которому в глобальном hash-map-е находится объект Symbol. Что, в общем-то, почти то же самое, но чуть дороже простого enum-а.
Угу, и если в двух словах то Symbol это т.н. perfect hash value: