如何检查 Common Lisp 中两种类型是否相同?
How to check if two types are the same in Common Lisp?
我正在学习 Common Lisp,现在正在尝试复制 C++(提升)lexical_cast<bool>(string)
。但是在 CL 中找不到比较类型的方法。
C++ 版本看起来像这样
template <typename ret_type>
ret_type lexical_cast(const std::string& val)
{
if constexpr (std::is_same_v<ret_type, bool>)
return val == "true";
// Otherwise don't care
}
到目前为止,我使用 equal
来比较类型,得出了以下 Lisp 代码。我认为这会起作用,因为 Lisp 对数据和代码漠不关心。
(defun lexical_cast(ret_type val)
(if (equal ret_type #'boolean) (equalp val "true"))
; Otherwise don't case
)
但 SBCL 生成错误:
; in: DEFUN LEXICAL_CAST
; #'BOOLEAN
;
; caught WARNING:
; The function BOOLEAN is undefined, and its name is reserved by ANSI CL so that
; even if it were defined later, the code doing so would not be portable.
;
; compilation unit finished
; Undefined function:
; BOOLEAN
; caught 1 WARNING condition
typep
也没有帮助,因为它将对象的类型与另一种类型进行比较。不直接比较它们。我搜索了 Lisp 教程和手册但没有成功。我应该怎么做?
我的错。我在发布问题后立即发现了问题。 # 不应该在那里
(defun lexical_cast(ret_type val)
(if (equal ret_type 'boolean) (equalp val "true"))
; Otherwise don't case
)
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
形式中,以检查内部结果是否为适当的 return 类型。这有助于检测实施错误。
内部形式对 return 类型 (cond
) 和值的类型 (typecase
s) 执行多重分派。例如,您可以将字符串转换为布尔值:
(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
,您需要维护 table 不同类型的转换函数,同时确保您始终 select与 return 类型最接近的类型。
一种非常简单的方法是为某些专用的 return 类型定义单独的通用转换函数:
;; 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*))
然后,您有一个单独的转换函数,它查找等于 return 类型的第一个类型,并调用关联的函数:
(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
系统有助于分析和简化类型表达式。
我正在学习 Common Lisp,现在正在尝试复制 C++(提升)lexical_cast<bool>(string)
。但是在 CL 中找不到比较类型的方法。
C++ 版本看起来像这样
template <typename ret_type>
ret_type lexical_cast(const std::string& val)
{
if constexpr (std::is_same_v<ret_type, bool>)
return val == "true";
// Otherwise don't care
}
到目前为止,我使用 equal
来比较类型,得出了以下 Lisp 代码。我认为这会起作用,因为 Lisp 对数据和代码漠不关心。
(defun lexical_cast(ret_type val)
(if (equal ret_type #'boolean) (equalp val "true"))
; Otherwise don't case
)
但 SBCL 生成错误:
; in: DEFUN LEXICAL_CAST
; #'BOOLEAN
;
; caught WARNING:
; The function BOOLEAN is undefined, and its name is reserved by ANSI CL so that
; even if it were defined later, the code doing so would not be portable.
;
; compilation unit finished
; Undefined function:
; BOOLEAN
; caught 1 WARNING condition
typep
也没有帮助,因为它将对象的类型与另一种类型进行比较。不直接比较它们。我搜索了 Lisp 教程和手册但没有成功。我应该怎么做?
我的错。我在发布问题后立即发现了问题。 # 不应该在那里
(defun lexical_cast(ret_type val)
(if (equal ret_type 'boolean) (equalp val "true"))
; Otherwise don't case
)
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
形式中,以检查内部结果是否为适当的 return 类型。这有助于检测实施错误。
内部形式对 return 类型 (cond
) 和值的类型 (typecase
s) 执行多重分派。例如,您可以将字符串转换为布尔值:
(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
,您需要维护 table 不同类型的转换函数,同时确保您始终 select与 return 类型最接近的类型。
一种非常简单的方法是为某些专用的 return 类型定义单独的通用转换函数:
;; 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*))
然后,您有一个单独的转换函数,它查找等于 return 类型的第一个类型,并调用关联的函数:
(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
系统有助于分析和简化类型表达式。