Что такое таблица имен (symbol table) в Perl?

Для каждого пакета в Perl создается таблица имен. Таблица имен представляет собой хеш, имя которого совпадает с именем пакета. Ключами этого хеша являются имена глобальных переменных (а так же функций, указателей файлов и т.п.), определенных в текущем пакете. Значения хеша таблицы имен - переменные типа typeglob, которые содержат значения объявленных глобальных переменных.

Новая запись в таблице имен создается в тот момент, когда происходит объявление новой
глобальной переменной. Лексические переменные и "локальные" функции в таблицу не вносятся.
Если для текущего кода пакет не объявлен (директивой package), запись будет помещена в таблицу имен %main:: .

В таблицу имен пакета, отличного от main, могут включаться только те идентификаторы, имена которых начинаются с буквы или символа подчеркивания.

Таблица имен существует только в момент работы интерпретатора, и хранится в памяти компьютера. Таблицы имен используются для доступа к используемым пакетам и их данным. Любой perl-скрипт может напрямую обращаться к содержимому таблиц имен.

Справка: Что такое пакет main?

Main - это пакет верхнего уровня.

Perl поддерживает два специальных имени пакетов:

  • main - пространство имен главной программы. Используется по умолчанию.
  • CORE - пространство имен встроенных функций.

Через main можно получить доступ ко всем используемым в программе пакетам.

Справка: Что такое typeglob?

typeglob - это особенный, внутренний тип данных в perl. Принадлежность к этому типу данных обозначается префиксом "*" . Префикс "*" можно рассматривать как метасимвол, вместо которого может стоять любой из префиксов "$", "@", "%", "&". Т.е. переменная типа typeglob может содержать в себе что угодно.

Переменную типа typeglob можно представить как небольшой хеш, ключами которого выступают имена типов данных. В зависимости от типа данных, размещенных в переменной typeglob, для их извлечения можно обратиться к соответствующим полям.

Для получения данных из записей таблицы имен можно использовать поля:

  • PACKAGE - имя пакета, в котором определен текущий идентификатор (имя переменной).
  • NAME - имя идентификатора.
  • SCALAR - ссылка на скалярную переменную идентификатора (если это скалярная переменная).
  • ARRAY - ссылка на массив (если переменная является массивом).
  • HASH - ссылка на хеш (если переменная является хешом).
  • CODE - ссылка на подпрограмму (если идентификатор был создан при объявлении функции).
  • IO, FILEHANDLE - Ссылка на указатель файла.
  • GLOB - Ссылка на typeglob для идентификатора.

Пример 1:

Любая глобальная переменная помещается в пространство имен своего пакета. Можно получить доступ к ее значению, используя таблицу имен. В таблице имен она будет иметь тип данных typeglob, поэтому обращаться к ней нужно через префикс "*".

Пример 2:

Примеры кода

Пример 1

Вывод содержимого пакета main.

Результат:

Пример 2

Допустим, нам хочется получить побольше информации о переменной %ENV, через таблицу имен:

Так мы получили полный список переменных окружения и их значений:

Пример 3

Очень простой пример. Объявляем глобальную переменную $count.
Т.к. этот простой скрипт не объявлен как пакет, переменная помещается в пространство имен пакета main. А потом получаем значение переменной через таблицу имен.

Результат:

В самом деле, использование полей CODE, ARRAY и др. для доступа к значению переменной совсем не обязательно. Однако, в этом случае надо точно знать, ссылку на значение какого типа вернет обращение к таблице имен. Потому что, ссылку нужно соответствующим образом разыменовать. Как это сделать, если вы не имеете никакого понятия, что подразумевается под именем переменной?

В данном примере, count мог оказаться не только скалярной переменной, но и именем массива, и функцией. Если вы работаете с переменной своего модуля - проблем не возникнет. Но если вы импортируете переменные из стороннего пакета - использовать обращение к полям CODE и пр. может оказаться удобнее.

Пример 4

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

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

Причем, системе не важно - была ли переопределяемая функция обычной функцией или методом объекта. Эта функция может даже вызываться не в самом скрипте, а где-то в глубине системы.

Что такое таблица имен (symbol table) в Perl?: 1 комментарий

  1. Andrey

    Хорошая заметка, но хотелось бы больше пояснений. Почему в main так много "мусора", как заглянуть в typeglob загруженных пакетов.

Комментарии запрещены.