Чому у Clojure є "ключові слова" на додаток до "символів"?


130

Я знаю про інші Lisps (зокрема, схему) з дороги назад. Нещодавно я читав про Clojure . Я бачу, що в ньому є і "символи", і "ключові слова". Символи, з якими я знайомий, але не з ключовими словами.

У інших Lisps є ключові слова? Чим ключові слова відрізняються від символів, окрім того, що мають різні позначення (тобто: двокрапки)?


Відповіді:


139

Ось документація Clojure для ключових слів та символів.

Ключові слова - це символьні ідентифікатори, які оцінюють самі. Вони забезпечують дуже швидкі тести на рівність ...

Символи - це ідентифікатори, які зазвичай використовуються для позначення чогось іншого. Вони можуть бути використані у програмних формах для позначення параметрів функції, прив’язки, назв класів та глобальних змін ...

Ключові слова зазвичай використовуються як легкі "постійні рядки", наприклад, для ключів хеш-карти або значень диспетчера мультиметоду. Символи зазвичай використовуються для назви змінних та функцій, і рідше маніпулювати ними як об'єктами безпосередньо, за винятком макросів і подібних. Але ніщо не заважає вам використовувати символ скрізь, коли ви використовуєте ключове слово (якщо ви не заперечуєте їх постійно цитувати).

Найпростіший спосіб побачити різницю - це читати Keyword.javaта Symbol.javaв джерелі Clojure. Є декілька очевидних відмінностей в реалізації. Наприклад, символ у Clojure може мати метадані, а ключове слово не може.

Окрім синтаксису з однократною двокрапкою, ви можете використовувати подвійну двокрапку, щоб створити ключове слово, призначене для простору імен.

user> :foo
:foo
user> ::foo
:user/foo

У звичайного Lisp є ключові слова, як у Ruby та інших мов. Вони, звичайно, трохи відрізняються в цих мовах. Деякі відмінності між загальними ключовими словами Lisp та ключовими словами Clojure:

  1. Ключові слова в Clojure - це не символи.

    user> (symbol? :foo)  
    false
    
  2. Ключові слова не належать до жодного простору імен, якщо ви спеціально не кваліфікуєте їх:

    user> (namespace :foo)
    nil
    user> (namespace ::foo)
    "user"
    

(Дякую Райнеру Джосвігу, що дав мені уявлення про речі, на які слід подивитися.)


10
Це пояснює, у чому полягають відмінності, але не чому потрібні дві різні конструкції. Чи не міг Clojure створити щось із об'єднанням можливостей як Ключового слова, так і символу?

25
Ключові слова легкі та мають зручний синтаксис, я думаю, це майже все, що там є. Мова буде добре працювати без них, але їх приємно мати, і вони дуже широко використовуються. Ви не можете об'єднати їх здібності, оскільки Ключові слова завжди самооцінюються (тобто ви не можете використовувати їх як назви змінних чи функцій), а символи взагалі не завжди можуть бути самооцінювальними.
Брайан Карпер

1
Здається , ключові слова є більш корисними в якості ключів в HashMaps і т.д. , оскільки вони не змінюють один раз оцінювали: (eval (eval ':a))проти (eval (eval ''a)). Чи є інші переваги? Продуктивність мудра, вони однакові?
kristianlm

5
(ідентичний?: qwe: qwe) -> вірно. (ідентичний? 'qwe' qwe) -> false. Символи використовують інтерновану рядок всередині, тому порівняння теж швидко.
desudesudesu

29

У звичайних Lisp є ключові слова.

Ключові слова теж є символами.

(symbolp ':foo) -> T

Що робить ключові слова особливими:

  • : foo розбирається звичайним читачем Lisp як ключове слово :: foo
  • ключові слова оцінюють самі:: foo ->: foo
  • домашнім пакетом символів ключових слів є пакет KEYWORD: ключове слово: foo ->: foo
  • Ключові слова експортуються з пакету KEYWORD
  • ключові слова - константи, не можна присвоювати інше значення

Інакше ключові слова є звичайними символами. Отже, ключові слова можуть називати функції або мати списки властивостей.

Пам'ятайте: в символі Common Lisp до пакету належать символи. Це можна записати так:

  • foo, коли символ доступний у поточному пакеті
  • foo: bar, коли символ FOO експортується з пакета BAR
  • foo :: bar, коли символ FOO знаходиться в пакеті BAR

Для символів ключових слів це означає, що: foo, ключове слово: foo та ключове слово :: foo - все той же символ. Таким чином, останні два позначення зазвичай не використовуються.

Отже: foo просто проаналізований, щоб бути в пакеті KEYWORD, припускаючи, що надання імені пакета перед іменем символу означає за замовчуванням пакет KEYWORD.


6

Ключові слова - це символи, які оцінюють самі, тому цитувати їх не потрібно.


5
Це все? Введення тексту: замість 'не здається великим виграшем, тим більше що: це додаткове натискання клавіш на більшості клавіатур.
Лоуренс Гонсалвес

11
Ну, насправді це не просто персонаж. Ключові слова залишаються ключовими словами після оцінювання, тоді як символи оцінюються залежно від того, що вони стосуються. Це більше схоже на смислову різницю, оскільки вони зазвичай використовуються для різних цілей.
Грег Х'югілл

13
Ключові слова не є символами в Clojure
David Plumpton

4

: ключові слова також спеціально обробляються багатьма колекціями, завдяки чому існує справді зручний синтаксис.

(:user-id (get-users-map))

те саме, що

((get-users-map) :user-id)

це робить речі трохи більш гнучкими


21
Це також стосується символів, ('a {' a 1 'b 2}) => 1 і ({' a 1 'b 2}' b) => 2.
Йонас

4

Для ключових слів значення хешу обчислюються та кешуються під час першого створення ключового слова. Якщо шукати ключове слово як хеш-ключ, воно просто повертає попередньо обчислене хешоване значення. Для рядків та символів хеш перераховується під час кожного пошуку.

Чому ж названі ключові слова завжди однакові, вони містять власні хеш-значення. Оскільки пошук у картах та наборах робиться за допомогою хеш-ключів, це сприяє підвищенню ефективності пошуку у випадку численних пошукових запитів, а не в самому пошуку.


0

Ключові слова глобальні , символи - ні .

Цей приклад написаний на 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.