Создание и интернирование символов
Чтобы понять, как символы создаются в GNU Emacs Lisp, необходимо знать, как Lisp их считывает. Lisp должен гарантировать, что каждый раз при чтении той же последовательности символов в том же контексте он находит один и тот же символ. В противном случае это приведет к полной путанице.
Когда читатель Lisp встречает имя, ссылающееся на символ в исходном коде, он считывает все символы этого имени. Затем он ищет это имя в таблице, называемой obarray, чтобы найти символ, который имел в виду программист. Метод, используемый в этом поиске, называется «хешированием», эффективным методом поиска, при котором последовательность символов преобразуется в число, называемое «хеш-кодом». Например, вместо того, чтобы искать в телефонной книге от начала до конца при поиске Яны Джонс, вы начинаете с буквы «Я» и переходите оттуда. Это простая версия хеширования. Каждый элемент obarray — это ведро, которое содержит все символы с заданным хеш-кодом; чтобы найти заданное имя, достаточно перебрать все символы в ведре с хеш-кодом этого имени. (Такой же принцип используется для общих хеш-таблиц Emacs, но они представляют собой другой тип данных; см. Хеш-таблицы.)
При поиске имен читатель Lisp также учитывает «псевдонимы». Если программист их предоставил, это позволяет читателю найти символ, даже если его имя не присутствует в полном виде в исходном коде. Конечно, читателю нужна информация о таких псевдонимах, точно так же, как нам нужен контекст, чтобы однозначно указать на Яну Джонс, используя только имя «Яна»: это, вероятно, приемлемо среди Джонсов или когда Яна упоминалась недавно, но очень неоднозначно в любой другой ситуации. См. Псевдонимы.
Если символ с нужным именем найден, читатель использует этот символ. Если в obarray нет символа с этим именем, читатель создает новый символ и добавляет его в obarray. Поиск или добавление символа с определенным именем называется его интернированием, а сам символ называется интернированным символом.
Интернирование гарантирует, что в каждом obarray существует только один символ с любым конкретным именем. Другие символы с таким же именем могут существовать, но не в одном и том же obarray. Таким образом, читатель получает те же символы для тех же имен, если вы продолжаете чтение с тем же obarray.
Интернирование обычно происходит автоматически в процессе чтения, но иногда другие программы могут захотеть выполнить его. Например, после выполнения команды M-x программа получает имя команды в виде строки с помощью мини-буфера, а затем интернирует эту строку, чтобы получить интернированный символ с этим именем. Еще один пример — гипотетическая программа телефонной книги, которая могла бы интернировать имя каждого найденного человека в виде символа, даже если его нет в obarray, чтобы прикрепить к этому новому символу информацию, например, о последнем времени поиска.
Ни один obarray не содержит все символы; на самом деле, некоторые символы не находятся ни в одном obarray. Они называются неинтернированными символами. Неинтернированный символ имеет те же четыре ячейки, что и другие символы; однако единственный способ получить к нему доступ — найти его в каком-то другом объекте или как значение переменной. Неинтернированные символы иногда полезны при генерации кода Lisp, см. ниже.
В Emacs Lisp obarray фактически является вектором. Каждый элемент вектора — это ведро; его значение — либо интернированный символ, имя которого хешируется в это ведро, либо 0, если ведро пусто. Каждый интернированный символ имеет внутреннюю ссылку (невидимую для пользователя) на следующий символ в ведре. Поскольку эти ссылки невидимы, нет способа найти все символы в obarray, кроме как используя mapatoms (ниже). Порядок символов в ведре не имеет значения.
В пустом obarray каждый элемент равен 0, поэтому вы можете создать obarray с помощью (make-vector length 0). Это единственный допустимый способ создания obarray. Простые числа в качестве длин обычно приводят к хорошему хешированию; длины, на единицу меньшие степени двойки, также хороши.
Не пытайтесь добавлять символы в obarray самостоятельно. Это не работает — только intern может правильно добавить символ в obarray.
Примечание по Common Lisp: В отличие от Common Lisp, Emacs Lisp не предоставляет возможность интернирования одного и того же имени в нескольких разных «пакетах», тем самым создавая несколько символов с одинаковым именем, но разными пакетами. Emacs Lisp предоставляет другую систему именования, называемую «псевдонимами» (см. Псевдонимы).
Большинство функций ниже принимают имя и иногда obarray в качестве аргументов. Ошибка wrong-type-argument возникает, если имя не является строкой или если obarray не является вектором.
- Функция: symbol-name символ
-
Эта функция возвращает строку, которая является именем символ. Например:
(symbol-name 'foo) ⇒ "foo"Предупреждение: Изменение строки путем замены символов изменяет имя символа, но не обновляет obarray, поэтому не делайте этого!
Создание неинтернированного символа полезно при генерации кода Lisp, потому что неинтернированный символ, используемый в качестве переменной в генерируемом коде, не может конфликтовать с переменными, используемыми в других программах Lisp.
- Функция: make-symbol имя
-
Эта функция возвращает недавно выделенный неинтернированный символ, имя которого имя (должно быть строкой). Его значение и определение функции не определены, а список свойств —
nil. В приведенном ниже примере значениеsymне равноeqдоfoo, потому что это отдельный неинтернированный символ, имя которого также ‘foo’.(setq sym (make-symbol "foo")) ⇒ foo (eq sym 'foo) ⇒ nil
- Функция: gensym &optional префикс
Эта функция возвращает символ, используя
make-symbol, имя которого создается путем добавленияgensym-counterк префикс и увеличения этого счётчика, гарантируя, что два вызова этой функции не сгенерируют символ с одинаковым именем. Префикс по умолчанию"g".
Для предотвращения проблем при случайном интернировании печатного представления сгенерированного кода (см. Печатное представление) рекомендуется использовать gensym вместо make-symbol.
- Функция: intern имя &optional obarray
-
Эта функция возвращает интернированный символ, имя которого имя. Если такого символа нет в obarray obarray,
internсоздает новый, добавляет его в obarray и возвращает его. Если 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 вы можете интернировать существующий символ в obarray. В Emacs Lisp это невозможно, потому что аргумент функции
internдолжен быть строкой, а не символом.
- Функция: intern-soft имя &optional obarray
-
Эта функция возвращает символ в obarray, имя которого имя, или
nil, если в obarray нет символа с этим именем. Поэтому вы можете использоватьintern-softдля проверки, уже ли интернирован символ с данным именем. Если obarray опущен, используется значение глобальной переменнойobarray.Аргумент имя также может быть символом; в этом случае функция возвращает имя, если имя интернировано в указанном obarray, и в противном случае
nil.(intern-soft "frazzle") ; No such symbol exists. ⇒ nil (make-symbol "frazzle") ; Create an uninterned one. ⇒ frazzle
(intern-soft "frazzle") ; That one cannot be found. ⇒ nil(setq sym (intern "frazzle")) ; Create an interned one. ⇒ frazzle(intern-soft "frazzle") ; That one can be found! ⇒ frazzle(eq sym 'frazzle) ; And it is the same one. ⇒ t
- Переменная: obarray
Эта переменная является стандартным obarray для использования функциями
internиread.
- Функция: mapatoms функция &optional obarray
-
Эта функция вызывает функция один раз с каждым символом в obarray obarray. Затем она возвращает
nil. Если obarray опущен, по умолчанию используется значение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 символ obarray
-
Эта функция удаляет символ из obarray obarray. Если символ фактически не содержится в obarray,
uninternничего не делает. Если obarray равенnil, используется текущий obarray.Если вы предоставляете строку вместо символа в качестве символ, она представляет имя символа. Затем
uninternудаляет символ (если таковой имеется) в obarray, имеющий это имя. Если такого символа нет,uninternничего не делает.Если
uninternудаляет символ, он возвращаетt. В противном случае он возвращаетnil.
Copyright © 1990-1996, 1998-2022 Free Software Foundation, Inc.
Licensed under the GNU GPL license.
https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html