如何根据类定义我的类型,以便我可以在 typecase 和相关表达式中使用该类型?

问题描述 投票:0回答:1

考虑一下:

(defclass my-string ()
  ((data :initarg :data :type simple-string)
   (properties :initarg :properties :type interval-tree))
  (:documentation
   "Represents a string with properties."))

我想使用 my-string 作为 typecase 表达式中的类型:

(defun upcase (obj)
  (typecase obj
    (my-string (string-upcase (slot-value obj 'data)))
    (string (string-upcase obj))
    (integer (char-upcase (code-char obj)))
    (character (char-upcase obj))
    (otherwise (error "Wrong type argument ~S" obj))))

我认为类是类型,但显然不是,因为上面是一个编译器错误。所以我声明了一个类型:

(deftype my-string (s) (typep s 'my-string))

看来我必须像这样使用(否则我会得到编译错误,typedef需要一个参数):

(defun upcase (obj)
  (typecase obj
    ((my-string obj) (string-upcase (slot-value obj 'data)))
    (string (string-upcase obj))
    (integer (char-upcase (code-char obj)))
    (character (char-upcase obj))
    (otherwise (error "Wrong type argument ~S" obj))))

但是 SBCL 将我的代码删除为无法访问! :-)

我该怎么做?如何正确声明类的类型,以便我可以在可以使用类型声明的表达式中使用它?

我知道我可以使用结构而不是类,在这种情况下,编译器会为该类型生成代码,并且初始类型“正常工作”:

(defstruct my-string
  data properties)

宏观展开并查看生成的代码并没有真正让人更加开明。我看到这个:

(SB-C:XDEFUN MY-STRING-P
     :PREDICATE
     NIL
     (SB-KERNEL::OBJECT)
   (TYPEP SB-KERNEL::OBJECT 'MY-STRING))

这正是我所做的,但我不认为该功能在剧中,或者我错了?要么它们以某种方式在内部将结构与类型关联起来,要么我错过了生成代码的一些相关部分?我对这一切都不太熟悉,所以也很有可能。

一个相关的问题:对扩展 CommonLisp 中的字符串等内置类型的自定义类进行建模的正确方法是什么?这是一种更好的组合方法(如 my-string 中所做的那样),还是从某些内置 CL 字符串类继承可能更好?基于这个 SX 问题和答案,在我看来,继承并不是建模类型的最佳方式,这可能是为了更好,因为我们不喜欢 Java 或旧 C++ 中已知的分类法。

最后,在这种情况下,如果我制作一个专门针对这些类型的泛型方法,我什至不需要 typecase,整个问题就会消失,我完全意识到这一点。但是,我想了解有关类型和类的更多信息,以及如何在 CL 中对上述内容进行建模。

抱歉,如果它有点长,并且问题太多,我正在学习这个。感觉就像我遇到的每个问题以及尝试寻找答案都会引发另外 10 个问题:)。

common-lisp
1个回答
0
投票

对于类行为,您最好使用 Common Lisp 的多重分派功能 - 通过使用通用函数宏

defgeneric
defmethod

无论如何,使用调度的好处是你更加灵活,并且类明智的选择可以在以后扩展(更利于维护)。不仅如此 - 如果您导入此代码(例如作为包) - 在包中使用此代码的代码 - 仍然可以扩展调度(为调度添加新案例和新类),这在编程中非常强大。

(defclass my-string ()
  ((data :initarg :data :type simple-string)
   (properties :initarg :properties :type interval-tree))
  (:documentation
   "Represents a string with properties."))

您首先使用

defgeneric
编写一个通用函数:

(defgeneric upcase (obj)
  (:documentation "A generic function to upcase different types of objects."))

然后使用

defmethod
将每个 auf 子句编写为单独的方法函数:

(defmethod upcase ((obj my-string))
  (string-upcase (slot-value obj 'data)))

(defmethod upcase ((obj string))
  (string-upcase obj))

(defmethod upcase ((obj integer))
  (char-upcase (code-char obj)))

(defmethod upcase ((obj character))
  (char-upcase obj))

(defmethod upcase ((obj t))
  (error "Wrong type argument ~S" obj))

通过以下方式尝试代码:

(let ((custom-string (make-instance 'my-string :data "hello" :properties nil)))
  (format t "Upcased my-string: ~A~%" (upcase custom-string)))

(format t "Upcased plain string: ~A~%" (upcase "world"))

(format t "Upcased integer: ~A~%" (upcase 97))  ; ASCII code for 'a'

(format t "Upcased character: ~A~%" (upcase #\a))

(handler-case
    (upcase '(1 2 3))
  (error (e) (format t "Error: ~A~%" e)))

;; The output should be:
Upcased my-string: HELLO
Upcased plain string: WORLD
Upcased integer: A
Upcased character: A
Error: Wrong type argument (1 2 3)
© www.soinside.com 2019 - 2024. All rights reserved.