В чем разница между символом ключевого слова и цитируемым символом?

В чем разница между символом ключевого слова

: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

Вы можете смешать это со значениями по умолчанию, но вы, вероятно, догадались.