在普通的 lisp 中,returns 两个对象最具体的超类型的函数是什么?

In common lisp, what is a function that returns the most specific supertype of two objects?

我对 SBCL 的适用性最感兴趣,但也对 Common Lisp 的其他实现感到好奇。

在 Common Lisp 中我们有 type hierarchy.

我想要一个函数,给定两个对象作为参数,returns 表示适用于这两个对象的最具体超类型的符号。它的用法类似于:

(most-specific-super-type x y)

例如,short-float 和 long-float 都是超类型的子类型 float

如果比较长浮点型和整数,最具体的超类型是 real

如果比较复数和浮点数,最具体的超类型是 number

比较来自该类型层次结构中不同树的两个对象,我们可能会返回类型 T 或在对象不是 cons 的情况下可能返回 atom 类型。

我很想避免自己写这个,我的直觉告诉我,它似乎是那种已经写好的函数。

我主要对标准语言中已经定义的类型感兴趣,但我的直觉也告诉我,CLOS classes 必须有一个与此有点相关的函数,以便确定 class优先。

因此,如果有一个函数可以同时适用于 classes 和类型,那就太棒了,但如果有一个仅适用于类型的解决方案,我会很高兴...

因为我认为这对其他人来说是有价值的,而且经过大量搜索后我自己在任何地方都找不到这样的解决方案,而且我也没有被咬太多,所以我有一个亲自为解决方案创建基本的未优化逻辑。

这不是最终的工作版本,但应该让其他任何寻找相同问题的解决方案,他们可以加强和重构。请评论并提供任何 corrections/fixes/issues。

感谢@Martin Buchmann 发布 https://www.informatimago.com/articles/cl-types/ 我曾经开始使用它。

第 1 步:设置哈希表。

(defvar *type-hash* (make-hash-table))

步骤 2: 定义有效类型谓词函数。在 SBCL 上,* returns TRUE 对于有效的类型说明符...所以我们只是排除该特定情况并使用处理程序案例来停止程序在无效类型上引发条件 -代号。

(defun valid-type-p (type-designator)
  (handler-case (and (SB-EXT:VALID-TYPE-SPECIFIER-P type-designator)
                     (not (eq type-designator 'cl:*)))))

第三步:我们从:cl包中提取外部符号,也就是说,本质上是common lisp语言。我们使用 valid-type-p 谓词来测试每个符号。如果有效,我们将其推送到名为 types.

的集合中

注意::cl 不是您可以执行此操作的唯一软件包。如果您在自己编写的包上执行此操作,该包也使用外部 cl 符号,并定义了一些您自己导出的自定义 CLOS classes,我认为这也捕获了其中包含的 class 层次结构。我没有测试太多,所以玩吧。我在我的示例中避免了这一点,因为我相信 CLOS 层次结构和 classes 可以在 运行 时间更改,这可能会使您的层次结构无效,除非您每次都重新计算它,而我我非常有信心内部类型将是目前对我的目的最有用的类型,并且不会改变。

使用每个有效类型作为键,并使用嵌套循环,在此操作之后,散列中的每个键现在都会为我们提供一个类型列表,其中该键是有效的子类型。

(let ((types nil))
  (do-external-symbols (s :cl)
    (when (ignore-errors (valid-type-p s))
      (push s types)))

  (loop for type1 in types
     do (loop for type2 in types
           do (if (subtypep type1 type2)
                  (push type2 (gethash type1 *type-hash*))))))

第 4 步: 我们定义一个函数来为我们提供两种类型中最具体的超类型。它是如何工作的?

首先,我们得到使用我们新填充的哈希获得的超类型的intersection

其次,我们对交集进行排序,使用subtypep作为排序谓词。 subtypep 的使用对我来说当然不是一个直观的排序谓词,直到我意识到使用它来排序层次结构是有意义的。我仍然不是 100% 确定没有一些边缘情况:\

无论如何,我们将return编辑超类型与排在第一位的低等级类型的交集列表,要得到它,我们只需取 car

(defun supertype-of-types (type1 type2)
  (car
   (sort
    (intersection (gethash type1 *type-hash*)
                  (gethash type2 *type-hash*))
    #'subtypep)))

第 5 步: Supertype-of-types 已经是一个有用的函数,但最终我们希望在实际值上使用它,而不仅仅是手动输入的表示类型的符号。

函数type-of看似returnSBCL中相对特定类型的值,但实际上它可以return列出。因此,如果发生这种情况,我们需要编写一个快速函数来从列表的第一部分中提取表示类型的符号...

(defun type-of-object (x)
  (let ((type (type-of x)))
    (if (listp type)
        (car type)
        type)))

第六步:最后,我们编写了我们想要的函数。我们首先明确地检查两个对象中的一个是否是另一个对象类型的子类型。如果是,那就是最具体的超类型。我们这样做的部分原因是 SBCL return 的对象类型比用 typeof 询问对象时仅由类型符号表示的类型更具体。出于我的优化目的,如果可能的话,我希望能够使用这些更具体的类型规范,这是在我明确地找出扩展类型说明符之前获得其中一些的快速拼凑。如果我不把它放进去,我们的下一个技术 returns 'INTEGER 作为 459 和 -345 的最具体的超类型,因为 SBCL returns (INTEGER 0 4611686018427387903) 用于 459 的类型和 fixnum 对于 -345 类型,最常见的超类型将 return 编辑为 INTEGER,而它们实际上都是特定类型 fixnum.

无论如何,如果一个值的类型不是另一个值类型的子类型,我们使用我们在步骤 5 中创建的 super-type-of-types 函数,并且我们总是可以 return T 作为最坏的情况,因为一切都是类型 T 的子类型。

(defun most-specialised-supertype (x y)
  (let ((typex (type-of x))
        (typey (type-of y)))
    (cond ((subtypep typex typey) typey)
          ((subtypep typey typex) typex) 
          ((supertype-of-types (type-of-object x) (type-of-object y)))
          (t t))))

并用它进行几次测试:

(most-specialised-supertype 5.0l0 435789)
REAL

(most-specialised-supertype 1.0s0 1.0l0)
FLOAT

(most-specialised-supertype 1.0 #c(1 1))
NUMBER

(most-specialised-supertype 'symbol "string")
ATOM

(most-specialised-supertype #(1 2 3) #*101)
VECTOR

我相信它至少看起来有点像 :)