Types 在common lisp中,返回两个对象的最特定超类型的函数是什么?

Types 在common lisp中,返回两个对象的最特定超类型的函数是什么?,types,common-lisp,subtype,supertype,Types,Common Lisp,Subtype,Supertype,我对SBCL的适用性最感兴趣,但对CommonLisp的其他实现也很好奇 在公共Lisp中,我们有 我想要一个函数,给定两个对象作为参数,返回表示适用于这两个对象的最特定超类型的符号。它的用法类似于: (most-specific-super-type x y) 例如,短浮点和长浮点都是super-typefloat的子类型 如果比较长浮点和整数,最具体的超级类型是real 如果比较复数和浮点数,最具体的超级类型是number 比较该类型层次结构中独立树中的两个对象,我们可能会返回类型T,或者

我对SBCL的适用性最感兴趣,但对CommonLisp的其他实现也很好奇

在公共Lisp中,我们有

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

(most-specific-super-type x y)
例如,短浮点和长浮点都是super-type
float
的子类型

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

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

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

我想避免自己写这个,我的直觉告诉我,这似乎是已经写过的函数

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


因此,如果有一个函数同时适用于类和类型,那就太好了,但是如果有一个解决方案只适用于类型,我会很高兴…

因为我认为这对其他人来说是有价值的,而且我自己在大量搜索之后,在任何地方都找不到这样的解决方案,我并没有被咬太多,我自己也尝试过用基本的未经选择的逻辑来解决问题

这不是一个最终的工作版本,但应该让任何其他寻找相同问题的人找到解决方案,在这一点上他们可以硬化和重构。请评论并提供任何更正/修复/问题

感谢@Martin Buchmann的发帖,我曾经开始发帖

步骤1:设置哈希表

(defvar *type-hash* (make-hash-table))
步骤2:定义有效的类型谓词函数。在SBCL上,
*
为有效的类型说明符返回TRUE…因此我们只需排除该特定情况,并使用处理程序情况来停止程序对无效类型指示符提出条件

(defun valid-type-p (type-designator)
  (handler-case (and (SB-EXT:VALID-TYPE-SPECIFIER-P type-designator)
                     (not (eq type-designator 'cl:*)))))
步骤3:我们从
:cl
包中提取外部符号,也就是说,基本上是通用的lisp语言。我们使用
valid-type-p
谓词来测试每个符号。如果有效,我们将其推送到名为
类型的集合中

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

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

(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步:我们定义了一个函数,为我们提供两种类型中最具体的超级类型。这是怎么回事

首先,我们使用新填充的散列得到超级类型的交集

其次,我们使用
子类p
作为排序谓词对交集进行排序。对我来说,
子类p
的使用当然不是一个直观的排序谓词,直到我意识到用它来排序继承权是有意义的。但我仍然不能100%确定是否存在一些边缘情况:\

不管怎样,我们都会得到一个超类型与排名靠前的类型的交集列表,要得到它,我们只需使用
汽车

(defun supertype-of-types (type1 type2)
  (car
   (sort
    (intersection (gethash type1 *type-hash*)
                  (gethash type2 *type-hash*))
    #'subtypep)))
步骤5:类型的超类型已经是一个有用的函数,但最终我们希望在实际值上使用它,而不仅仅是手动输入表示类型的符号

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

(defun type-of-object (x)
  (let ((type (type-of x)))
    (if (listp type)
        (car type)
        type)))
第6步:最后,我们编写所需的函数。我们首先显式地检查两个对象中的一个是否是另一个对象类型的子类型。如果是,那就是最具体的超类型。我们这样做的部分原因是,SBCL返回的对象类型比使用
typeof
查询对象时仅由类型符号表示的对象类型更具体。出于优化的目的,如果可能的话,我希望能够使用这些更具体的类型规范,这是一个快速的乱码,在我明确定义扩展类型说明符之前可以得到其中一些。如果我不把它放进去,我们的下一个技术返回'INTEGER'作为459和-345最具体的超类型,因为SBCL返回
(INTEGER 0 4611686018427387903)
对于459类型和
fixnum
对于-345类型,最常见的超类型将返回
INTEGER
,然而,它们实际上都是特定类型的
fixnum

无论如何,如果一个值的类型不是另一个值的类型的子类型,我们将使用在步骤5中创建的
超级类型
函数,并且我们总是可以返回t作为最坏情况,因为所有内容都是ty的子类型
(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