Common Lisp 定义SUBTYPEP 来比较类型。使用它,您可以定义一个类型相等比较函数:
(defun type-equal (t1 t2)
(and (subtypep t1 t2)
(subtypep t2 t1)))
例如,boolean 类型有不同的编码方式,但它们涵盖相同的值集,因此它们是相等的:
(type-equal '(member t nil) '(or (eql t) (eql nil)))
=> T, T
还有一个COERCE 函数以有限的方式允许将值转换为另一种类型。然而,这是不可扩展的。
简单的演员表
让我们首先为不知道如何将值转换为目标类型的情况定义一个新条件。为简单起见,此错误没有关联数据:
(define-condition cast-error (error) ())
那么,lexical-cast 的一个严格但简单的定义可以是:
(defun lexical-cast (return-type value)
(coerce (cond
((type-equal return-type 'boolean)
(typecase value
(string (string= value "true"))
(real (/= value 0))
(t (error 'cast-error))))
((subtypep return-type 'real)
(typecase value
(string (read-from-string value))
(boolean (if value 1 0))
(t (error 'cast-error))))
(t
(error 'cast-error)))
return-type))
我将cond 表单包装在coerce 表单中,以检查内部结果是否具有适当的返回类型。这有助于检测实施错误。
内部表单对返回类型 (cond) 和值的类型 (typecases) 执行多次调度。例如,您可以将字符串转换为布尔值:
(lexical-cast '(member t nil) "true")
T
假设我们遵循 0 为假的 C 约定,这里是一个数字的强制转换(注意布尔类型的表达方式不同):
(lexical-cast '(or (eql t) (eql nil)) 0)
NIL
还请注意,Common Lisp 允许您定义范围。这里我们有一个错误,因为从字符串中读取的值超出了例外范围(注意:从字符串中读取对于某些输入可能是不安全的,但这超出了这里的范围)。
(lexical-cast '(integer 0 10) "50")
;; error from COERCE
(lexical-cast '(integer 0 10) "5")
5
这是另一个示例,从布尔值转换为实数的子类型:
(lexical-cast 'bit t)
1
可扩展的演员表
为了拥有通用的可扩展词法转换,如PPRINT-DISPATCH,您需要维护不同类型的转换函数表,同时确保始终选择与返回类型匹配的最接近的类型。
一种非常简单的方法是为一些专用的返回类型定义单独的通用转换函数:
;; can be extended for other classes of values
(defgeneric cast-to-boolean (value)
(:method ((v symbol)) v)
(:method ((v integer)) (eql v 1))
(:method ((v string)) (string= v "true")))
然后,您维护从类型到转换函数的映射:
;; usually this is hidden behind user-friendly macros
(defvar *type-cast* nil)
;; this could be written instead (set-cast 'boolean #'cast-to-boolean)
;; for example, but here we just set the list directly.
(setf *type-cast*
(acons 'boolean #'cast-to-boolean *type-cast*))
然后,您有一个单一的转换函数,它查找等于返回类型的第一个类型,并调用关联的函数:
(defun gen-type-cast (return-type value)
(let ((f (assoc return-type *type-cast* :test #'type-equal)))
(if f (funcall (cdr f) value) (error 'cast-error))))
例如:
(gen-type-cast '(member t nil) "true")
T
另见
lisp-types 系统有助于分析和简化类型表达式。