9.3 Создание и интернирование символов

Чтобы понять, как создаются символы в GNU Emacs Lisp, необходимо знать, как Lisp читает их. Lisp должен гарантировать, что он находит один и тот же символ каждый раз, когда читает одну и ту же последовательность символов в одном и том же контексте. Невыполнение этого требования привело бы к полной путанице.

Когда читатель Lisp встречает имя, ссылающееся на символ в исходном коде, он ищет это имя в таблице, называемой обаррей, чтобы найти символ, который имел в виду программист. Обаррей — это неупорядоченный контейнер символов, индексированный по именам.

Читатель Lisp также учитывает «сокращения». Если программист их предоставил, это позволяет читателю найти символ, даже если его имя не присутствует в полной форме в исходном коде. См. Сокращения.

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

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

Интернирование обычно происходит автоматически в читателе, но иногда другие программы могут захотеть сделать это. Например, после того как команда M-x получает имя команды в виде строки с помощью минибуфера, она затем интернирует строку, чтобы получить интернированный символ с этим именем. В качестве другого примера, гипотетическая программа телефонной книги могла бы интернировать имя каждого искомого человека как символ, даже если обаррей не содержал его, чтобы она могла прикрепить информацию к этому новому символу, например, время последнего обращения к нему.

Ни один обаррей не содержит всех символов; на самом деле, некоторые символы не находятся ни в одном обаррее. Они называются неинтернированными символами. Неинтернированный символ имеет те же четыре ячейки, что и другие символы; однако единственный способ получить доступ к нему — найти его в каком-либо другом объекте или как значение переменной. Неинтернированные символы иногда полезны при генерации кода Lisp, см. ниже.

Примечание Common Lisp: В отличие от Common Lisp, Emacs Lisp не предусматривает интернирование одного и того же имени в нескольких разных «пакетах», таким образом создавая несколько символов с одинаковым именем, но разными пакетами. Emacs Lisp предоставляет другую систему пространства имен, называемую «сокращениями» (см. Сокращения).

Функция: obarray-make &optional size

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

Функция: obarrayp object

Эта функция возвращает t, если object является обарреем, и nil в противном случае.

Большинство функций, приведенных ниже, принимают имя и, иногда, обаррей в качестве аргументов. Ошибка wrong-type-argument сигнализируется, если имя не является строкой или если обаррей не является объектом обаррея.

Функция: symbol-name symbol

Эта функция возвращает строку, которая является именем symbol. Например:

(symbol-name 'foo)
     ⇒ "foo"

Предупреждение: Никогда не изменяйте строку, возвращаемую этой функцией. Это может сделать Emacs нефункциональным и даже привести к аварийному завершению Emacs.

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

Функция: make-symbol name

Эта функция возвращает новый выделенный неинтернированный символ, имя которого — name (должно быть строкой). Его значение и определение функции пусты, а список свойств равен nil. В приведенном ниже примере значение sym не является eq к foo, потому что это отдельный неинтернированный символ, имя которого также «foo».

(setq sym (make-symbol "foo"))
     ⇒ foo
(eq sym 'foo)
     ⇒ nil
Функция: gensym &optional prefix

Эта функция возвращает символ, используя make-symbol, имя которого создается путем добавления gensym-counter к prefix и увеличения этого счетчика, что гарантирует, что никакие два вызова этой функции не сгенерируют символ с одинаковым именем. Префикс по умолчанию — "g".

Чтобы избежать проблем при случайном интернировании печатного представления сгенерированного кода (см. Печатное представление и синтаксис чтения), рекомендуется использовать gensym вместо make-symbol.

Функция: intern name &optional obarray

Эта функция возвращает интернированный символ, имя которого — name. Если в обаррее obarray нет такого символа, intern создает новый, добавляет его в обаррей и возвращает его. Если obarray опущен, используется значение глобальной переменной obarray.

(setq sym (intern "foo"))
     ⇒ foo
(eq sym 'foo)
     ⇒ t

(setq sym1 (intern "foo" other-obarray))
     ⇒ foo
(eq sym1 'foo)
     ⇒ nil

Примечание Common Lisp: В Common Lisp вы можете интернировать существующий символ в обаррей. В Emacs Lisp вы не можете этого сделать, потому что аргумент для intern должен быть строкой, а не символом.

Функция: intern-soft name &optional obarray

Эта функция возвращает символ в obarray, имя которого — name, или nil, если obarray не имеет символа с таким именем. Таким образом, вы можете использовать intern-soft для проверки, интернирован ли уже символ с заданным именем. Если obarray опущен, используется значение глобальной переменной obarray.

Аргумент name также может быть символом; в этом случае функция возвращает name, если name интернирован в указанном обаррее, и nil в противном случае.

(intern-soft "frazzle")        ; Такого символа не существует.
     ⇒ nil
(make-symbol "frazzle")        ; Создать неинтернированный.
     ⇒ frazzle
(intern-soft "frazzle")        ; Этот не может быть найден.
     ⇒ nil
(setq sym (intern "frazzle"))  ; Создать интернированный.
     ⇒ frazzle
(intern-soft "frazzle")        ; Этот можно найти!
     ⇒ frazzle
(eq sym 'frazzle)              ; И это тот же самый.
     ⇒ t
Переменная: obarray

Эта переменная является стандартным обарреем для использования intern и read.

Функция: mapatoms function &optional obarray

Эта функция вызывает function один раз для каждого символа в обаррее obarray. Затем она возвращает nil. Если obarray опущен, по умолчанию используется значение obarray — стандартный обаррей для обычных символов.

(setq count 0)
     ⇒ 0
(defun count-syms (s)
  (setq count (1+ count)))
     ⇒ count-syms
(mapatoms 'count-syms)
     ⇒ nil
count
     ⇒ 1871

См. documentation в Доступ к строкам документации для другого примера использования mapatoms.

Функция: unintern symbol obarray

Эта функция удаляет symbol из обаррея obarray. Если symbol на самом деле не находится в обаррее, unintern ничего не делает. Если obarray равен nil, используется текущий обаррей.

Если вы передаете строку вместо символа в качестве symbol, она обозначает имя символа. Тогда unintern удаляет символ (если есть) в обаррее, который имеет это имя. Если такого символа нет, unintern ничего не делает.

Если unintern действительно удаляет символ, он возвращает t. В противном случае он возвращает nil.

Функция: obarray-clear obarray

Эта функция удаляет все символы из obarray.