【问题标题】:Common Lisp type vs. class distinctionsCommon Lisp 类型与类的区别
【发布时间】:2017-03-01 19:52:55
【问题描述】:

Common Lisp class hierarchy 的帖子中,Rainer Joswig 和 Joshua Taylor 仔细区分了内置 Common Lisptypesclasses 之间的一些区别,其中类构成了一部分CLOS 对基线的扩展。 Pfeil 的综合hierarchy diagram 也反映了类型/类别(加上拖把)的区别。使用该图,似乎可以提取两个不同的层次结构。特别是,我目前对层次结构的顶部最感兴趣;即t 的直接子类型和子类(因为t 既是类型又是类)。以下是从图中提取的一些临时子类型/子类:

对于类型层次结构,t 的直接子类型似乎是atomcharacterrandom-statehash-tablerestartreadtablepackagepathnamestreamfunctionarraysequencenumbercondition。所有其他类型,如 floatlist 都是这些类型之一的子类型。类型层次结构也不是严格的层次结构(因为(subtype t t) => T,但这似乎是唯一的例外)。 (ps:symbolstructure-object这两种类型不包含在图中,也可能是t的直接子类型。)

对于类层次结构,t 的直接子类包括上述所有类型的对应类(atom 除外,也许structure-object 现在是standard-object(?) 的子类),加上@ 987654350@.

MOP 通过添加类metaobject(加上一些元子类)扩展了CLOS,但似乎没有添加到t 的直接子类。

有人可以验证这种理解是否正确,或提供额外的说明吗?

注意:我在上面的类型层次描述中发现了至少一个错误。所有列出的子类型(character 等)显然都是atom 的子类型,因此它们不是t 的直接子类型。 t 的唯一其他直接子类型似乎是sequence,因为序列可以是cons(非原子)。此外,symbol 实际上包含在图中,也是atom 的子类型。

【问题讨论】:

  • 什么是“直接子类型”? Common Lisp 没有定义这样的概念。当characterT 的子类型时,direct subtypedirect subtype,它是atom 的子类型?此外,Common Lisp 标准并未为 restart 等类定义所有超类(以及超类型)。它可能只是T 或添加类似structure-objectstandard-object 的内容。因此,符合要求的实现可能具有不同的类层次结构。为什么要推测?获取您使用的实现并检查它们。大多数都有足够的内省能力,您可以获得类型/类信息。

标签: class types common-lisp


【解决方案1】:

在 LispWorks 中绘制 T 的 CL 子类型

查找子类型:

(defun find-synonym-types (type1 types)
  (remove-if-not (lambda (type2)
                   (and (not (eq type1 type2))
                        (subtypep type1 type2)
                        (subtypep type2 type1)))
                 types))

(defun find-all-types-in-packages (packages &key include-nil)
  (let ((types nil))
    (loop for package in packages
          when (find-package package)
          do (do-external-symbols (sym (find-package package))
               (when (ignore-errors (subtypep sym t))
                 (let ((synonyms (find-synonym-types sym types)))
                   (if synonyms
                       (pushnew sym (get (first synonyms) :type-synonyms))
                     (pushnew sym types))))))
    (if include-nil types (remove nil types))))

(defun direct-subtypes (supertype &key all-types)
  (loop with subtypes = (remove supertype (loop for type in all-types
                                                when (subtypep type supertype)
                                                collect type))

        for type in subtypes
        when (loop for type2 in (remove type subtypes)
                   never (subtypep type type2))
        collect type))

绘制它:

#+capi
(defun class-color (class-name)
  (typecase (find-class class-name)
    (standard-class  :blue)
    (built-in-class  :violet)
    (structure-class :brown)
    (otherwise       :red)))

#+capi
(defun graph-subtypes-from-t ()
  (let ((all-types (find-all-types-in-packages '("CL" "CLOS"))))
    (capi:contain
     (make-instance
      'capi:graph-pane
      :roots '(t)
      :children-function (lambda (type)
                           (direct-subtypes type :all-types all-types))
      :node-pane-function #'(lambda (graph-pane node)
                              (declare (ignore graph-pane))
                              (make-instance
                               'capi:item-pinboard-object
                               :text (format
                                      nil "~{~a~^, ~}"
                                      (cons node
                                            (get node :type-synonyms)))
                               :graphics-args `(:foreground
                                                ,(if (find-class node
                                                                 nil)
                                                     (class-color node)
                                                   :black))))
      :edge-pane-function #'(lambda (self from to)
                              (declare (ignore self from to))
                              (make-instance
                               'capi:arrow-pinboard-object
                               :graphics-args '(:foreground
                                                :grey))))
     :title "Common Lisp Subtypes in LispWorks")))

图表

对应CLOS类的类型用蓝色写,结构类用棕色写。 NIL 类型未绘制。包括来自 CLOS MOP 的类型/类。有些类型有多个名称。另请注意,此图特定于 LispWorks,尤其是哪些类型实际上也是结构、CLOS 类、内置类或其他东西。

【讨论】:

  • 请注意,这仅包括那些由单个符号命名的类型。
  • @Svante:这些是原子类型说明符。只有将符号作为名称来讨论才有意义。 (integer 0 10) 之类的东西可能不是类型名称,而是复合类型说明符DEFTYPE 例如需要一个名称的符号。 Common Lisp 可能允许定义其他类型,但在标准中,仅显式列出了命名类型。
  • 有趣的是(至少对我来说)整个类型层次结构,虽然允许多个超类型,但避免了原始类型之间的递归,除了(据我所知)每种类型本身(即,(subtypep <type1> <type1>) => T)。既然所有的类也是类型,那么添加 clos 时这也成立吗?
  • @RainerJoswig,非常好的答案,非常有用的图表!
  • @RainerJoswig。我正在层压。
【解决方案2】:

直接子类型这个词几乎没有意义。

为什么你认为charactert直接子类型

但是characteratom 的子类型,t 的子类型!

嗯,也许characteratom直接 子类型?

不,character(or character number) 的子类型,atom 的子类型。

那么,也许character(or character number)直接 子类型?

不,character(or character integer) 的子类型,(or character number) 的子类型。

您可以将 视为整数,将 types 视为有理数 - 3 到 4 之间没有整数,但在任何两个不同的有理数之间都有很多有理数。

【讨论】:

  • 我仅指的是 ansi 标准中的原始内置类型,但也请记住,正如您所指出的,可用类型的数量是无限的。
【解决方案3】:

您已经知道 Common Lisp 中有类型和类。

类型对对象进行分类,而类标记对象。此外,一个对象可能有多种类型,但它是单个类的直接实例。

如果您通过type-of 询问对象的类型,您可能会得到许多有效的答案。一个非详尽的例子:

 (type-of 1)
=> (integer 1 1)
OR => bit
OR => (unsigned-byte 1)
OR => (mod 2)
OR => (integer (0) (2))
OR => (signed-byte 1)
OR => (signed-byte 16)
OR => fixnum
OR => integer
OR => (integer * *)
OR => rational
OR => real
OR => number
OR => t
OR => atom

对于typepsubtypep,事情要复杂得多,因为您可以随意生成类型,例如(eql 1)(member 1)(or fixnum bignum)(and (integer * 1) (integer 1 *))(not (satisfies zerop))、@987654331 @ 等表示正确结果,同样表示错误结果。

但是,对于每个对象,您只有一个类:

 (class-of 1)
=> #<BUILT-IN-CLASS INTEGER>

实际上,您无法确定整个类型层次结构。有一种观念认为任何东西都是t,没有东西是nil,并且可能存在原子标准类型的层次结构。但现实情况是存在复合类型和用户定义类型,因此在技术上不可能绘制完整的类型层次结构,因为这需要查看每个可以想象的对象的 subtypep 的结果。

即便如此,类型之间可能不存在层次结构。考虑到你说你发现的错误,我相信作者不想在图表中乱扔指向atom 的箭头,而不是cons。但实际上,这可能根本不是一个正确的表示,因为listsequence 类型的一些对象是cons,而另一些是atom,具体来说,空列表是atom

但是,您可以确定整个类层次结构,即使一个类可能有多个超类。


某些实现在 class-of 示例中返回 fixnum 类,这是标准的扩展(fixnum 是一种类型但不是类),以允许在 fixnum 上进行 CLOS 方法特化和/或允许优化技巧。

从我尝试的几个实现中,只有 CLISP 返回了 integer 类。

【讨论】:

  • 如果“类型对对象进行分类,而类对对象进行标记”,那么定义一个作为其他两个(超)类联合的类是否有意义——类似于(deftype symnum () '(or symbol number))
  • 当你创建一个子类时,它会继承超类的特征,以及这个类的超类,等等。对于多个超类,一个类按顺序继承所有的特征,更类似于(and super1 super2)。可能,它引入了自己的特征和/或覆盖了一些继承的特征。例如,专用方法是类的特征,标准对象的槽也是。
  • 对于类型,一个类型是其他类型的子集或超集没有内在含义,其含义由其使用和/或文档给出。例如,您可以为每个指示符创建一个类型(例如(deftype function-designator () '(or symbol function)))并将其用于check-type 和/或采用函数指示符的操作中的类型声明。如果一个优化编译器信任断言,它甚至可以生成更好的代码,甚至可以在指示符是符号和函数时进行分支,并在每个分支中展开代码。
  • 有趣。谢谢。那么有没有什么方法可以有效地专注于像(defmethod x-designator (object (or symbol function ...)))这样结合了专家的方法呢?
  • 使用标准方法,没有。但是你可以定义一个泛型函数元类和一个方法元类,专门化compute-discriminating-function,或者compute-applicable-methodscompute-applicable-methods-using-classes;您将在 defgeneric 上指定这些类,并且您必须定义自己的 defmethod 来解析自定义专家(或直接使用 ensure-generic-functionadd-method)。但这本身就是一个完整的话题。以CLOS discriminating functions and user-defined specializers开头。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多