为什么Clojure除了“符号”外还有“关键字”?


130

从过去开始,我就对其他Lisps(尤其是Scheme)有所了解。最近,我一直在阅读有关Clojure的文章。我看到它既有“符号”又有“关键字”。我熟悉的符号,但不包含关键字。

其他Lisps是否有关键字?除了具有不同的符号(即冒号)以外,关键字与符号有何不同?


Answers:


139

这是关键字和符号的Clojure文档

关键字是自我评估的符号标识符。他们提供非常快速的平等测试...

符号是通常用于引用其他内容的标识符。它们可以在程序形式中用于引用函数参数,让绑定,类名和全局变量...

关键字通常用作轻量级的“常量字符串”,例如用于哈希映射的键或多方法的调度值。符号通常用于命名变量和函数,除了在宏等中,直接将它们作为对象进行操作并不常见。但是,没有什么阻止您在使用关键字的任何地方都使用符号的(如果您不介意一直引用它们)。

看出区别,最简单的方法是阅读Keyword.javaSymbol.java在Clojure的来源。有一些明显的实现差异。例如,Clojure中的符号可以具有元数据,而关键字则不能。

除了单冒号语法之外,您还可以使用双冒号来创建名称空间限定的关键字。

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

Common Lisp以及Ruby和其他语言都具有关键字。它们在这些语言中当然有所不同。Common Lisp关键字和Clojure关键字之间的一些区别:

  1. Clojure中的关键字不是符号。

    user> (symbol? :foo)  
    false
  2. 除非您特别限定关键字,否则关键字不属于任何名称空间:

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

(感谢Rainer Joswig给我一些要看的东西的想法。)


10
这解释了差异是什么,但没有解释为什么需要两个不同的构造。Clojure不能通过关键字和符号功能的结合来创建某些东西吗?

25
关键字是轻量级的,并且具有方便的语法,我认为这就是全部。没有它们,该语言就可以很好地工作,但是它们很不错,并且被广泛使用。您不能将它们的能力结合在一起,因为关键字始终是自我评估的(即,您不能将它们用作变量或函数名称),而符号通常不能始终自我评估。
布赖恩·卡珀

1
关键字似乎更适合用作哈希图中的键,因为它们一旦被评估就不会改变:(eval (eval ':a))vs (eval (eval ''a))。还有其他优势吗?性能明智,它们是相同的吗?
kristianlm

5
(相同?:qwe:qwe)->是。(相同?'qwe'qwe)->错误。符号内部使用了分隔字符串,因此比较也很快。
desudesudesu 2014年

29

Common Lisp具有关键字符号。

关键字也是符号。

(symbolp ':foo) -> T

是什么让关键字与众不同:

  • :foo被Common Lisp阅读器解析为符号关键字:: foo
  • 关键字对自己求值::foo->:foo
  • 关键字符号的主程序包是KEYWORD程序包:keyword:foo->:foo
  • 关键字是从软件包KEYWORD中导出的
  • 关键字是常量,不允许分配其他值

否则,关键字是普通符号。因此,关键字可以命名函数或具有属性列表。

请记住:在Common Lisp中,符号属于包装。可以这样写:

  • foo,当符号在当前包中可访问时
  • foo:bar,当从包BAR导出符号FOO时
  • foo :: bar,当符号FOO位于包BAR中时

对于关键字符号,这意味着:foo,keyword:foo和keyword :: foo都是相同的符号。因此,通常不使用后两个符号。

因此,:foo仅被解析为包含在KEYWORD包中,假设在符号名之前不给出任何包名意味着默认情况下是KEYWORD包。


6

关键字是对自身进行评估的符号,因此您不必记住引用它们。


5
是吗 键入:而不是'似乎不是一个大胜利,尤其是因为:在大多数键盘上都是额外的按键。
劳伦斯·贡萨尔维斯(Lawrence Gonsalves),2009年

11
嗯,这不仅仅只是角色。关键字在评估后仍为关键字,而符号则根据它们绑定的内容进行评估。这更像是语义上的差异,因为它们通常用于不同的目的。
Greg Hewgill,2009年

13
关键字不是Clojure中的符号
David Plumpton 2010年

4

:keywords也被许多集合特别对待,允许使用一些非常方便的语法。

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

是相同的

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

这使事情变得更加灵活


21
对于符号('a {'a 1'b 2})=> 1和({'a 1'b 2}'b)=> 2也是如此
。– Jonas

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.