symbol 变量

Elisp 中的 symbol 类型

symbol 类型是 Elisp 中比较复杂的一个类型之一,每一个 symbol 都有一个 name 。这里 只介绍 symbol 的 name 的格式。symbol 可以用来表示 varible, function, 还可以用来 标识一个唯一的值,例如一种颜色,一周星期的名字,可以说 symbol 是使用最广泛的一种 类型。

symbol name 可以任意字符。这一点和很多其他非 Lisp 类的语言有很大区别,一般语言中, 用来表示变量的名字都不能任意取,例如: C 语言中便亮名字必须以非数字开头,仅有字 母,下划线和数字组成,而且还不能是保留字,例如 for , while 等。但是在 ELisp 中完 全没有什么保留字。

但是有一些特殊字符会被特殊解释,例如 ( , 1.2 , ) , 等等,要想 symbol name 中含 有这些东西,那么就必须用斜杠使之转义。然而,让名字中含有一些特殊字符如果并不能增 强可读性,就是给自己找麻烦。

每一个 symbol 有 4 个 cell 用来存储不同的信息。

  1. name
  2. value
  3. function
  4. property list
(setq var 'a-symbol)
(symbol-name 'var)   ;; 返回 "var"
(symbol-name var)    ;; 返回 "a-symbol"

symbol 的 name 是唯一的,两个重名的 symbol 是同一 个 symbol。 Elisp 的解释器保证了这一点,在读取一个 symbol 的时候,首先查找是否有已经存在的 symbol , 如果有,则用原来的,如果没有就新建一个。

可以用函数 symbol-value 来得到一个 symbol 的 value 。 boundp 可以判断一个 symbol 时候有 symbol-value 当 symbol 作为一个 varible 的时候,会得到他的 symbol value 。在对一个 symbol 求值的时候,就会得 到 symbol value 。常用的设置 symbol value 的办法就 是用 setq 。

(setq a 1)
(setq b 2)
(list a b)  ;; 返回 (1 2)
(setq add (lambda (a b) (- a b))) ;; 注意 add 现在的 symbol-value 是一个函数
(fboundp 'add)  ;; 返回 nil , add 不对应一个的函数
(add 1 1) ;; 会产生错误
(defun add (a b) (+ a b))  ;; 注意这时 add 就对应一个函数。
(add 1 3)  ;; 返回 4 , 1 + 3
(funcall add 3 1) ;; 返回  3 - 1 , 因为 add 作为参数的时候,取 value ,
;; 下面的为了加深理解
(funcall (symbol-value 'add) 3 1) ;; 返回 2
(funcall (symbol-function 'add) 3 1) ;; 返回 4

创建和 interning 一个 symbol

当 emacs 的 elisp 解释器碰到一个 symbol 的时候, 注意 symbol 和 quote 的区别。 `var= 等同于 (quote var) ,而后者是一个 list ,其中有两个 symbol , elisp 认为第 一个 symbol 是一个函数,取 symbol 中.

每一个 elisp 碰到的每一个 symbol , 都会在 obarray 中搜索。 obarray 是一个数组, 用于 emacs 内部的 hash 结构,用于快速查找 symbol。

(setq b 1)
b ;; 返回 1
(unintern 'b)
b ;; 产生错误,symbol b 不存在了。
(set (intern "b") 1) ;; 几乎等同于 (setq b 1)
b ;; 返回 1

可以创建自己的这种结构,类似于 hash 结构。速度很快。


(setq myobarray (make-vector 1511 0)) ;; 1511 是一个质数,更加有效率
(set (intern "a" myobarray) 1)
(set (intern "b" myobarray) 2)
(set (intern "c" myobarray) 3)
;; 打印
;;a --> 1
;;b --> 2
;;c --> 3
(mapatoms (lambda (s)
           (print (format "%s -->%d" (symbol-name s) (symbol-value s))))
         myobarray)
(intern-soft "a" myobarray) ;; 返回一个 symbol
(intern-soft "x" myobarray) ;; 返回 nil
(unintern "a" myobarray) ;; 删除一个 symbol

如果 myobarray 中不存在某个 symbol , intern 函数会自动创建一个,然后返回这个 创建的 symbol. 所以这个函数不能判断 myobarray 是否存在某个 symbol ,=intern-soft= 可以实现这个功能,对于不存在的 symbol, 该函数返回 nil

下面的例子加强理解

(setq a nil)
(setq my-obarray-list (cons (make-vector 1511 0) nil))
(setq car-of-obarray-list (car my-obarray-list))
(set (intern "a" car-of-obarray-list) "hello")
(symbol-value (intern-soft "a" (car my-obarray-list)))  ;; 返回 "hello"
(eq (intern "a" car-of-obarray-list) 'a) ;; 返回 nil , 两个 a 在不同的 obarray 中。
(eq (symbol-name (intern "a" car-of-obarray-list)) (symbol-name 'a)) ;; 返回nil,
(eq "a" "a") ;; 返回 nil
(equal (symbol-name (intern "a" b)) (symbol-name 'a)) ;; 返回 t
(eq (intern "a" car-of-obarray-list) (intern "a" (car my-obarray-list))) ;; 返回 t