【问题标题】:Equality of Function Designators功能指示符的相等性
【发布时间】:2019-06-19 17:52:00
【问题描述】:

两个具有相同符号指示符的 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 替代方案是从函数对象派生符号?一种方法比另一种更好吗?

【问题讨论】:

  • hash-table-test 的返回值不是符号指示符,而是函数指示符。如果它是四个标准测试之一,它需要是一个符号,而不是一个符号指示符。否则,它可以是其他类型的函数指示符。
  • 我不认为“符号指示符”是一回事。
  • 是的,我本可以更准确一些,但这有点令人困惑。为了进一步澄清我的理解,hash-table-test 的返回值是一个符号(可以使用作为函数指示符)。功能指示符是测试本身。
  • 当你说“从函数对象中导出符号”时,你会怎么做?没有返回函数对象名称的函数。
  • 我最初认为可能有这样一个函数可以跟踪命名函数与其符号之间的关联,但感谢您确认没有这样的事情。

标签: function common-lisp


【解决方案1】:

两个具有相同符号指示符的 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 返回的值)进行比较,显然这种比较返回的是错误值。

总结:

  1. 比较函数不是很合理,除非你想知道两个函数是否实际上是内存中的同一个对象(例如,如果这两个东西是相同的编译代码)。

  2. 将函数与其作为符号(函数名称)或列表的名称(如(LAMBDA parameters body))区分开来并决定我们要实际比较的内容总是很重要的。

【讨论】:

  • "比较函数不是很合理。"除非您想将它们用作 alist/hash 表的键。
【解决方案2】:

#'eql 等价于(function eql)。除非有eql 的词法函数绑定,否则它被定义为返回符号eql 的全局函数定义。这也是 (symbol-function 'eql) 被定义返回的内容。

所以对于任何没有被词法定义遮蔽的全局定义函数f

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

应该永远是真的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 2018-11-06
    相关资源
    最近更新 更多