功能代号的平等

时间:2019-06-19 17:52:00

标签: function common-lisp

两个具有相同符号指示符的Common Lisp函数对象是否总是eq?例如,这种比较似乎有效:

(defun foo (fn)
  (let ((ht (make-hash-table)))
    (eq (symbol-function (hash-table-test ht)) fn)))

FOO
* (foo #'eql)
T
*(foo #'equal)
NIL

但这可能依赖于未制作功能的潜在副本的实现,大概是出于效率方面的考虑。由于hash-table-test返回一个符号指示符,另一个(可能更好)的eq替代方案是从函数对象中导出符号吗?一种方法比另一种更好吗?

2 个答案:

答案 0 :(得分:4)

  

两个具有相同符号指示符的Common Lisp函数对象是否总是eq?

在Common Lisp中,函数是一段代码,无论是否编译。来自Glossary

  

函数 n。 1.表示代码的对象,可以用零个或多个参数调用,并产生零个或多个参数价值观。 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返回的值)进行比较,显然,该比较将返回一个假值。

结论:

  1. 比较函数不是很合理,除非您想知道两个函数实际上是否在内存中是同一对象(为方便起见,如果这两个对象是相同的编译代码)。

    < / li>
  2. 始终重要的是,将功能与其作为符号(功能名称)或列表(如(LAMBDA parameters body))的名称区别开来,并确定我们要实际比较的内容。

答案 1 :(得分:3)

#'eql等效于(function eql)。除非存在eql的词法函数绑定,否则将其定义为返回符号eql的全局函数定义。这也是(symbol-function 'eql)定义要返回的内容。

因此,对于任何未被词法定义遮盖的全局定义函数f

(eq #'f (symbol-function 'f))

应始终为真。