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

我对其他 Lisps (特别是 Scheme)有一定的了解。最近我一直在阅读关于 Clojure的文章。我看到它有“符号”和“关键字”。我熟悉的符号,但不熟悉关键字。

其他 Lisps 有关键字吗? 除了有不同的符号(例如: 冒号) ,关键字与符号有什么不同?

27075 次浏览

关键字是对它们自身进行计算的符号,所以您不必记住引用它们。

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

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

符号是通常用来指代其他事物的标识符。它们可以在程序表单中用来引用函数参数、让绑定、类名和全局变量。

关键字通常用作轻量级的“常量字符串”,例如用于散列映射的键或多方法的分派值。符号通常用于命名变量和函数,除了宏之类的对象外,很少直接将它们作为对象来操作。但是没有什么可以阻止你在使用关键字的任何地方使用一个符号(如果你不介意一直引用它们的话)。

查看差异的最简单方法是在 Clojure 源代码中读取 Keyword.javaSymbol.java。在实现上有一些明显的差异。例如,Clojure 中的“符号”可以具有元数据,而“关键字”不能具有元数据。

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

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

Common Lisp 有关键字,Ruby 和其他语言也有关键字。当然,在这些语言中它们略有不同。Common Lisp 关键字和 Clojure 关键字之间的一些区别:

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

    user> (symbol? :foo)
    false
    
  2. Keywords don't belong to any namespace unless you specifically qualify them:

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

(Thanks Rainer Joswig for giving me ideas of things to look at.)

Common Lisp 关键词符号。

关键字也是符号。

(symbolp ':foo) -> T

关键词的特殊之处:

  • Common Lisp 阅读器将 :foo解析为符号 keyword::foo
  • 关键字自我评估: :foo-> :foo
  • 关键字符号的主包是 KEYWORD包: keyword:foo-> :foo
  • 关键字是从包 KEYWORD导出的
  • 关键字是常量,不允许分配不同的值

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

请记住: 在 Common Lisp 中,符号属于一个包: 关键字软件包:

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

对于关键字符号,这意味着 :fookeyword:fookeyword::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)