我对SBCL的适用性最感兴趣,但对Common Lisp的其他实现也很好奇。
在Common Lisp中,我们具有类型层次结构。
我想要一个给定两个对象作为参数的函数,该函数返回表示适用于这两个对象的最特定超级类型的符号。它的用法类似于:
(most-specific-super-type x y)
因此,例如,短浮点型和长浮点型都是super-type的子类型float
。
如果比较长浮点数和整数,则最具体的超类型为real
。
如果将复数和浮点数进行比较,则最具体的超类型为number
。
比较该类型层次结构中来自不同树的两个对象,大概会返回该类型,T
或者atom
在对象不是a的情况下返回cons
。
我很想避免自己写这个,我的直觉告诉我,好像已经编写了那种函数。
我主要对标准语言中已经定义的类型感兴趣,但我的直觉还告诉我,对于CLOS类,必须有一个与此功能相关的函数,以确定类优先级。
因此,如果有一个同时适用于类和类型的函数,那将是超级,但如果仅针对类型有解决方案,我将很高兴...
我认为这对其他人来说是值得的,经过大量搜索之后,我一直无法在自己的任何地方找到这样的解决方案,而且我也没有被太多人咬到,所以我一直在努力自己解决方案的基本未优化逻辑。
这不是最终的工作版本,但是应该使其他任何人寻求相同的问题来解决,然后他们可以进行强化和重构。请发表评论并提供任何更正/修正/问题。
感谢@Martin Buchmann发布了我以前开始的https://www.informatimago.com/articles/cl-types/。
步骤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
谓词来测试每个符号。如果有效,我们会将其推送到名为的集合中types
。
注意::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:我们定义一个函数,为我们提供两种类型中最具体的超类型。这是如何运作的?
首先,我们intersection
获得使用新填充的哈希值获得的超类型的。
其次,我们将相交subtypep
作为排序谓词进行排序。使用的subtypep
肯定不是我一个直观的排序谓词,直到我意识到它种很有意义的使用它来排序层次结构。我仍然不是100%肯定没有某些极端情况:\
无论如何,我们都将返回在第一位置具有低排名类型的超类型的交集的列表,而要获取它,我们只需将 car
(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返回459和-450(INTEGER 0 4611686018427387903)
的类型。fixnum
对于-345类型,最常见的超级类型将作为返回INTEGER
,而它们实际上都是特定类型fixnum
。
无论如何,如果一个值的类型不是另一个值的类型的子类型,我们将使用super-type-of-types
在步骤5中创建的函数,并且在最坏的情况下我们总是可以返回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
我相信它至少看起来像是在起作用:)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句