两个具有相同符号指示符的 Common Lisp 函数对象总是 eq 吗?
在 Common Lisp 中,函数是一段代码,无论它是否已编译。来自Glossary:
function n. 1. object 表示代码,可以用零个或多个参数调用,并产生零个或多个价值观。 2. 函数类型的对象。
另一方面,功能指示符可以是符号:
功能指示符 n.功能指示符;也就是说,一个表示函数的对象,它是以下之一:符号(表示在全局环境中由该符号命名的函数)或函数(表示自身)。
因此,作为函数指示符的符号是在特定上下文中或使用特定语法(如#'symbol 或(function symbol))时生成一个函数,并且两个函数指示符的比较是它们表示的功能比较:
CL-USER> (eql #'car #'cdr)
NIL
CL-USER> (eql #'car (symbol-function 'car))
T
但请注意,此相等性测试只是比较功能对象(代码段)的身份,如:
CL-USER> (eq #'car #'car)
T
CL-USER> (let ((a (lambda (x) (1+ x))))
(eq a a))
T
但不是代表它们的实际字节(代码!):
CL-USER> (let ((a (lambda (x) (car x)))
(eq a #'car))
NIL
CL-USER> (defun f (x) (1+ x))
F
CL-USER> (defun g (x) (1+ x))
G
CL-USER> (equalp (function f) (function g))
NIL
CL-USER> (equalp (lambda (x) (1+ x)) (lambda (x) (1+ x)))
NIL
请注意,在所有这些情况下,被比较的两个函数不仅具有相同的“含义”,而且在大多数情况下具有相同的“源代码”,以相同的方式编译,并且在相同的输入数据上表现相同.这是因为函数在数学上是(可能)无限组对(输入、输出),无法比较无限对象。
但这可能依赖于不制作函数的潜在副本的实现,大概是出于效率的原因。
用户无法复制函数(系统也没有任何理由复制一段代码!),因此任何函数都等于自身,就像任何指针都相等一样对自己。
既然 hash-table-test 返回一个符号指示符,另一个(可能更好的) eq 替代方案是从函数对象派生符号?一种方法比另一种更好吗?
(我想您打算使用函数指示符,而不是符号指示符)
实际上,hash-table-test 通常只返回一个函数指示符作为符号,如manual 中所说:
test---一个功能指示符。对于四个标准化的哈希表测试函数(参见 make-hash-table),返回的测试值始终是一个符号。如果一个实现允许额外的测试,那么这些测试是作为函数对象还是函数名返回取决于实现。
所以:
CL-USER> (type-of (hash-table-test (make-hash-table)))
SYMBOL
CL-USER> (eq 'eql (hash-table-test (make-hash-table)))
T
CL-USER> (eq #'eql (hash-table-test (make-hash-table)))
NIL
请注意,在最后一种情况下,我们将函数(#'eql 的值)与符号(hash-table-test 返回的值)进行比较,显然这种比较返回的是错误值。
总结:
-
比较函数不是很合理,除非你想知道两个函数是否实际上是内存中的同一个对象(例如,如果这两个东西是相同的编译代码)。
将函数与其作为符号(函数名称)或列表的名称(如(LAMBDA parameters body))区分开来并决定我们要实际比较的内容总是很重要的。