【问题标题】:How to check if two types are the same in Common Lisp?如何检查 Common Lisp 中的两种类型是否相同?
【发布时间】:2021-04-09 13:26:15
【问题描述】:

我正在学习 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 教程和手册,但没有成功。我该怎么做?

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

    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 系统有助于分析和简化类型表达式。

    【讨论】:

      【解决方案2】:

      我的错。我在发布问题后立即发现了问题。 # 不应该在那里

      (defun lexical_cast(ret_type val)
          (if (equal ret_type 'boolean) (equalp val "true"))
          ; Otherwise don't case
      )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-02-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-17
        相关资源
        最近更新 更多