В чем разница между символом ключевого слова и цитируемым символом?
В чем разница между символом ключевого слова
:foo
и цитируемый символ:
'foo
Оба выступают сами по себе и могут использоваться как идентификатор. Я вижу, что символы ключевого слова в основном используются для именованных параметров, но я спрашивал себя, невозможно ли реализовать это с использованием цитированных символов?
Другими словами: для чего нужны оба?
Ответы
Ответ 1
Во-первых: 'something
является более коротким обозначением для (quote something)
. Читатель преобразует символ кавычки в список с символом cl:quote
в качестве первого элемента. Для оценщика это означает: не оценивайте something
, просто верните его в результате.
CL-USER 22 > '(quote foo)
(QUOTE FOO)
CL-USER 23 > ''foo
(QUOTE FOO)
CL-USER 24 > (read-from-string "'foo")
(QUOTE FOO)
Двоеточие :
- это маркер пакета. Если имя пакета отсутствует, символ находится в пакете KEYWORD
.
Мы можем дать foo
значение:
CL-USER 11 > (setq foo 10)
10
foo
оценивает его значение.
CL-USER 12 > foo
10
Цитируемый символ обозначает символ.
CL-USER 13 > 'foo
FOO
Мы не можем дать :foo
значение:
CL-USER 14 > (setq :foo 10)
Error: Cannot setq :FOO -- it is a keyword.
1 (abort) Return to level 0.
2 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 15 : 1 > :top
:foo
уже имеет значение: сам.
CL-USER 16 > :foo
:FOO
Естественно, что цитируемый :foo
имеет значение :foo
.
CL-USER 17 > ':foo
:FOO
Символ foo
находится в некотором пакете, здесь CL-USER
.
CL-USER 18 > (symbol-package 'foo)
#<The COMMON-LISP-USER package, 92/256 internal, 0/4 external>
Символ :foo
находится в пакете KEYWORD
.
CL-USER 19 > (symbol-package ':foo)
#<The KEYWORD package, 0/4 internal, 6230/8192 external>
Так как :foo
- это значение :foo
, мы можем также написать:
CL-USER 20 > (symbol-package :foo)
#<The KEYWORD package, 0/4 internal, 6230/8192 external>
:foo
является аббревиатурой keyword:foo
. Таким образом, символ находится в пакете ключевых слов и экспортируется.
CL-USER 21 > keyword:foo
:FOO
Таким образом, символы ключевых слов являются символами постоянной самооценки в пакете ключевых слов. Они используются как маркеры в структурах данных и в арсеналах ключевых слов. Хорошие вещи: вам не нужно бороться с пакетами, и они сами оценивают, поэтому цитата не нужна.
Ответ 2
Различия между ключевыми словами и другими символами
Ответ Райнера Йосвига описывает символы очень хорошо. Подводя итог, каждый символ относится к пакету. p::foo
(или p:foo
, если он внешний) является символом в пакете p
. Если вы попытаетесь оценить его как форму, вы получите его значение-символ, которое вы можете установить с помощью set
или `(setf symbol-value):
CL-USER> (set 'foo 'bar)
BAR
CL-USER> foo
BAR
CL-USER> (setf (symbol-value 'foo) 'baz)
BAZ
CL-USER> foo
BAZ
Там есть специальный пакет с именем keyword
. Вы можете написать keyword::foo
, если хотите, но все символы пакета ключевых слов являются внешними, поэтому вы можете написать keyword:foo
. Поскольку они так часто используются, вы даже получаете для них специальный синтаксис: :foo
. У них также есть специальное свойство, которое вы не можете установить для себя; их значения сами:
CL-USER> :foo
:FOO
CL-USER> (symbol-value :bar)
:BAR
И это действительно все, что делает специальные символы ключевых слов сами по себе.
Ключевые слова и другие символы как имена ключевых слов в списках лямбда
Что, вероятно, более важно, так это то, что они по умолчанию используются в качестве индикаторов для "аргументов ключевого слова" в лямбда-списках. Например,
CL-USER> ((lambda (&key foo bar)
(list foo bar))
:bar 23 :foo 12)
(12 23)
Я вижу, что символы ключевых слов в основном используются для именованных параметров, но я спрашивал себя, невозможно ли реализовать это, используя цитируемые символы?
Синтаксис для лямбда-списков на самом деле позволяет вам сделать гораздо больше настроек с помощью аргументов ключевого слова. Обычным является указание значений по умолчанию:
CL-USER> ((lambda (&key (foo 'default-foo) bar)
(list foo bar))
:bar 23)
(DEFAULT-FOO 23)
Вы также можете указать имя переменной, которая привязана к логическому значению, указывающему, был ли указан параметр или нет:
CL-USER> ((lambda (&key (foo 'default-foo foo-p) (bar 'default-bar bar-p))
(format t "~{~A:~7t~A~%~}"
(list 'foo foo
'foo-p foo-p
'bar bar
'bar-p bar-p)))
:bar 23)
FOO: DEFAULT-FOO
FOO-P: NIL
BAR: 23
BAR-P: T
Полный синтаксис для 3.4.1 Обычные списки Лямбда позволяет нам делать еще больше. Это
lambda-list::= (var*
[&optional {var | (var [init-form [supplied-p-parameter]])}*]
[&rest var]
[&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
[&aux {var | (var [init-form])}*])
Обратите внимание, что вы можете указать keyword-name
. Он по умолчанию соответствует символу в пакете ключевых слов с тем же именем, что и var
, но вы можете фактически предоставить его и указать свои собственные "ключевые слова". Это может быть удобно, например, если вы хотите описательное имя ключевого слова, но не хотите иметь такое длинное имя переменной:
CL-USER> ((lambda (&key ((:home-directory dir)))
(list dir))
:home-directory "/home/me")
("/home/me")
Вы также можете использовать его для указания имен ключевых слов, которые не являются символами ключевых слов:
CL-USER> ((lambda (&key ((surprise surprise)))
(list surprise))
'surprise "hello world!")
("hello world!")
Вы также можете комбинировать эти два:
CL-USER> ((lambda (&key ((hidden-parameter secret)))
(format t "the secret is ~A" secret))
'hidden-parameter 42)
the secret is 42
Вы можете смешать это со значениями по умолчанию, но вы, вероятно, догадались.