符号 简写符号(shorthands),有时也被称作 “重命名符号(renamed symbols)”,是出现在 Lisp 源码中的符号形式。它们与普通符号形式完全一样,唯一的区别是:当 Lisp 读取器遇到它们时,会生成具有不同且通常更长的 打印名(print name) 的符号(see 符号的组成)。
可以把简写符号理解为对目标符号完整名称的 缩写,这一理解很有用。尽管如此,不要将简写符号与缩写(Abbrev)系统混淆(see Abbrevs and Abbrev Expansion)。
简写符号让 Emacs Lisp 的 命名空间规范(namespacing etiquette) 更易落地使用。由于所有符号都存储在同一个符号表中(see 创建与编入符号),程序员通常会在每个符号名前加上其所属库的名称作为前缀。例如,函数 text-property-search-forward 和 text-property-search-backward 都隶属于 text-property-search.el 库(see 加载)。通过为符号名添加规范的前缀,能够有效避免不同库中名称相似但功能不同的符号产生命名冲突。然而这种做法往往会导致符号名变得非常长,时间一长,输入和阅读都会十分不便。简写符号则以一种简洁优雅的方式解决了这些问题。
该变量的值是一个关联列表(alist),其元素格式为 (shorthand-prefix . longhand-prefix)。列表中的每个元素都会指示 Lisp 读取器:将所有以 shorthand-prefix 开头的符号形式,视作以 longhand-prefix 开头的符号形式来解析。
该变量仅允许在文件局部变量中设置(see Local Variables in Files in The GNU Emacs Manual)。
下面是一个在假想的字符串操作库 some-nice-string-utils.el 中使用简写符号的示例。
(defun some-nice-string-utils-split (separator s &optional omit-nulls) "A match-data saving variant of `split-string'." (save-match-data (split-string s separator omit-nulls))) (defun some-nice-string-utils-lines (s) "Split string S at newline characters into a list of strings." (some-nice-string-utils-split "\\(\r\n\\|[\n\r]\\)" s))
可以看到,由于需要输入的符号名太长,编写和阅读这段代码都会相当繁琐。我们可以使用简写符号来缓解这一问题。
(defun snu-split (separator s &optional omit-nulls)
"A match-data saving variation on `split-string'."
(save-match-data (split-string s separator omit-nulls)))
(defun snu-lines (s)
"Split string S into a list of strings on newline characters."
(snu-split "\\(\r\n\\|[\n\r]\\)" s))
;; Local Variables:
;; read-symbol-shorthands: (("snu-" . "some-nice-string-utils-"))
;; End:
尽管这两段代码片段看起来不同,但经过 Lisp 读取器处理后,二者会变得完全一致。两种写法最终都会将完全相同的符号注册到符号表中(see 创建与编入符号)。因此,加载或字节编译这两个文件中的任意一个,得到的结果都是等效的。
第二个版本中使用的简写符号 snu-split 和 snu-lines,并不会被注册到符号表中。要验证这一点很简单:将光标移到使用这些简写符号的位置,稍作等待,ElDoc 功能(see Local Variables in Files in The GNU Emacs Manual)就会在回显区提示光标所在位置符号的真实完整名称。
由于 read-symbol-shorthands 是文件局部变量,因此多个依赖some-nice-string-utils-lines.el 库的代码,可能会用不同的简写前缀引用同一个符号,甚至完全不使用简写符号。在下一个示例中,my-tricks.el 库就使用 sns- 前缀(而非 snu-)来引用some-nice-string-utils-lines 这个符号。
(defun t-reverse-lines (s) (string-join (reverse (sns-lines s)) "\n")
;; Local Variables:
;; read-symbol-shorthands: (("t-" . "my-tricks-")
;; ("sns-" . "some-nice-string-utils-"))
;; End:
注意:如果在同一个文件中有两个简写符号,且其中一个是另一个的前缀,那么更长的简写前缀会优先匹配。无论你在文件的局部变量段中以何种顺序定义这些简写,都会按此规则执行。
'(
t//foo ; reads to 'my-tricks--foo', not 'my-tricks-/foo'
t/foo ; reads to 'my-tricks-foo'
)
;; Local Variables:
;; read-symbol-shorthands: (("t/" . "my-tricks-")
;; ("t//" . "my-tricks--")
;; End:
简写符号转换规则存在两种例外情形:
- 或 /= 用作简写前缀,但这不会覆盖掉同名的算术 函数(functions)。