📗
elisp manual zh
  • LAND OF LISP
  • 引言
  • 进度 & 计划
  • 一些记录
  • Lisp 数据类型
    • 2 Lisp 数据类型
    • 2.1 打印表示 和 读取语法
    • 2.2 特定的读取语法
    • 2.3 注释
    • 2.4 编程类型
      • 2.4.1 整型
      • 2.4.2 浮点型
      • 2.4.3 字符型
        • 2.4.3.1 基本字符语法
        • 2.4.3.2 通用转义语法
        • Page 2
        • Page 3
        • Page 4
        • Page 1
      • 2.4.4 符号类型
      • 2.4.5 序列类型
      • 2.4.6 点对类型
        • 2.4.6.1 点对可视化
        • 2.4.6.2 点对记法
        • 2.4.6.3 关联列表
      • 2.4.7 数组类型
      • 2.4.8 字符串类型
        • 2.4.8.1 字符串的语法
        • 2.4.8.2 字符串中的非ASCII码
        • 2.4.8.3 不可打印字符
        • 2.4.8.4 字符串的文本属性
      • 2.4.9 向量类型
      • 2.4.10 字符表类型
      • 2.4.11 布尔向量类型
      • 2.4.12 哈希映射类型
      • 2.4.13 函数类型
      • 2.4.14 宏类型
      • 2.4.15 原函数类型
      • 2.4.16 字节码类型
      • 2.4.17 记录类型
      • 2.4.18 类型描述符
      • 2.4.19 自动加载类型
      • 2.4.20 终止类型
    • 2.5 编辑类型
      • 2.5.1 缓冲区变量
      • 2.5.2 标记类型
      • 2.5.3 窗口类型
      • 2.5.4 框架类型
      • 2.5.5 终端类型
      • 2.5.6 窗口配置类型
      • 2.5.7 框架配置类型
      • 2.5.8 进程类型
      • 2.5.9 线程类型
      • 2.5.10 互斥锁类型
      • 2.5.11 条件变量类型
      • 2.5.12 流类型
      • 2.5.13 按键映射类型
      • 2.5.14 覆盖类型
      • 2.5.15 字体类型
    • 2.6 循环结构对象
    • 2.7 类型谓语
    • 2.8 相等谓语
    • 2.9 可变性
  • 数字
    • 3.1 整型数
    • 3.2 浮点型基础
    • 3.3 谓词:数字相关
    • 3.4 数字比较
    • 3.5 数字类型转换
    • 3.6 数学变换
    • 3.7 取整
    • 3.8 位运算
    • 3.9 数学函数
    • 3.10 随机数
  • 字符串 和 字符
    • 4 字符串和字符
    • 4.1 字符串基础
    • 4.2 谓语:字符串相关
    • 4.3 创建字符串
    • 4.4 修改字符串
    • 4.5 字符串比较
    • 4.6 字符串转换
    • 4.7 字符串格式化
    • 自定义格式化
    • Case Conversion
    • Case Table
  • 列表
    • 5 列表
    • 5.1 列表与点对
    • 5.2 列表相关的谓词
    • 5.3 列表元素的存取
    • 5.4 列表与点对的构建
    • 5.5 列表变量的修改
    • 5.6 列表结构的修改
    • 5.7 列表和集合的使用
    • 5.8 关联列表 Association Lists (alist)
    • 5.9 属性列表 Property Lists (plist)
  • 序列, 数组, 和 向量
    • 6 序列、数组、以及向量
    • 6.1 序列
    • 6.2 数组
    • 6.3 数组相关函数
    • 6.4 向量
    • 6.5 向量相关函数
    • 6.6 字符表
    • 6.7 布尔向量
    • 6.8 大小固定的对象环的管理
  • 记录
    • 7 记录
    • 7.1记录相关函数
    • 7.2 向后兼容
  • 哈希表
    • 8 哈希表
    • 8.1 哈希表创建
    • 8.2 哈希表存取
    • 8.3 哈希表定制
    • 8.4 其他哈希表相关函数
  • 符号
    • 9 符号(3/4)
    • 9.1 符号的组成(DONE)
    • 9.2 定义符号(DONE)
    • 9.3 创建(Creating)并注册(Interning)符号(DONE)
    • 9.4 符号属性
      • 9.4.1 存取符号属性(DONE)
      • 9.4.2 标准符号属性
  • 求值
    • 10 求值(DONE)
    • 10.1 表达式类型(DONE)
      • 10.2.1 自求值表达式(DONE)
      • 10.2.2 符号表达式(DONE)
      • 10.2.3 列表表达式(DONE)
      • 10.2.4 函数符号转义(DONE)
      • 10.2.5 函数表达式求值(DONE)
      • 10.2.6 Lisp 宏求值(DONE)
      • 10.2.7 特殊表达式(DONE)
      • 10.2.8 自动加载(DONE)
    • 10.2 引用(Quoting)(DONE)
    • 10.3 反引用(DONE)
    • 10.4 Eval(求值)(DONE)
    • 10.5 延迟求值(DONE)
  • 控制结构
    • 11. 控制结构
    • 11.1 顺序执行(DONE)
    • 11.2 条件判断(DONE)
    • 11.3 条件组合结构(DONE)
    • 11.4 模式匹配(TODO)
    • 11.5 迭代(DONE)
    • 11.6 生成器(DONE)
    • 11.7 非局部退出
      • 11.7.1 显式非局部退出:catch 和 throw
      • 11.7.2 catch 和 throw 的例子
      • 11.7.3 错误
        • 11.7.3.1 如何传递出一个错误
        • 11.7.3.2 Emcas 的错误处理流程
        • 11.7.3.3 编写错误处理函数
        • 11.7.3.4 错误符号和条件名
      • 11.7.4 非局部退出后的清理工作
  • 变量
    • 12 变量
    • 12.1 全局变量
    • 12.2 常量变量
    • 12.3 局部变量
    • 12.4 无效(Void)变量
    • 12.5 定义全局变量
    • 12.6 定义变量的建议
    • 12.7 变量获取
    • 12.8 变量设置
    • 12.9 变量监视
      • 12.9.1 限制
    • 12.10 变量作用域规则
      • Untitled
      • 12.10.1 动态绑定
      • 12.10.2 动态绑定的建议
      • 12.10.3 词法绑定
      • 12.10.4 如何正确的使用词法绑定
    • 文件变量
    • 目录变量
    • 通信变量
    • 变量别名
    • 变量限制
    • 泛型变量
  • 函数
    • 13 函数
    • 13.1 函数是什么?(DONE)
    • 13.2 Lambda 表达式(WORKING)
      • 13.2.1 lambda表达式的组成部分
      • 13.2.2 一个简单的lambda表达式例子
      • 13.2.3 参数列表的特性
      • 13.2.4 函数的文档字符串
    • 13.3 函数命名
    • 13.4 函数定义
    • 13.5 函数调用
    • 13.6 函数映射式调用
    • 13.7 匿名函数
    • 13.8 泛型函数
    • 13.9 读取函数槽信息
    • 13.10 闭包
    • 13.11 Emacs Lisp 函数最佳实践
    • 13.12 声明废弃函数
    • 13.13 内联函数
    • 13.14 declare 表达式
    • 13.15 告知编译器某函数已定义
    • 13.16 函数安全调用
    • 13.17 有关函数的其他主题
  • 宏
    • Untitled
  • 自定义设置
  • 加载脚本
  • 字节码编译
  • 调试 Lisp 代码
  • 读取和打印
    • 19.1 读取与打印简介
    • 19.2 输入流
    • 19.3 输入函数
    • 19.4 输出流
    • 19.5 输出函数
    • 19.6 输出变量
  • Mini Buffer
  • Loop 循环
  • 按键绑定
  • Modes
  • 23.1 钩子
    • 23.1.1 运行钩子
    • 23.1.2 设置钩子
  • 23.2
  • Untitled
  • Untitled
  • Untitled
  • Untitled
  • 文档
    • Untitled
  • 文件访问
    • Untitled
  • 备份 及 自动保存
    • Untitled
  • Buffers
    • Untitled
  • 窗口
    • Untitled
  • 框架
    • Untitled
  • 位置
    • Untitled
  • 标记
    • Untitled
  • 文本
    • 32 文本
      • 32.1 位置点附近 (Near Point)
      • 32.2 缓冲区内容 (Buffer Contents)
  • 非 ASCII 字符
    • Untitled
  • 搜索 和 匹配
    • Untitled
  • 语法表
    • Untitled
  • 缩写 及 缩写拓展
    • 34 搜索 与 替换
      • 34.1 文本搜索
  • 进程
    • Untitled
  • 线程
    • Untitled
  • Emacs 显示
    • Untitled
  • 操作系统接口
    • Untitled
  • 标准错误
    • Untitled
  • 标准按键映射
    • Untitled
  • 标准 Hooks
    • Untitled
  • Tip & Conventions
    • Untitled
  • 索引
    • Untitled
  • 附录
    • Untitled
    • 附录 H: 标准钩子
由 GitBook 提供支持
在本页

这有帮助吗?

  1. 符号

9.3 创建(Creating)并注册(Interning)符号(DONE)

2021-8-14 21:52

我们先介绍 Lisp 是怎么读取符号的。最重要一个原则,Lisp 确保了 组成符号名称的字符 和 符号本身 是一一对应的。理解这一点非常重要。

在源码中,当 Lisp 读取器读取到一个符号时,它会首先读取表示该符号名字的所有字符。然后,读取器会在一个称之为 obarray 的表中查找这个名字。这里具体使用的查找技术称为哈希("hashing"),这是一种很高效的查找技术。该技术先将一段字符序列转换为一个数字,也就是我们常说的哈希值("hash code")。举个例子,你想在电话簿中查找Jan Jones的电话号码,相比在电话簿中一页一页的查找,我们先查去 J 索引,再寻找电话号码会更加快捷高效。这就是哈希映射的简单应用。obarray 中的每个元素都是一个“桶”,这些“桶”中储存了给定哈希值的所有符号;先通过哈希值找到相应的 “桶”,然后在这些 “桶”中遍历,寻找目标符号。(在Emacs 中通用哈希表的实现有相同的思路,但是具体的数据类型并不相同,详情查阅 哈希表章。)

如果该符号被找到了,那么读取器便会直接使用这个符号。如果 obarray 中不包含这个符号,那么读取器会创建该符号,然后将其加入到 obarray 对应的“桶”中。寻找并添加符号的过程称为 interning,之后该符号被称为 interned (已注册的) 符号。

interning 保证了同一个 obarray 中的所有符号的名称都是独一无二的。Lisp 中可能会存在同名的符号,但这些同名的符号绝对不可能出现在同一个obarray中,而只能存在于不同的 obarray中。因此,确定obarray的前提下,读取器在读取相同的名字时查找到的符号必定是同一个符号。

通常 interning 会在读取器中自动进行,但有的时候可能会有程序手动 interning。比如,运行M-x命令来从minibuffer中获取某些命令的文本表示后,minibuffer会将该文本作为符号注册。

obarray 不可能储存所有的符号;实际上,有些符号根本就不储存在 obarray 中。这些符号称为 uninterned(未注册) 符号。 uninterned 符号和常规符号相同,都由四个部分构成。不过,获取这些符号的唯一方法是在其他对象中查找或在某些变量的value。uninterned 符号在Lisp的代码生成中十分有用。在你生成的代码中 (通常在写宏的时候),使用 uninterned 符号,可以保证这些符号不会与 Lisp 程序中使用的其他变量发生命名冲突。

在 Emacs Lisp 中, obarray 实际上是一个向量。该向量中的元素都是一个桶;其中要么装着 hash 到这个桶的 interned 符号,要么装着 0 (表示这个桶内没有符号) 。每一个 interned 符号都有一个内部的指针(对于用户是不可见的)来指向到桶里的下一个符号。因为这些指针是不可见的,使用常规手段是没办法找到所有的符号的。你可以使用后续介绍的 mapatoms 查找一个桶中的所有符号。不过,符号在桶中的具体顺序在绝大多数时候并不重要。

在一个空 obarray 中,每个元素都是0 。你可以使用 (make-vector length 0) 来创建一个 obarray 。这是唯一的合法创建 obarray 的方法。通常来说,质数长度更有利于哈希操作,长度小于平方根减一效果也不错。

不要尝试手动将符号放入 obarray 中。这并不会起作用——只有 intern 可以正确地将符号放入 obarray 中。

Common Lisp 注意:和 Common Lisp 不同,Emacs Lisp 没有提供将一个符号放入放入几个不同 obarray 中的方法。

下面提到的绝大部分函数都接受名称,或 obarray 作为参数。当名名称不是字符串,或 obarray 不是向量时,会抛出 wrong-type-argument 错误。

Function: symbol-name symbol 这个函数以字符串的形式返回符号的名字。比如:

(symbol-name 'foo)
    => "foo"

警告:通过替换字符来改变字符串确实改变了符号的名称,但却不会更新obarray,所以不要这样做!

Function: make-symbol name 这个函数返回一个新创建的,指定名字的 uninterned(未注册) 符号。这个符号的 function 和 value 部分均为空,其属性列表也是 nil 。这下面这个例子中,sym 的 value 和 foo 并不 eq。原因是,sym 是一个 uninterned(未注册) 符号,其名字也是‘foo’。(而符号foo是一个 interned(已注册) 符号)

(setq sym (make-symbol "foo"))
    => foo
(eq sym 'foo)
    => nil

Function: gensym &optional prefix 这个函数返回了一个使用 make-symbol 创建的符号,但名称会使用 gensym-counter 加上前缀 prefix。默认的前缀为 "g"。

Function: intern name &optional obarray 这个函数返回一个指定 name 的 interned 的符号。如果 obarray 中没有这个符号,intern 会创建该符号,并将这个符号加入 obarray,然后返回。如果没有提供 obarray ,则会使用默认的全局 obarray。

(setq sym (intern "foo"))
    => foo
(eq sym 'foo)
    => t
    
(setq syml (intern "foo" other-obarray))
    => foo
(eq syml 'foo)
    => nil

Common Lisp 注意:在 Common Lisp 中,你可以将一个已存在的符号 intern(注册) 到一个 obarray 中。在 Emacs Lisp 中,你不能这样做,因为 intern 的参数必须是字符串,而不能是符号。

Function: intern-soft name &optional obarray 这个函数返回 obarray 中名字为 name 的符号。如果 obarray 中不存在,则返回 nil。因此,你可以使用 intern-soft 来测试某个符号有没有 interned。如果没有提供 obarray,那么将会使用默认的全局 obarray。

参数 name 可以是符号;在这种情况下,如果 name 已经 interned,函数则会返回指定 obarray 中的 name,否则返回 nil。

(intern-soft "frazzle")    ; 不存在 frazzle 符号.
    => nil
(make-symbol "frazzle")    ; 创建一个 uninterned(未注册) 的 frazzle 符号.
    => frazzle
(intern-soft "frazzle")    ; 显然这未注册的符号是不存在于 obarray 中的.
    => nil
(setq sym (intern "frazzle"))   ; 创建一个 interned(已注册) 的 frazzle 符号.
    => frazzle
(intern-soft "frazzle")    ; 在 obarray 中找到了 frazzle 符号!
    => frazzle
(eq sym 'frazzle)          ; 这和 frazzle 是同一个符号 .
    => t

Variable: obarray 这个变量是一个默认的全局的 obarray。用来 intern(注册) 和 read(读取) 符号。

Function: mapatoms function &optional obarray 这个函数对 obarray 中所有的符号调用一次 function。返回值为 nil。如果没有提供 obarray, 则会使用默认的全局变量 obarray,这是标准规定的 obarray。

(setq count 0)
    => 0
(defun count-syms (s)
  (setq count (1+ count)))
    => count-syms
(mapatoms 'count-syms)
    => nil
count
    => 1871

查阅另一个文档(Accessiong Documentation)的另一个例子。

Function: unintern symbol obarray 这个函数将 symbol 从 obarray 中删除掉。如果符号并不在 obarray 中,unintern 什么事情都不会做。如果没有提供 obarray,则默认使用全局 obarray。

如果你提供了一个字符串,而不是符号,那默认会将字符串转换成对应的符号。unintern 删除 obarray 中的指定字符串。如果没有这个符号,unintern 将不会做任何事情。

另外,如果unintern 成功删除了指定符号,则返回 t。否则返回 nil。

上一页9.2 定义符号(DONE)下一页9.4 符号属性

最后更新于2年前

这有帮助吗?