Lisp 中的 Symbol 與 Atom 差別在哪

初學 Lisp 時一直搞不懂 atom 跟 symbol 到底差在哪…不對那個時候連 symbol 是三小都搞不清楚。不過不才在下最近突然理解了 Lisp 裡面的 Symbol 跟 Atom 的意義,所以來解釋一下。

此篇假設你已經理解 Lisp 中的 cons 與 list

atom

atom 這個詞本來的意思就是指不可分割的東西、最小的單位,而 Lisp 中,只有 cons (或者用其他語言使用者比較可以理解的詞,叫做 list) 是可以分割的。例如最基本的 operator 之一。

其他舉凡 integer, float, symbol, string 甚至乍看之下以為可以分割的 vector 都是屬於「不可分割」的東西。

Lisp 中一切不可分割的東西,都叫做 atom。或者換句話說,list 以外的任何東西,都是 atom,因為只有 list 是可以分割的。

有個例外…就是 nil。 這鬼東西比較機車一點,雖然它屬於 list(空的 list),但也同時屬於 atom(空的 list 確實也無法再分割啊)…

atom 這個最基本的 Lisp operator 的功能就是判斷一個物件是否為 atom:

1
2
3
4
5
6
7
(atom 12)        ; => t
(atom 12.3) ; => t
(atom 'hello) ; => t
(atom "hello") ; => t
(atom [1 2 3]) ; => t
(atom '(1 2 3)) ; => nil
(atom '(1 . 2)) ; => nil

所以…在現代的 Lisp dialect 中,(atom x) 的意義基本上完全等同於 (not (consp nil)),真是毫無屁用啊

symbol

一個 symbol 一定屬於 atom,但 atom 不一定屬於 symbol

舉例來說,一個 integer 是個 atom,但並不屬於 symbol

1
2
3
4
5
6
7
;; 一個 symbol
(atom 'world) ; => t
(symbolp 'world) ; => t

;; 一個 integer
(atom 21) ; => t
(symbolp 21) ; => nil

一個 interger 只是 atom,而不是 symbol,所以當然也沒有 symbol 該有的功能,例如 每一個 symbol 都有點像是物件導向語言中的物件,裡面包含了一些屬性,以 Common Lisp 跟 Emacs Lisp 為例,每個 symbol 都擁有如下的屬性:

屬性名稱 可用啥函數取出內容 用途
name symbol-name 該 symbol 轉成字串時的樣子
value symbol-value 該 symbol 作為 variable 使用時所儲存的
function symbol-function 該 symbol 作為 function 使用時所儲存的 function 本體
plist symbol-plist 一個看你要拿來幹麻都行的 plist

你就可以知道,為什麼一個 function 或 variable 要取名時得用 symbol 當名稱。一個整數在 Lisp 無法拿來當作 function 或 variable 的名稱,因為整數並不是 symbol。

symbol 可以想成是 C/C++ 裡的指標,每個 symbol 都指向記憶體的某個位置。所以,當你用 eq 來比較兩個 symbol 是否相同時,會比使用 equalstring= 來比較兩個字串速度來得快。(因為字串得一個個字元比較,而 symbol 只要比對記憶體位置)