Почему Clojure имеет "ключевые слова" в дополнение к "символам"?
У меня есть мимолетные знания о других Лиспах (особенно Схеме) с давних времен. Недавно я читал о Clojure. Я вижу, что у этого есть и "символы" и "ключевые слова". Символы, с которыми я знаком, но не с ключевыми словами.
У других Лиспов есть ключевые слова? Чем ключевые слова отличаются от символов, кроме обозначений (например, двоеточия)?
5 ответов
Вот документация Clojure для ключевых слов и символов.
Ключевые слова - это символические идентификаторы, которые оценивают сами себя. Они обеспечивают очень быстрые тесты на равенство...
Символы - это идентификаторы, которые обычно используются для обозначения чего-то другого. Они могут использоваться в формах программы для ссылки на параметры функций, привязки, имена классов и глобальные переменные...
Ключевые слова обычно используются в качестве облегченных "константных строк", например, для ключей хэш-карты или значений диспетчеризации мультиметода. Символы обычно используются для именования переменных и функций, и реже манипулировать ими как объектами напрямую, за исключением макросов и тому подобного. Но ничто не мешает вам использовать символ везде, где вы используете ключевое слово (если вы не возражаете цитировать их постоянно).
Самый простой способ увидеть разницу - это прочитать Keyword.java
а также Symbol.java
в источнике Clojure. Есть несколько очевидных различий в реализации. Например, у символа в Clojure могут быть метаданные, а у ключевого слова - нет.
В дополнение к синтаксису из одной двоеточия вы можете использовать двойное двоеточие для создания ключевого слова, квалифицированного в пространстве имен.
user> :foo
:foo
user> ::foo
:user/foo
Common Lisp имеет ключевые слова, как и Ruby и другие языки. Они немного отличаются в этих языках, конечно. Некоторые различия между ключевыми словами Common Lisp и ключевыми словами Clojure:
Ключевые слова в Clojure не являются символами.
user> (symbol? :foo) false
Ключевые слова не принадлежат ни одному пространству имен, если вы не определите их специально:
user> (namespace :foo) nil user> (namespace ::foo) "user"
(Спасибо Rainer Joswig за то, что он дал мне идеи о том, на что посмотреть.)
Common Lisp имеет ключевые слова.
Ключевые слова тоже символы.
(symbolp ':foo) -> T
Что делает ключевые слова особенными:
- : foo анализируется читателем Common Lisp как ключевое слово символа:: foo
- ключевые слова оценивают сами::foo ->:foo
- домашний пакет символов ключевых слов - это пакет KEYWORD: keyword:foo ->:foo
- ключевые слова экспортируются из пакета KEYWORD
- ключевые слова являются константами, нельзя назначать другое значение
В противном случае ключевые слова являются обычными символами. Таким образом, ключевые слова могут называть функции или иметь списки свойств.
Помните: в Common Lisp символы принадлежат пакету. Это можно записать как:
- foo, когда символ доступен в текущем пакете
- foo: bar, когда символ FOO экспортируется из пакета BAR
- foo:: bar, когда символ FOO находится в пакете BAR
Для символов ключевых слов это означает, что: foo, keyword: foo и keyword:: foo - это один и тот же символ. Таким образом, последние два обозначения обычно не используются.
Итак: foo просто анализируется как находящийся в пакете KEYWORD, предполагая, что отсутствие имени пакета перед именем символа означает по умолчанию пакет KEYWORD.
Ключевые слова являются символами, которые оценивают сами себя, поэтому вам не нужно не забывать цитировать их.
: ключевые слова также специально обрабатываются во многих коллекциях, что позволяет использовать действительно удобный синтаксис.
(:user-id (get-users-map))
такой же как
((get-users-map) :user-id)
это делает вещи немного более гибкими
Для ключевых слов хэш-значения вычисляются и кэшируются при первом создании ключевого слова. При поиске ключевого слова в качестве хеш-ключа оно просто возвращает предварительно вычисленное хеш-значение. Для строк и символов хеш пересчитывается при каждом поиске.
Почему ключевые слова с одинаковыми именами всегда идентичны, они содержат свои собственные значения хеш-функции. Поскольку поиск по картам и наборам производится из хеш-ключей, это повышает эффективность поиска в случае многочисленных поисков, а не в самом поиске.
Ключевые слова глобальны, символы - нет.
Этот пример написан на JavaScript, но я надеюсь, что он поможет донести мысль.
const foo = Symbol.for(":foo") // this will create a keyword
const foo2 = Symbol.for(":foo") // this will return the same keyword
const foo3 = Symbol(":foo") // this will create a new symbol
foo === foo2 // true
foo2 === foo3 // false
Когда вы создаете символ с помощью Symbol
функции вы каждый раз получаете отдельный / частный символ. Когда вы запрашиваете символ черезSymbol.for
функция, вы будете возвращать один и тот же символ каждый раз.
(println :foo) ; Clojure
System.out.println(RT.keyword(null, "foo")) // Java
console.log(System.for(":foo")) // JavaScript
Все они одинаковы.
Имена аргументов функции являются локальными. т.е. не ключевые слова.
(def foo (fn [x] (println x))) ; x is a symbol
(def bar (fn [x] (println x))) ; not the same x (different symbol)