LISP - 符号
Lisp - 符号与属性列表
Section titled “Lisp - 符号与属性列表”在 Common Lisp 中,符号(symbol)是一个唯一的、命名的(named)数据对象。它不仅仅是一个变量名;符号本身就是一个可以被操作的对象。可以把它想象成一个命名的容器,可以同时容纳一个值、一个函数和一个属性列表(property list)。
符号的经典特性之一是属性列表(property list),或简称 plist。plist 是一个直接附加到符号上的简单键值存储(key-value store),允许你将任意元数据(metadata)与之关联。
理解属性列表
Section titled “理解属性列表”属性列表的结构是一个由交替出现的键(称为 指示符,indicator)和值组成的列表。例如,如果我们想描述一个人,我们可以使用符号 'person 并附加 age、job 和 city 等属性。
当一个符号首次创建时,它的属性列表是空的。你可以使用 setf 表达式中的 get 函数来添加或修改属性。setf 是 Lisp 的通用赋值操作符(assignment operator),能够修改多种不同类型的位置。
让我们看看如何给名为 book 的符号分配属性。
示例:创建和访问属性
Section titled “示例:创建和访问属性”创建一个名为 main.lisp 的新源文件,并输入以下代码。此示例演示了如何设置然后从 book 符号中检索属性。
main.lisp
Section titled “main.lisp”;;; 使用 SETF 与 GET 来给 'book 符号分配属性。;;; 注意,符号 ('book) 和指示符 ('title 等) 都需要引用。(setf (get 'book 'title) "Gone with the Wind")(setf (get 'book 'author) "Margaret Mitchell")(setf (get 'book 'publisher) "Warner Books")
;;; 使用 GET 来检索特定属性的值。(format t "Title: ~s~%" (get 'book 'title))(format t "Author: ~s~%" (get 'book 'author))
;;; 如果属性不存在,GET 返回 NIL。(format t "ISBN: ~s~%" (get 'book 'isbn))当你加载并执行代码(例如,在 SBCL 的 REPL 中使用 (load "main.lisp"))时,它会产生以下结果:
Title: "Gone with the Wind"Author: "Margaret Mitchell"ISBN: NIL管理属性列表
Section titled “管理属性列表”Common Lisp 提供了一些用于管理属性列表的关键函数。
get symbol indicator &optional default:检索给定indicator的值。如果未找到,则返回NIL或提供的default值。symbol-plist symbol:返回符号的整个属性列表。remprop symbol indicator:从符号的属性列表中移除一个属性(包括键和值)。
示例:检查和移除属性
Section titled “示例:检查和移除属性”让我们在前一个示例的基础上,看看这些函数的实际应用。
main.lisp
Section titled “main.lisp”;; 定义一个函数来设置和演示符号的属性。(defun demonstrate-plists (person-symbol) (setf (get person-symbol 'age) 43) (setf (get person-symbol 'job) "Accountant") (setf (get person-symbol 'sex) 'female)
(format t "Initial plist for '~s: ~s~%" person-symbol (symbol-plist person-symbol))
;; 移除 'age' 属性 (remprop person-symbol 'age) (format t "After removing 'age: ~s~%" (symbol-plist person-symbol)))
;; 使用符号 'annie 运行演示(demonstrate-plists 'annie)Initial plist for 'ANNIE: (SEX FEMALE JOB "Accountant" AGE 43)After removing 'age: (SEX FEMALE JOB "Accountant")现代最佳实践:属性列表与其他结构
Section titled “现代最佳实践:属性列表与其他结构”虽然属性列表是 Lisp 的经典特性,但对于建模结构化的应用程序数据,现代 Common Lisp 提供了更好的替代方案:
- 哈希表(Hash Tables):适用于键不一定是符号的通用键值映射(key-value mapping)。它们效率很高。
(make-hash-table) - 结构体(Structs):一种定义带有命名槽(named slots)的简单类对象结构(object-like structures)的轻量级方式。它们提供比属性列表更好的性能和类型检查。
(defstruct) - CLOS 类(CLOS Classes):完整的 Common Lisp 对象系统(Common Lisp Object System)提供了强大的功能,如继承(inheritance)、多方法(multi-methods)和内省(introspection)。
(defclass)
何时使用属性列表? 属性列表最适合用于将元数据 附加到符号本身,特别是在元编程或与编译器相关的任务中。对于在应用程序中表示书籍或用户等实体,出于简化目的,优先选择 defstruct,或根据更复杂的需求选择 defclass。
示例:defstruct 的替代方案
Section titled “示例:defstruct 的替代方案”;; 定义一个书籍结构体(defstruct book title author publisher isbn)
;; 创建一个书籍结构体的实例(let ((my-book (make-book :title "Structure and Interpretation of Computer Programs" :author "Abelson & Sussman" :publisher "MIT Press")))
;; 访问槽(slots)清晰高效 (format t "Struct Title: ~s~%" (book-title my-book)) (format t "Struct Author: ~s~%" (book-author my-book)))Struct Title: "Structure and Interpretation of Computer Programs"Struct Author: "Abelson & Sussman"