Common Lisp中使用'#(...)和#(...)创建向量有实际差异吗
Common Lisp 字面量向量创建方式的差异与开发影响
在 SBCL 环境下,我们可以用两种写法创建字面量向量,返回值看起来完全一致:
CL-USER> '#(a b c) #(A B C) CL-USER> #(a b c) #(A B C)
对两个返回值执行type-of和describe查询,结果也没有区别:
CL-USER> (type-of #(a b c)) (SIMPLE-VECTOR 3) CL-USER> (type-of '#(a b c)) (SIMPLE-VECTOR 3) CL-USER> (describe #(a b c)) #(A B C) [simple-vector] Element-type: T Length: 3 ; No value CL-USER> (describe '#(a b c)) #(A B C) [simple-vector] Element-type: T Length: 3 ; No value
用按值比较的equalp判断返回为真:
CL-USER> (equalp '#(a b c) #(a b c)) T
但用equal判断则返回假,甚至两个完全相同的'#(a b c)写法的返回值用equal比较也为假:
CL-USER> (equal '#(a b c) #(a b c)) NIL CL-USER> (equal '#(a b c) '#(a b c)) NIL
核心原因与开发影响
本质差异澄清
首先需要明确:#(...)是Common Lisp的读取器宏,本身返回的就是字面量常量向量,在前面加单引号'只是额外加了一次quote操作,对最终结果没有任何影响,两种写法得到的对象完全等价。equal返回NIL的原因是:Common Lisp标准规定,equal对于列表以外的序列类型(包括向量),仅判断两个对象是否是同一个内存对象(即eq判断),而SBCL实现不会对内容相同的字面量向量做常量合并,每次读取到#(...)字面量都会生成新的内存对象,所以哪怕写法完全一致,得到的也是不同的对象,equal自然返回NIL。
实际开发中的注意事项
- 禁止修改字面量向量:无论加不加单引号,
#(...)写法得到的都是常量向量,Common Lisp标准明确规定修改字面量常量属于未定义行为,SBCL中直接修改字面量向量的元素会触发运行时报错。如果需要可修改的向量,不要使用字面量写法,应该用vector函数或者make-array函数动态创建,例如(vector 'a 'b 'c)得到的就是可修改的向量。 - 选对相等性判断函数:如果需要判断两个向量的内容是否相同,统一使用
equalp,不要用equal,equalp会对所有序列类型执行逐元素的递归值比较,符合绝大多数业务场景的相等判断需求。 - 不要依赖字面量的对象同一性:无论写多少次相同内容的
#(...)字面量,都不要假设它们会指向同一个内存对象,不要用eq或者equal判断字面量向量的相等性。
内容的提问来源于stack exchange,提问作者Pedro Delfino
相关产品推荐
相关产品推荐

